clockwerk-opensim-stable – Blame information for rev 1

Subversion Repositories:
Rev:
Rev Author Line No. Line
1 vero 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  
28 using System;
29 using System.Collections.Generic;
30 using System.Drawing;
31 using System.IO;
32 using System.Reflection;
33 using System.Runtime.Serialization;
34 using System.Security.Permissions;
35 using System.Xml;
36 using System.Xml.Serialization;
37 using log4net;
38 using OpenMetaverse;
39 using OpenMetaverse.Packets;
40 using OpenMetaverse.StructuredData;
41 using OpenSim.Framework;
42 using OpenSim.Region.Framework.Interfaces;
43 using OpenSim.Region.Framework.Scenes.Scripting;
44 using OpenSim.Region.Framework.Scenes.Serialization;
45 using OpenSim.Region.Physics.Manager;
46 using PermissionMask = OpenSim.Framework.PermissionMask;
47  
48 namespace OpenSim.Region.Framework.Scenes
49 {
50 #region Enumerations
51  
52 [Flags]
53 public enum Changed : uint
54 {
55 INVENTORY = 1,
56 COLOR = 2,
57 SHAPE = 4,
58 SCALE = 8,
59 TEXTURE = 16,
60 LINK = 32,
61 ALLOWED_DROP = 64,
62 OWNER = 128,
63 REGION = 256,
64 TELEPORT = 512,
65 REGION_RESTART = 1024,
66 MEDIA = 2048,
67 ANIMATION = 16384
68 }
69  
70 // I don't really know where to put this except here.
71 // Can't access the OpenSim.Region.ScriptEngine.Common.LSL_BaseClass.Changed constants
72 [Flags]
73 public enum ExtraParamType
74 {
75 Something1 = 1,
76 Something2 = 2,
77 Something3 = 4,
78 Something4 = 8,
79 Flexible = 16,
80 Light = 32,
81 Sculpt = 48,
82 Something5 = 64,
83 Something6 = 128
84 }
85  
86 [Flags]
87 public enum TextureAnimFlags : byte
88 {
89 NONE = 0x00,
90 ANIM_ON = 0x01,
91 LOOP = 0x02,
92 REVERSE = 0x04,
93 PING_PONG = 0x08,
94 SMOOTH = 0x10,
95 ROTATE = 0x20,
96 SCALE = 0x40
97 }
98  
99 public enum PrimType : int
100 {
101 BOX = 0,
102 CYLINDER = 1,
103 PRISM = 2,
104 SPHERE = 3,
105 TORUS = 4,
106 TUBE = 5,
107 RING = 6,
108 SCULPT = 7
109 }
110  
111 public enum UpdateRequired : byte
112 {
113 NONE = 0,
114 TERSE = 1,
115 FULL = 2
116 }
117  
118 #endregion Enumerations
119  
120 public class SceneObjectPart : ISceneEntity
121 {
122 /// <value>
123 /// Denote all sides of the prim
124 /// </value>
125 public const int ALL_SIDES = -1;
126  
127 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
128  
129 /// <summary>
130 /// Dynamic attributes can be created and deleted as required.
131 /// </summary>
132 public DAMap DynAttrs { get; set; }
133  
134 private DOMap m_dynObjs;
135  
136 /// <summary>
137 /// Dynamic objects that can be created and deleted as required.
138 /// </summary>
139 public DOMap DynObjs
140 {
141 get
142 {
143 if (m_dynObjs == null)
144 m_dynObjs = new DOMap();
145  
146 return m_dynObjs;
147 }
148  
149 set
150 {
151 m_dynObjs = value;
152 }
153 }
154  
155 /// <value>
156 /// Is this a root part?
157 /// </value>
158 /// <remarks>
159 /// This will return true even if the whole object is attached to an avatar.
160 /// </remarks>
161 public bool IsRoot
162 {
163 get { return ParentGroup.RootPart == this; }
164 }
165  
166 /// <summary>
167 /// Is an explicit sit target set for this part?
168 /// </summary>
169 public bool IsSitTargetSet
170 {
171 get
172 {
173 return
174 !(SitTargetPosition == Vector3.Zero
175 && (SitTargetOrientation == Quaternion.Identity // Valid Zero Rotation quaternion
176 || SitTargetOrientation.X == 0f && SitTargetOrientation.Y == 0f && SitTargetOrientation.Z == 1f && SitTargetOrientation.W == 0f // W-Z Mapping was invalid at one point
177 || SitTargetOrientation.X == 0f && SitTargetOrientation.Y == 0f && SitTargetOrientation.Z == 0f && SitTargetOrientation.W == 0f)); // Invalid Quaternion
178 }
179 }
180  
181 #region Fields
182  
183 public bool AllowedDrop;
184  
185 public bool DIE_AT_EDGE;
186  
187 public bool RETURN_AT_EDGE;
188  
189 public bool BlockGrab;
190  
191 public bool StatusSandbox;
192  
193 public Vector3 StatusSandboxPos;
194  
195 [XmlIgnore]
196 public int[] PayPrice = {-2,-2,-2,-2,-2};
197  
198 [XmlIgnore]
199 /// <summary>
200 /// The representation of this part in the physics scene.
201 /// </summary>
202 /// <remarks>
203 /// If you use this property more than once in a section of code then you must take a reference and use that.
204 /// If another thread is simultaneously turning physics off on this part then this refernece could become
205 /// null at any time.
206 /// </remarks>
207 public PhysicsActor PhysActor { get; set; }
208  
209 //Xantor 20080528 Sound stuff:
210 // Note: This isn't persisted in the database right now, as the fields for that aren't just there yet.
211 // Not a big problem as long as the script that sets it remains in the prim on startup.
212 // for SL compatibility it should be persisted though (set sound / displaytext / particlesystem, kill script)
213  
214 public UUID Sound;
215  
216 public byte SoundFlags;
217  
218 public double SoundGain;
219  
220 public double SoundRadius;
221  
222 /// <summary>
223 /// Should sounds played from this prim be queued?
224 /// </summary>
225 /// <remarks>
226 /// This should only be changed by sound modules. It is up to sound modules as to how they interpret this setting.
227 /// </remarks>
228 public bool SoundQueueing { get; set; }
229  
230 public uint TimeStampFull;
231  
232 public uint TimeStampLastActivity; // Will be used for AutoReturn
233  
234 public uint TimeStampTerse;
235  
236 public int STATUS_ROTATE_X;
237  
238 public int STATUS_ROTATE_Y;
239  
240 public int STATUS_ROTATE_Z;
241  
242 private Dictionary<int, string> m_CollisionFilter = new Dictionary<int, string>();
243  
244 /// <value>
245 /// The UUID of the user inventory item from which this object was rezzed if this is a root part.
246 /// If UUID.Zero then either this is not a root part or there is no connection with a user inventory item.
247 /// </value>
248 private UUID m_fromUserInventoryItemID;
249  
250 public UUID FromUserInventoryItemID
251 {
252 get { return m_fromUserInventoryItemID; }
253 set { m_fromUserInventoryItemID = value; }
254 }
255  
256 public scriptEvents AggregateScriptEvents;
257  
258 public Vector3 AttachedPos;
259  
260 public Vector3 RotationAxis = Vector3.One;
261  
262 public bool VolumeDetectActive; // XmlIgnore set to avoid problems with persistance until I come to care for this
263 // Certainly this must be a persistant setting finally
264  
265 public bool IsWaitingForFirstSpinUpdatePacket;
266  
267 public Quaternion SpinOldOrientation = Quaternion.Identity;
268  
269 protected int m_APIDIterations = 0;
270 protected Quaternion m_APIDTarget = Quaternion.Identity;
271 protected float m_APIDDamp = 0;
272 protected float m_APIDStrength = 0;
273  
274 /// <summary>
275 /// This part's inventory
276 /// </summary>
277 public IEntityInventory Inventory
278 {
279 get { return m_inventory; }
280 }
281 protected SceneObjectPartInventory m_inventory;
282  
283 public bool Undoing;
284  
285 public bool IgnoreUndoUpdate = false;
286  
287 public PrimFlags LocalFlags;
288  
289 private float m_damage = -1.0f;
290 private byte[] m_TextureAnimation;
291 private byte m_clickAction;
292 private Color m_color = Color.Black;
293 private readonly List<uint> m_lastColliders = new List<uint>();
294 private int m_linkNum;
295  
296 private int m_scriptAccessPin;
297  
298 private readonly Dictionary<UUID, scriptEvents> m_scriptEvents = new Dictionary<UUID, scriptEvents>();
299 private string m_sitName = String.Empty;
300 private Quaternion m_sitTargetOrientation = Quaternion.Identity;
301 private Vector3 m_sitTargetPosition;
302 private string m_sitAnimation = "SIT";
303 private string m_text = String.Empty;
304 private string m_touchName = String.Empty;
305 private readonly List<UndoState> m_undo = new List<UndoState>(5);
306 private readonly List<UndoState> m_redo = new List<UndoState>(5);
307  
308 private bool m_passTouches = false;
309 private bool m_passCollisions = false;
310  
311 protected Vector3 m_acceleration;
312 protected Vector3 m_angularVelocity;
313  
314 //unkown if this will be kept, added as a way of removing the group position from the group class
315 protected Vector3 m_groupPosition;
316 protected uint m_localId;
317 protected Material m_material = OpenMetaverse.Material.Wood;
318 protected string m_name;
319 protected Vector3 m_offsetPosition;
320  
321 protected SceneObjectGroup m_parentGroup;
322 protected byte[] m_particleSystem = Utils.EmptyBytes;
323 protected ulong m_regionHandle;
324 protected Quaternion m_rotationOffset = Quaternion.Identity;
325 protected PrimitiveBaseShape m_shape;
326 protected UUID m_uuid;
327 protected Vector3 m_velocity;
328  
329 protected Vector3 m_lastPosition;
330 protected Quaternion m_lastRotation;
331 protected Vector3 m_lastVelocity;
332 protected Vector3 m_lastAcceleration;
333 protected Vector3 m_lastAngularVelocity;
334 protected int m_lastTerseSent;
335  
336 protected byte m_physicsShapeType = (byte)PhysShapeType.prim;
337 protected float m_density = 1000.0f; // in kg/m^3
338 protected float m_gravitymod = 1.0f;
339 protected float m_friction = 0.6f; // wood
340 protected float m_bounce = 0.5f; // wood
341  
342 /// <summary>
343 /// Stores media texture data
344 /// </summary>
345 protected string m_mediaUrl;
346  
347 // TODO: Those have to be changed into persistent properties at some later point,
348 // or sit-camera on vehicles will break on sim-crossing.
349 private Vector3 m_cameraEyeOffset;
350 private Vector3 m_cameraAtOffset;
351 private bool m_forceMouselook;
352  
353 // TODO: Collision sound should have default.
354 private UUID m_collisionSound;
355 private float m_collisionSoundVolume;
356  
357 public KeyframeMotion KeyframeMotion
358 {
359 get; set;
360 }
361  
362 #endregion Fields
363  
364 // ~SceneObjectPart()
365 // {
366 // Console.WriteLine(
367 // "[SCENE OBJECT PART]: Destructor called for {0}, local id {1}, parent {2} {3}",
368 // Name, LocalId, ParentGroup.Name, ParentGroup.LocalId);
369 // m_log.DebugFormat(
370 // "[SCENE OBJECT PART]: Destructor called for {0}, local id {1}, parent {2} {3}",
371 // Name, LocalId, ParentGroup.Name, ParentGroup.LocalId);
372 // }
373  
374 #region Constructors
375  
376 /// <summary>
377 /// No arg constructor called by region restore db code
378 /// </summary>
379 public SceneObjectPart()
380 {
381 m_TextureAnimation = Utils.EmptyBytes;
382 m_particleSystem = Utils.EmptyBytes;
383 Rezzed = DateTime.UtcNow;
384 Description = String.Empty;
385 DynAttrs = new DAMap();
386  
387 // Prims currently only contain a single folder (Contents). From looking at the Second Life protocol,
388 // this appears to have the same UUID (!) as the prim. If this isn't the case, one can't drag items from
389 // the prim into an agent inventory (Linden client reports that the "Object not found for drop" in its log
390 m_inventory = new SceneObjectPartInventory(this);
391 }
392  
393 /// <summary>
394 /// Create a completely new SceneObjectPart (prim). This will need to be added separately to a SceneObjectGroup
395 /// </summary>
396 /// <param name="ownerID"></param>
397 /// <param name="shape"></param>
398 /// <param name="position"></param>
399 /// <param name="rotationOffset"></param>
400 /// <param name="offsetPosition"></param>
401 public SceneObjectPart(
402 UUID ownerID, PrimitiveBaseShape shape, Vector3 groupPosition,
403 Quaternion rotationOffset, Vector3 offsetPosition) : this()
404 {
405 m_name = "Primitive";
406  
407 CreationDate = (int)Utils.DateTimeToUnixTime(Rezzed);
408 LastOwnerID = CreatorID = OwnerID = ownerID;
409 UUID = UUID.Random();
410 Shape = shape;
411 OwnershipCost = 0;
412 ObjectSaleType = 0;
413 SalePrice = 0;
414 Category = 0;
415 GroupPosition = groupPosition;
416 OffsetPosition = offsetPosition;
417 RotationOffset = rotationOffset;
418 Velocity = Vector3.Zero;
419 AngularVelocity = Vector3.Zero;
420 Acceleration = Vector3.Zero;
421 Flags = 0;
422 CreateSelected = true;
423  
424 TrimPermissions();
425 }
426  
427 #endregion Constructors
428  
429 #region XML Schema
430  
431 private UUID _lastOwnerID;
432 private UUID _ownerID;
433 private UUID _groupID;
434 private int _ownershipCost;
435 private byte _objectSaleType;
436 private int _salePrice;
437 private uint _category;
438 private Int32 _creationDate;
439 private uint _parentID = 0;
440 private uint _baseMask = (uint)(PermissionMask.All | PermissionMask.Export);
441 private uint _ownerMask = (uint)(PermissionMask.All | PermissionMask.Export);
442 private uint _groupMask = (uint)PermissionMask.None;
443 private uint _everyoneMask = (uint)PermissionMask.None;
444 private uint _nextOwnerMask = (uint)PermissionMask.All;
445 private PrimFlags _flags = PrimFlags.None;
446 private DateTime m_expires;
447 private DateTime m_rezzed;
448 private bool m_createSelected = false;
449  
450 private UUID _creatorID;
451 public UUID CreatorID
452 {
453 get { return _creatorID; }
454 set { _creatorID = value; }
455 }
456  
457 private string m_creatorData = string.Empty;
458 /// <summary>
459 /// Data about the creator in the form home_url;name
460 /// </summary>
461 public string CreatorData
462 {
463 get { return m_creatorData; }
464 set { m_creatorData = value; }
465 }
466  
467 /// <summary>
468 /// Used by the DB layer to retrieve / store the entire user identification.
469 /// The identification can either be a simple UUID or a string of the form
470 /// uuid[;home_url[;name]]
471 /// </summary>
472 public string CreatorIdentification
473 {
474 get
475 {
476 if (CreatorData != null && CreatorData != string.Empty)
477 return CreatorID.ToString() + ';' + CreatorData;
478 else
479 return CreatorID.ToString();
480 }
481 set
482 {
483 if ((value == null) || (value != null && value == string.Empty))
484 {
485 CreatorData = string.Empty;
486 return;
487 }
488  
489 if (!value.Contains(";")) // plain UUID
490 {
491 UUID uuid = UUID.Zero;
492 UUID.TryParse(value, out uuid);
493 CreatorID = uuid;
494 }
495 else // <uuid>[;<endpoint>[;name]]
496 {
497 string name = "Unknown User";
498 string[] parts = value.Split(';');
499 if (parts.Length >= 1)
500 {
501 UUID uuid = UUID.Zero;
502 UUID.TryParse(parts[0], out uuid);
503 CreatorID = uuid;
504 }
505 if (parts.Length >= 2)
506 {
507 CreatorData = parts[1];
508 if (!CreatorData.EndsWith("/"))
509 CreatorData += "/";
510 }
511 if (parts.Length >= 3)
512 name = parts[2];
513  
514 CreatorData += ';' + name;
515  
516 }
517 }
518 }
519  
520 /// <summary>
521 /// A relic from when we we thought that prims contained folder objects. In
522 /// reality, prim == folder
523 /// Exposing this is not particularly good, but it's one of the least evils at the moment to see
524 /// folder id from prim inventory item data, since it's not (yet) actually stored with the prim.
525 /// </summary>
526 public UUID FolderID
527 {
528 get { return UUID; }
529 set { } // Don't allow assignment, or legacy prims wil b0rk - but we need the setter for legacy serialization.
530 }
531  
532 /// <value>
533 /// Access should be via Inventory directly - this property temporarily remains for xml serialization purposes
534 /// </value>
535 public uint InventorySerial
536 {
537 get { return m_inventory.Serial; }
538 set { m_inventory.Serial = value; }
539 }
540  
541 /// <value>
542 /// Access should be via Inventory directly - this property temporarily remains for xml serialization purposes
543 /// </value>
544 public TaskInventoryDictionary TaskInventory
545 {
546 get { return m_inventory.Items; }
547 set { m_inventory.Items = value; }
548 }
549  
550 /// <summary>
551 /// This is idential to the Flags property, except that the returned value is uint rather than PrimFlags
552 /// </summary>
553 [Obsolete("Use Flags property instead")]
554 public uint ObjectFlags
555 {
556 get { return (uint)Flags; }
557 set { Flags = (PrimFlags)value; }
558 }
559  
560 public UUID UUID
561 {
562 get { return m_uuid; }
563 set
564 {
565 m_uuid = value;
566  
567 // This is necessary so that TaskInventoryItem parent ids correctly reference the new uuid of this part
568 if (Inventory != null)
569 Inventory.ResetObjectID();
570 }
571 }
572  
573 public uint LocalId
574 {
575 get { return m_localId; }
576 set
577 {
578 m_localId = value;
579 // m_log.DebugFormat("[SCENE OBJECT PART]: Set part {0} to local id {1}", Name, m_localId);
580 }
581 }
582  
583 public virtual string Name
584 {
585 get { return m_name; }
586 set
587 {
588 m_name = value;
589  
590 PhysicsActor pa = PhysActor;
591  
592 if (pa != null)
593 pa.SOPName = value;
594 }
595 }
596  
597 public byte Material
598 {
599 get { return (byte) m_material; }
600 set
601 {
602 m_material = (Material)value;
603  
604 PhysicsActor pa = PhysActor;
605  
606 if (pa != null)
607 pa.SetMaterial((int)value);
608 }
609 }
610  
611 [XmlIgnore]
612 public bool PassTouches
613 {
614 get { return m_passTouches; }
615 set
616 {
617 m_passTouches = value;
618  
619 if (ParentGroup != null)
620 ParentGroup.HasGroupChanged = true;
621 }
622 }
623  
624 public bool PassCollisions
625 {
626 get { return m_passCollisions; }
627 set
628 {
629 m_passCollisions = value;
630  
631 if (ParentGroup != null)
632 ParentGroup.HasGroupChanged = true;
633 }
634 }
635  
636 public Dictionary<int, string> CollisionFilter
637 {
638 get { return m_CollisionFilter; }
639 set
640 {
641 m_CollisionFilter = value;
642 }
643 }
644  
645 protected Quaternion APIDTarget
646 {
647 get { return m_APIDTarget; }
648 set { m_APIDTarget = value; }
649 }
650  
651  
652 protected float APIDDamp
653 {
654 get { return m_APIDDamp; }
655 set { m_APIDDamp = value; }
656 }
657  
658  
659 protected float APIDStrength
660 {
661 get { return m_APIDStrength; }
662 set { m_APIDStrength = value; }
663 }
664  
665 public ulong RegionHandle
666 {
667 get { return m_regionHandle; }
668 set { m_regionHandle = value; }
669 }
670  
671 public int ScriptAccessPin
672 {
673 get { return m_scriptAccessPin; }
674 set { m_scriptAccessPin = (int)value; }
675 }
676 private SceneObjectPart m_PlaySoundMasterPrim = null;
677 public SceneObjectPart PlaySoundMasterPrim
678 {
679 get { return m_PlaySoundMasterPrim; }
680 set { m_PlaySoundMasterPrim = value; }
681 }
682  
683 private List<SceneObjectPart> m_PlaySoundSlavePrims = new List<SceneObjectPart>();
684 public List<SceneObjectPart> PlaySoundSlavePrims
685 {
686 get { return m_PlaySoundSlavePrims; }
687 set { m_PlaySoundSlavePrims = value; }
688 }
689  
690 private SceneObjectPart m_LoopSoundMasterPrim = null;
691 public SceneObjectPart LoopSoundMasterPrim
692 {
693 get { return m_LoopSoundMasterPrim; }
694 set { m_LoopSoundMasterPrim = value; }
695 }
696  
697 private List<SceneObjectPart> m_LoopSoundSlavePrims = new List<SceneObjectPart>();
698 public List<SceneObjectPart> LoopSoundSlavePrims
699 {
700 get { return m_LoopSoundSlavePrims; }
701 set { m_LoopSoundSlavePrims = value; }
702 }
703  
704  
705 public Byte[] TextureAnimation
706 {
707 get { return m_TextureAnimation; }
708 set { m_TextureAnimation = value; }
709 }
710  
711  
712 public Byte[] ParticleSystem
713 {
714 get { return m_particleSystem; }
715 set { m_particleSystem = value; }
716 }
717  
718  
719 public DateTime Expires
720 {
721 get { return m_expires; }
722 set { m_expires = value; }
723 }
724  
725  
726 public DateTime Rezzed
727 {
728 get { return m_rezzed; }
729 set { m_rezzed = value; }
730 }
731  
732  
733 public float Damage
734 {
735 get { return m_damage; }
736 set { m_damage = value; }
737 }
738  
739 /// <summary>
740 /// The position of the entire group that this prim belongs to.
741 /// </summary>
742 public Vector3 GroupPosition
743 {
744 get
745 {
746 // If this is a linkset, we don't want the physics engine mucking up our group position here.
747 PhysicsActor actor = PhysActor;
748 // If physical and the root prim of a linkset, the position of the group is what physics thinks.
749 if (actor != null && ParentID == 0)
750 m_groupPosition = actor.Position;
751  
752 // If I'm an attachment, my position is reported as the position of who I'm attached to
753 if (ParentGroup.IsAttachment)
754 {
755 ScenePresence sp = ParentGroup.Scene.GetScenePresence(ParentGroup.AttachedAvatar);
756 if (sp != null)
757 return sp.AbsolutePosition;
758 }
759  
760 return m_groupPosition;
761 }
762 set
763 {
764 m_groupPosition = value;
765  
766 PhysicsActor actor = PhysActor;
767 if (actor != null)
768 {
769 try
770 {
771 // Root prim actually goes at Position
772 if (ParentID == 0)
773 {
774 actor.Position = value;
775 }
776 else
777 {
778 // The physics engine always sees all objects (root or linked) in world coordinates.
779 actor.Position = GetWorldPosition();
780 actor.Orientation = GetWorldRotation();
781 }
782  
783 // Tell the physics engines that this prim changed.
784 if (ParentGroup != null && ParentGroup.Scene != null && ParentGroup.Scene.PhysicsScene != null)
785 ParentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(actor);
786 }
787 catch (Exception e)
788 {
789 m_log.ErrorFormat("[SCENEOBJECTPART]: GROUP POSITION. {0}", e);
790 }
791 }
792  
793 // TODO if we decide to do sitting in a more SL compatible way (multiple avatars per prim), this has to be fixed, too
794 if (SitTargetAvatar != UUID.Zero)
795 {
796 ScenePresence avatar;
797 if (ParentGroup.Scene.TryGetScenePresence(SitTargetAvatar, out avatar))
798 {
799 avatar.ParentPosition = GetWorldPosition();
800 }
801 }
802 }
803 }
804  
805 public Vector3 OffsetPosition
806 {
807 get { return m_offsetPosition; }
808 set
809 {
810 // StoreUndoState();
811 m_offsetPosition = value;
812  
813 if (ParentGroup != null && !ParentGroup.IsDeleted)
814 {
815 PhysicsActor actor = PhysActor;
816 if (ParentID != 0 && actor != null)
817 {
818 actor.Position = GetWorldPosition();
819 actor.Orientation = GetWorldRotation();
820  
821 // Tell the physics engines that this prim changed.
822 if (ParentGroup.Scene != null)
823 ParentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(actor);
824 }
825 }
826 }
827 }
828  
829 public Vector3 RelativePosition
830 {
831 get
832 {
833 if (IsRoot)
834 {
835 if (ParentGroup.IsAttachment)
836 return AttachedPos;
837 else
838 return AbsolutePosition;
839 }
840 else
841 {
842 return OffsetPosition;
843 }
844 }
845 }
846  
847 public Quaternion RotationOffset
848 {
849 get
850 {
851 // We don't want the physics engine mucking up the rotations in a linkset
852 PhysicsActor actor = PhysActor;
853 // If this is a root of a linkset, the real rotation is what the physics engine thinks.
854 // If not a root prim, the offset rotation is computed by SOG and is relative to the root.
855 if (ParentID == 0 && (Shape.PCode != 9 || Shape.State == 0) && actor != null)
856 {
857 if (actor.Orientation.X != 0f || actor.Orientation.Y != 0f
858 || actor.Orientation.Z != 0f || actor.Orientation.W != 0f)
859 {
860 m_rotationOffset = actor.Orientation;
861 }
862 }
863  
864 // float roll, pitch, yaw = 0;
865 // m_rotationOffset.GetEulerAngles(out roll, out pitch, out yaw);
866 //
867 // m_log.DebugFormat(
868 // "[SCENE OBJECT PART]: Got euler {0} for RotationOffset on {1} {2}",
869 // new Vector3(roll, pitch, yaw), Name, LocalId);
870  
871 return m_rotationOffset;
872 }
873  
874 set
875 {
876 StoreUndoState();
877 m_rotationOffset = value;
878  
879 PhysicsActor actor = PhysActor;
880 if (actor != null)
881 {
882 try
883 {
884 // Root prim gets value directly
885 if (ParentID == 0)
886 {
887 actor.Orientation = value;
888 //m_log.Info("[PART]: RO1:" + actor.Orientation.ToString());
889 }
890 else
891 {
892 // Child prim we have to calculate it's world rotationwel
893 Quaternion resultingrotation = GetWorldRotation();
894 actor.Orientation = resultingrotation;
895 //m_log.Info("[PART]: RO2:" + actor.Orientation.ToString());
896 }
897  
898 if (ParentGroup != null && ParentGroup.Scene != null && ParentGroup.Scene.PhysicsScene != null)
899 ParentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(actor);
900 //}
901 }
902 catch (Exception ex)
903 {
904 m_log.Error("[SCENEOBJECTPART]: ROTATIONOFFSET" + ex.Message);
905 }
906 }
907  
908 // float roll, pitch, yaw = 0;
909 // m_rotationOffset.GetEulerAngles(out roll, out pitch, out yaw);
910 //
911 // m_log.DebugFormat(
912 // "[SCENE OBJECT PART]: Set euler {0} for RotationOffset on {1} {2}",
913 // new Vector3(roll, pitch, yaw), Name, LocalId);
914 }
915 }
916  
917 /// <summary></summary>
918 public Vector3 Velocity
919 {
920 get
921 {
922 PhysicsActor actor = PhysActor;
923 if (actor != null)
924 {
925 if (actor.IsPhysical)
926 {
927 m_velocity = actor.Velocity;
928 }
929 }
930  
931 return m_velocity;
932 }
933  
934 set
935 {
936 m_velocity = value;
937  
938 PhysicsActor actor = PhysActor;
939 if (actor != null)
940 {
941 if (actor.IsPhysical)
942 {
943 actor.Velocity = value;
944 ParentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(actor);
945 }
946 }
947 }
948 }
949  
950 /// <summary>Update angular velocity and schedule terse update.</summary>
951 public void UpdateAngularVelocity(Vector3 avel)
952 {
953 AngularVelocity = avel;
954 ScheduleTerseUpdate();
955 ParentGroup.HasGroupChanged = true;
956 }
957  
958 /// <summary>Get or set angular velocity. Does not schedule update.</summary>
959 public Vector3 AngularVelocity
960 {
961 get
962 {
963 PhysicsActor actor = PhysActor;
964 if ((actor != null) && actor.IsPhysical)
965 {
966 m_angularVelocity = actor.RotationalVelocity;
967 }
968 return m_angularVelocity;
969 }
970 set { m_angularVelocity = value; }
971 }
972  
973 /// <summary></summary>
974 public Vector3 Acceleration
975 {
976 get { return m_acceleration; }
977 set { m_acceleration = value; }
978 }
979  
980 public string Description { get; set; }
981  
982 /// <value>
983 /// Text color.
984 /// </value>
985 public Color Color
986 {
987 get { return m_color; }
988 set { m_color = value; }
989 }
990  
991 public string Text
992 {
993 get
994 {
995 if (m_text.Length > 255)
996 return m_text.Substring(0, 254);
997 return m_text;
998 }
999 set { m_text = value; }
1000 }
1001  
1002  
1003 public string SitName
1004 {
1005 get { return m_sitName; }
1006 set { m_sitName = value; }
1007 }
1008  
1009 public string TouchName
1010 {
1011 get { return m_touchName; }
1012 set { m_touchName = value; }
1013 }
1014  
1015 public int LinkNum
1016 {
1017 get { return m_linkNum; }
1018 set
1019 {
1020 // if (ParentGroup != null)
1021 // {
1022 // m_log.DebugFormat(
1023 // "[SCENE OBJECT PART]: Setting linknum of {0}@{1} to {2} from {3}",
1024 // Name, AbsolutePosition, value, m_linkNum);
1025 // Util.PrintCallStack();
1026 // }
1027  
1028 m_linkNum = value;
1029 }
1030 }
1031  
1032 public byte ClickAction
1033 {
1034 get { return m_clickAction; }
1035 set
1036 {
1037 m_clickAction = value;
1038 }
1039 }
1040  
1041 public PrimitiveBaseShape Shape
1042 {
1043 get { return m_shape; }
1044 set { m_shape = value;}
1045 }
1046  
1047 /// <summary>
1048 /// Change the scale of this part.
1049 /// </summary>
1050 public Vector3 Scale
1051 {
1052 get { return m_shape.Scale; }
1053 set
1054 {
1055 if (m_shape != null)
1056 {
1057 StoreUndoState();
1058  
1059 m_shape.Scale = value;
1060  
1061 PhysicsActor actor = PhysActor;
1062 if (actor != null)
1063 {
1064 if (ParentGroup.Scene != null)
1065 {
1066 if (ParentGroup.Scene.PhysicsScene != null)
1067 {
1068 actor.Size = m_shape.Scale;
1069  
1070 // if (Shape.SculptEntry)
1071 // CheckSculptAndLoad();
1072 // else
1073 ParentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(actor);
1074 }
1075 }
1076 }
1077 }
1078  
1079 TriggerScriptChangedEvent(Changed.SCALE);
1080 }
1081 }
1082  
1083 public UpdateRequired UpdateFlag { get; set; }
1084 public bool UpdatePhysRequired { get; set; }
1085  
1086 /// <summary>
1087 /// Used for media on a prim.
1088 /// </summary>
1089 /// Do not change this value directly - always do it through an IMoapModule.
1090 public string MediaUrl
1091 {
1092 get
1093 {
1094 return m_mediaUrl;
1095 }
1096  
1097 set
1098 {
1099 m_mediaUrl = value;
1100  
1101 if (ParentGroup != null)
1102 ParentGroup.HasGroupChanged = true;
1103 }
1104 }
1105  
1106 public bool CreateSelected
1107 {
1108 get { return m_createSelected; }
1109 set
1110 {
1111 // m_log.DebugFormat("[SOP]: Setting CreateSelected to {0} for {1} {2}", value, Name, UUID);
1112 m_createSelected = value;
1113 }
1114 }
1115  
1116 #endregion
1117  
1118 //---------------
1119 #region Public Properties with only Get
1120  
1121 public Vector3 AbsolutePosition
1122 {
1123 get
1124 {
1125 if (ParentGroup.IsAttachment)
1126 return GroupPosition;
1127  
1128 return m_offsetPosition + m_groupPosition;
1129 }
1130 }
1131  
1132 public SceneObjectGroup ParentGroup
1133 {
1134 get { return m_parentGroup; }
1135 private set { m_parentGroup = value; }
1136 }
1137  
1138 public scriptEvents ScriptEvents
1139 {
1140 get { return AggregateScriptEvents; }
1141 }
1142  
1143 public Quaternion SitTargetOrientation
1144 {
1145 get { return m_sitTargetOrientation; }
1146 set
1147 {
1148 m_sitTargetOrientation = value;
1149 // m_log.DebugFormat("[SCENE OBJECT PART]: Set sit target orientation {0} for {1} {2}", m_sitTargetOrientation, Name, LocalId);
1150 }
1151 }
1152  
1153 public Vector3 SitTargetPosition
1154 {
1155 get { return m_sitTargetPosition; }
1156 set
1157 {
1158 m_sitTargetPosition = value;
1159 // m_log.DebugFormat("[SCENE OBJECT PART]: Set sit target position to {0} for {1} {2}", m_sitTargetPosition, Name, LocalId);
1160 }
1161 }
1162  
1163 // This sort of sucks, but I'm adding these in to make some of
1164 // the mappings more consistant.
1165 public Vector3 SitTargetPositionLL
1166 {
1167 get { return m_sitTargetPosition; }
1168 set { m_sitTargetPosition = value; }
1169 }
1170  
1171 public Quaternion SitTargetOrientationLL
1172 {
1173 get { return m_sitTargetOrientation; }
1174 set { m_sitTargetOrientation = value; }
1175 }
1176  
1177 public bool Stopped
1178 {
1179 get {
1180 double threshold = 0.02;
1181 return (Math.Abs(Velocity.X) < threshold &&
1182 Math.Abs(Velocity.Y) < threshold &&
1183 Math.Abs(Velocity.Z) < threshold &&
1184 Math.Abs(AngularVelocity.X) < threshold &&
1185 Math.Abs(AngularVelocity.Y) < threshold &&
1186 Math.Abs(AngularVelocity.Z) < threshold);
1187 }
1188 }
1189  
1190 /// <summary>
1191 /// The parent ID of this part.
1192 /// </summary>
1193 /// <remarks>
1194 /// If this is a root part which is not attached to an avatar then the value will be 0.
1195 /// If this is a root part which is attached to an avatar then the value is the local id of that avatar.
1196 /// If this is a child part then the value is the local ID of the root part.
1197 /// </remarks>
1198 public uint ParentID
1199 {
1200 get { return _parentID; }
1201 set { _parentID = value; }
1202 }
1203  
1204 public int CreationDate
1205 {
1206 get { return _creationDate; }
1207 set { _creationDate = value; }
1208 }
1209  
1210 public uint Category
1211 {
1212 get { return _category; }
1213 set { _category = value; }
1214 }
1215  
1216 public int SalePrice
1217 {
1218 get { return _salePrice; }
1219 set { _salePrice = value; }
1220 }
1221  
1222 public byte ObjectSaleType
1223 {
1224 get { return _objectSaleType; }
1225 set { _objectSaleType = value; }
1226 }
1227  
1228 public int OwnershipCost
1229 {
1230 get { return _ownershipCost; }
1231 set { _ownershipCost = value; }
1232 }
1233  
1234 public UUID GroupID
1235 {
1236 get { return _groupID; }
1237 set { _groupID = value; }
1238 }
1239  
1240 public UUID OwnerID
1241 {
1242 get { return _ownerID; }
1243 set { _ownerID = value; }
1244 }
1245  
1246 public UUID LastOwnerID
1247 {
1248 get { return _lastOwnerID; }
1249 set { _lastOwnerID = value; }
1250 }
1251  
1252 public uint BaseMask
1253 {
1254 get { return _baseMask; }
1255 set { _baseMask = value; }
1256 }
1257  
1258 public uint OwnerMask
1259 {
1260 get { return _ownerMask; }
1261 set { _ownerMask = value; }
1262 }
1263  
1264 public uint GroupMask
1265 {
1266 get { return _groupMask; }
1267 set { _groupMask = value; }
1268 }
1269  
1270 public uint EveryoneMask
1271 {
1272 get { return _everyoneMask; }
1273 set { _everyoneMask = value; }
1274 }
1275  
1276 public uint NextOwnerMask
1277 {
1278 get { return _nextOwnerMask; }
1279 set { _nextOwnerMask = value; }
1280 }
1281  
1282 /// <summary>
1283 /// Property flags. See OpenMetaverse.PrimFlags
1284 /// </summary>
1285 /// <remarks>
1286 /// Example properties are PrimFlags.Phantom and PrimFlags.DieAtEdge
1287 /// </remarks>
1288 public PrimFlags Flags
1289 {
1290 get { return _flags; }
1291 set
1292 {
1293 // m_log.DebugFormat("[SOP]: Setting flags for {0} {1} to {2}", UUID, Name, value);
1294 _flags = value;
1295 }
1296 }
1297  
1298 /// <summary>
1299 /// ID of the avatar that is sat on us if we have a sit target. If there is no such avatar then is UUID.Zero
1300 /// </summary>
1301 public UUID SitTargetAvatar { get; set; }
1302  
1303 /// <summary>
1304 /// IDs of all avatars sat on this part.
1305 /// </summary>
1306 /// <remarks>
1307 /// We need to track this so that we can stop sat upon prims from being attached.
1308 /// </remarks>
1309 /// <value>
1310 /// null if there are no sitting avatars. This is to save us create a hashset for every prim in a scene.
1311 /// </value>
1312 private HashSet<UUID> m_sittingAvatars;
1313  
1314 public virtual UUID RegionID
1315 {
1316 get
1317 {
1318 if (ParentGroup.Scene != null)
1319 return ParentGroup.Scene.RegionInfo.RegionID;
1320 else
1321 return UUID.Zero;
1322 }
1323 set {} // read only
1324 }
1325  
1326 private UUID _parentUUID = UUID.Zero;
1327  
1328 public UUID ParentUUID
1329 {
1330 get
1331 {
1332 if (ParentGroup != null)
1333 _parentUUID = ParentGroup.UUID;
1334  
1335 return _parentUUID;
1336 }
1337  
1338 set { _parentUUID = value; }
1339 }
1340  
1341 public string SitAnimation
1342 {
1343 get { return m_sitAnimation; }
1344 set { m_sitAnimation = value; }
1345 }
1346  
1347 public UUID CollisionSound
1348 {
1349 get { return m_collisionSound; }
1350 set
1351 {
1352 m_collisionSound = value;
1353 aggregateScriptEvents();
1354 }
1355 }
1356  
1357 public float CollisionSoundVolume
1358 {
1359 get { return m_collisionSoundVolume; }
1360 set { m_collisionSoundVolume = value; }
1361 }
1362  
1363 public byte DefaultPhysicsShapeType()
1364 {
1365 byte type;
1366  
1367 if (Shape != null && (Shape.SculptType == (byte)SculptType.Mesh))
1368 type = (byte)PhysShapeType.convex;
1369 else
1370 type = (byte)PhysShapeType.prim;
1371  
1372 return type;
1373 }
1374  
1375 public byte PhysicsShapeType
1376 {
1377 get { return m_physicsShapeType; }
1378 set
1379 {
1380 byte oldv = m_physicsShapeType;
1381  
1382 if (value >= 0 && value <= (byte)PhysShapeType.convex)
1383 {
1384 if (value == (byte)PhysShapeType.none && ParentGroup != null && ParentGroup.RootPart == this)
1385 m_physicsShapeType = DefaultPhysicsShapeType();
1386 else
1387 m_physicsShapeType = value;
1388 }
1389 else
1390 m_physicsShapeType = DefaultPhysicsShapeType();
1391  
1392 if (m_physicsShapeType != oldv && ParentGroup != null)
1393 {
1394 if (m_physicsShapeType == (byte)PhysShapeType.none)
1395 {
1396 if (PhysActor != null)
1397 {
1398 Velocity = new Vector3(0, 0, 0);
1399 Acceleration = new Vector3(0, 0, 0);
1400 if (ParentGroup.RootPart == this)
1401 AngularVelocity = new Vector3(0, 0, 0);
1402 ParentGroup.Scene.RemovePhysicalPrim(1);
1403 RemoveFromPhysics();
1404 }
1405 }
1406 else if (PhysActor == null)
1407 {
1408 ApplyPhysics((uint)Flags, VolumeDetectActive);
1409 }
1410 else
1411 {
1412 PhysActor.PhysicsShapeType = m_physicsShapeType;
1413 }
1414  
1415 if (ParentGroup != null)
1416 ParentGroup.HasGroupChanged = true;
1417 }
1418  
1419 if (m_physicsShapeType != value)
1420 {
1421 UpdatePhysRequired = true;
1422 }
1423 }
1424 }
1425  
1426 public float Density // in kg/m^3
1427 {
1428 get { return m_density; }
1429 set
1430 {
1431 if (value >=1 && value <= 22587.0)
1432 {
1433 m_density = value;
1434 UpdatePhysRequired = true;
1435 }
1436  
1437 ScheduleFullUpdateIfNone();
1438  
1439 if (ParentGroup != null)
1440 ParentGroup.HasGroupChanged = true;
1441  
1442 PhysicsActor pa = PhysActor;
1443 if (pa != null)
1444 pa.Density = Density;
1445 }
1446 }
1447  
1448 public float GravityModifier
1449 {
1450 get { return m_gravitymod; }
1451 set
1452 {
1453 if( value >= -1 && value <=28.0f)
1454 {
1455 m_gravitymod = value;
1456 UpdatePhysRequired = true;
1457 }
1458  
1459 ScheduleFullUpdateIfNone();
1460  
1461 if (ParentGroup != null)
1462 ParentGroup.HasGroupChanged = true;
1463  
1464 PhysicsActor pa = PhysActor;
1465 if (pa != null)
1466 pa.GravModifier = GravityModifier;
1467 }
1468 }
1469  
1470 public float Friction
1471 {
1472 get { return m_friction; }
1473 set
1474 {
1475 if (value >= 0 && value <= 255.0f)
1476 {
1477 m_friction = value;
1478 UpdatePhysRequired = true;
1479 }
1480  
1481 ScheduleFullUpdateIfNone();
1482  
1483 if (ParentGroup != null)
1484 ParentGroup.HasGroupChanged = true;
1485  
1486 PhysicsActor pa = PhysActor;
1487 if (pa != null)
1488 pa.Friction = Friction;
1489 }
1490 }
1491  
1492 public float Restitution
1493 {
1494 get { return m_bounce; }
1495 set
1496 {
1497 if (value >= 0 && value <= 1.0f)
1498 {
1499 m_bounce = value;
1500 UpdatePhysRequired = true;
1501 }
1502  
1503 ScheduleFullUpdateIfNone();
1504  
1505 if (ParentGroup != null)
1506 ParentGroup.HasGroupChanged = true;
1507  
1508 PhysicsActor pa = PhysActor;
1509 if (pa != null)
1510 pa.Restitution = Restitution;
1511 }
1512 }
1513  
1514 #endregion Public Properties with only Get
1515  
1516 private uint ApplyMask(uint val, bool set, uint mask)
1517 {
1518 if (set)
1519 {
1520 return val |= mask;
1521 }
1522 else
1523 {
1524 return val &= ~mask;
1525 }
1526 }
1527  
1528 /// <summary>
1529 /// Clear all pending updates of parts to clients
1530 /// </summary>
1531 public void ClearUpdateSchedule()
1532 {
1533 UpdateFlag = UpdateRequired.NONE;
1534 }
1535  
1536 /// <summary>
1537 /// Send this part's properties (name, description, inventory serial, base mask, etc.) to a client
1538 /// </summary>
1539 /// <param name="client"></param>
1540 public void SendPropertiesToClient(IClientAPI client)
1541 {
1542 client.SendObjectPropertiesReply(this);
1543 }
1544  
1545 // TODO: unused:
1546 // private void handleTimerAccounting(uint localID, double interval)
1547 // {
1548 // if (localID == LocalId)
1549 // {
1550 // float sec = (float)interval;
1551 // if (m_parentGroup != null)
1552 // {
1553 // if (sec == 0)
1554 // {
1555 // if (m_parentGroup.scriptScore + 0.001f >= float.MaxValue - 0.001)
1556 // m_parentGroup.scriptScore = 0;
1557 //
1558 // m_parentGroup.scriptScore += 0.001f;
1559 // return;
1560 // }
1561 //
1562 // if (m_parentGroup.scriptScore + (0.001f / sec) >= float.MaxValue - (0.001f / sec))
1563 // m_parentGroup.scriptScore = 0;
1564 // m_parentGroup.scriptScore += (0.001f / sec);
1565 // }
1566 // }
1567 // }
1568  
1569 #region Public Methods
1570  
1571 public void ResetExpire()
1572 {
1573 Expires = DateTime.Now + new TimeSpan(600000000);
1574 }
1575  
1576 public void AddFlag(PrimFlags flag)
1577 {
1578 // PrimFlags prevflag = Flags;
1579 if ((Flags & flag) == 0)
1580 {
1581 //m_log.Debug("Adding flag: " + ((PrimFlags) flag).ToString());
1582 Flags |= flag;
1583  
1584 if (flag == PrimFlags.TemporaryOnRez)
1585 ResetExpire();
1586 }
1587 // m_log.Debug("Aprev: " + prevflag.ToString() + " curr: " + Flags.ToString());
1588 }
1589  
1590 public void AddNewParticleSystem(Primitive.ParticleSystem pSystem)
1591 {
1592 m_particleSystem = pSystem.GetBytes();
1593 }
1594  
1595 public void RemoveParticleSystem()
1596 {
1597 m_particleSystem = new byte[0];
1598 }
1599  
1600 public void AddTextureAnimation(Primitive.TextureAnimation pTexAnim)
1601 {
1602 byte[] data = new byte[16];
1603 int pos = 0;
1604  
1605 // The flags don't like conversion from uint to byte, so we have to do
1606 // it the crappy way. See the above function :(
1607  
1608 data[pos] = ConvertScriptUintToByte((uint)pTexAnim.Flags); pos++;
1609 data[pos] = (byte)pTexAnim.Face; pos++;
1610 data[pos] = (byte)pTexAnim.SizeX; pos++;
1611 data[pos] = (byte)pTexAnim.SizeY; pos++;
1612  
1613 Utils.FloatToBytes(pTexAnim.Start).CopyTo(data, pos);
1614 Utils.FloatToBytes(pTexAnim.Length).CopyTo(data, pos + 4);
1615 Utils.FloatToBytes(pTexAnim.Rate).CopyTo(data, pos + 8);
1616  
1617 m_TextureAnimation = data;
1618 }
1619  
1620 public void AdjustSoundGain(double volume)
1621 {
1622 if (volume > 1)
1623 volume = 1;
1624 if (volume < 0)
1625 volume = 0;
1626  
1627 ParentGroup.Scene.ForEachRootClient(delegate(IClientAPI client)
1628 {
1629 client.SendAttachedSoundGainChange(UUID, (float)volume);
1630 });
1631 }
1632  
1633 /// <summary>
1634 /// hook to the physics scene to apply impulse
1635 /// This is sent up to the group, which then finds the root prim
1636 /// and applies the force on the root prim of the group
1637 /// </summary>
1638 /// <param name="impulsei">Vector force</param>
1639 /// <param name="localGlobalTF">true for the local frame, false for the global frame</param>
1640 public void ApplyImpulse(Vector3 impulsei, bool localGlobalTF)
1641 {
1642 Vector3 impulse = impulsei;
1643  
1644 if (localGlobalTF)
1645 {
1646 Quaternion grot = GetWorldRotation();
1647 Quaternion AXgrot = grot;
1648 Vector3 AXimpulsei = impulsei;
1649 Vector3 newimpulse = AXimpulsei * AXgrot;
1650 impulse = newimpulse;
1651 }
1652  
1653 if (ParentGroup != null)
1654 {
1655 ParentGroup.applyImpulse(impulse);
1656 }
1657 }
1658  
1659 /// <summary>
1660 /// hook to the physics scene to apply angular impulse
1661 /// This is sent up to the group, which then finds the root prim
1662 /// and applies the force on the root prim of the group
1663 /// </summary>
1664 /// <param name="impulsei">Vector force</param>
1665 /// <param name="localGlobalTF">true for the local frame, false for the global frame</param>
1666 public void ApplyAngularImpulse(Vector3 impulsei, bool localGlobalTF)
1667 {
1668 Vector3 impulse = impulsei;
1669  
1670 if (localGlobalTF)
1671 {
1672 Quaternion grot = GetWorldRotation();
1673 Quaternion AXgrot = grot;
1674 Vector3 AXimpulsei = impulsei;
1675 Vector3 newimpulse = AXimpulsei * AXgrot;
1676 impulse = newimpulse;
1677 }
1678  
1679 ParentGroup.applyAngularImpulse(impulse);
1680 }
1681  
1682 /// <summary>
1683 /// hook to the physics scene to apply angular impulse
1684 /// This is sent up to the group, which then finds the root prim
1685 /// and applies the force on the root prim of the group
1686 /// </summary>
1687 /// <param name="impulsei">Vector force</param>
1688 /// <param name="localGlobalTF">true for the local frame, false for the global frame</param>
1689 public void SetAngularImpulse(Vector3 impulsei, bool localGlobalTF)
1690 {
1691 Vector3 impulse = impulsei;
1692  
1693 if (localGlobalTF)
1694 {
1695 Quaternion grot = GetWorldRotation();
1696 Quaternion AXgrot = grot;
1697 Vector3 AXimpulsei = impulsei;
1698 Vector3 newimpulse = AXimpulsei * AXgrot;
1699 impulse = newimpulse;
1700 }
1701  
1702 ParentGroup.setAngularImpulse(impulse);
1703 }
1704  
1705 /// <summary>
1706 /// Apply physics to this part.
1707 /// </summary>
1708 /// <param name="rootObjectFlags"></param>
1709 /// <param name="VolumeDetectActive"></param>
1710 public void ApplyPhysics(uint rootObjectFlags, bool _VolumeDetectActive)
1711 {
1712 VolumeDetectActive = _VolumeDetectActive;
1713  
1714 if (!ParentGroup.Scene.CollidablePrims)
1715 return;
1716  
1717 if (PhysicsShapeType == (byte)PhysShapeType.none)
1718 return;
1719  
1720 bool isPhysical = (rootObjectFlags & (uint) PrimFlags.Physics) != 0;
1721 bool isPhantom = (rootObjectFlags & (uint) PrimFlags.Phantom) != 0;
1722  
1723 if (_VolumeDetectActive)
1724 isPhantom = true;
1725  
1726 if (IsJoint())
1727 {
1728 DoPhysicsPropertyUpdate(isPhysical, true);
1729 }
1730 else
1731 {
1732 if ((!isPhantom || isPhysical || _VolumeDetectActive) && !ParentGroup.IsAttachment
1733 && !(Shape.PathCurve == (byte)Extrusion.Flexible))
1734 {
1735 AddToPhysics(isPhysical, isPhantom, isPhysical);
1736 }
1737 else
1738 PhysActor = null; // just to be sure
1739 }
1740 }
1741  
1742 public byte ConvertScriptUintToByte(uint indata)
1743 {
1744 byte outdata = (byte)TextureAnimFlags.NONE;
1745 if ((indata & 1) != 0) outdata |= (byte)TextureAnimFlags.ANIM_ON;
1746 if ((indata & 2) != 0) outdata |= (byte)TextureAnimFlags.LOOP;
1747 if ((indata & 4) != 0) outdata |= (byte)TextureAnimFlags.REVERSE;
1748 if ((indata & 8) != 0) outdata |= (byte)TextureAnimFlags.PING_PONG;
1749 if ((indata & 16) != 0) outdata |= (byte)TextureAnimFlags.SMOOTH;
1750 if ((indata & 32) != 0) outdata |= (byte)TextureAnimFlags.ROTATE;
1751 if ((indata & 64) != 0) outdata |= (byte)TextureAnimFlags.SCALE;
1752 return outdata;
1753 }
1754  
1755 /// <summary>
1756 /// Duplicates this part.
1757 /// </summary>
1758 /// <param name="localID"></param>
1759 /// <param name="AgentID"></param>
1760 /// <param name="GroupID"></param>
1761 /// <param name="linkNum"></param>
1762 /// <param name="userExposed">True if the duplicate will immediately be in the scene, false otherwise</param>
1763 /// <returns></returns>
1764 public SceneObjectPart Copy(uint localID, UUID AgentID, UUID GroupID, int linkNum, bool userExposed)
1765 {
1766 SceneObjectPart dupe = (SceneObjectPart)MemberwiseClone();
1767 dupe.m_shape = m_shape.Copy();
1768 dupe.m_regionHandle = m_regionHandle;
1769 if (userExposed)
1770 dupe.UUID = UUID.Random();
1771  
1772 dupe.PhysActor = null;
1773  
1774 dupe.OwnerID = AgentID;
1775 dupe.GroupID = GroupID;
1776 dupe.GroupPosition = GroupPosition;
1777 dupe.OffsetPosition = OffsetPosition;
1778 dupe.RotationOffset = RotationOffset;
1779 dupe.Velocity = Velocity;
1780 dupe.Acceleration = Acceleration;
1781 dupe.AngularVelocity = AngularVelocity;
1782 dupe.Flags = Flags;
1783  
1784 dupe.OwnershipCost = OwnershipCost;
1785 dupe.ObjectSaleType = ObjectSaleType;
1786 dupe.SalePrice = SalePrice;
1787 dupe.Category = Category;
1788 dupe.m_rezzed = m_rezzed;
1789  
1790 dupe.m_inventory = new SceneObjectPartInventory(dupe);
1791 dupe.m_inventory.Items = (TaskInventoryDictionary)m_inventory.Items.Clone();
1792  
1793 if (userExposed)
1794 {
1795 dupe.ResetIDs(linkNum);
1796 dupe.m_inventory.HasInventoryChanged = true;
1797 }
1798 else
1799 {
1800 dupe.m_inventory.HasInventoryChanged = m_inventory.HasInventoryChanged;
1801 }
1802  
1803 // Move afterwards ResetIDs as it clears the localID
1804 dupe.LocalId = localID;
1805 // This may be wrong... it might have to be applied in SceneObjectGroup to the object that's being duplicated.
1806 dupe.LastOwnerID = OwnerID;
1807  
1808 byte[] extraP = new byte[Shape.ExtraParams.Length];
1809 Array.Copy(Shape.ExtraParams, extraP, extraP.Length);
1810 dupe.Shape.ExtraParams = extraP;
1811  
1812 // safeguard actual copy is done in sog.copy
1813 dupe.KeyframeMotion = null;
1814 dupe.PayPrice = (int[])PayPrice.Clone();
1815  
1816 dupe.DynAttrs.CopyFrom(DynAttrs);
1817  
1818 if (userExposed)
1819 {
1820 /*
1821 if (dupe.m_shape.SculptEntry && dupe.m_shape.SculptTexture != UUID.Zero)
1822 {
1823 ParentGroup.Scene.AssetService.Get(
1824 dupe.m_shape.SculptTexture.ToString(), dupe, dupe.AssetReceived);
1825 }
1826 */
1827 bool UsePhysics = ((dupe.Flags & PrimFlags.Physics) != 0);
1828 dupe.DoPhysicsPropertyUpdate(UsePhysics, true);
1829 }
1830  
1831 ParentGroup.Scene.EventManager.TriggerOnSceneObjectPartCopy(dupe, this, userExposed);
1832  
1833 // m_log.DebugFormat("[SCENE OBJECT PART]: Clone of {0} {1} finished", Name, UUID);
1834  
1835 return dupe;
1836 }
1837  
1838 /// <summary>
1839 /// Called back by asynchronous asset fetch.
1840 /// </summary>
1841 /// <param name="id">ID of asset received</param>
1842 /// <param name="sender">Register</param>
1843 /// <param name="asset"></param>
1844 /*
1845 protected void AssetReceived(string id, Object sender, AssetBase asset)
1846 {
1847 if (asset != null)
1848 SculptTextureCallback(asset);
1849 else
1850 m_log.WarnFormat(
1851 "[SCENE OBJECT PART]: Part {0} {1} requested mesh/sculpt data for asset id {2} from asset service but received no data",
1852 Name, UUID, id);
1853 }
1854 */
1855 /// <summary>
1856 /// Do a physics property update for a NINJA joint.
1857 /// </summary>
1858 /// <param name="UsePhysics"></param>
1859 /// <param name="isNew"></param>
1860 protected void DoPhysicsPropertyUpdateForNinjaJoint(bool UsePhysics, bool isNew)
1861 {
1862 if (UsePhysics)
1863 {
1864 // by turning a joint proxy object physical, we cause creation of a joint in the ODE scene.
1865 // note that, as a special case, joints have no bodies or geoms in the physics scene, even though they are physical.
1866  
1867 PhysicsJointType jointType;
1868 if (IsHingeJoint())
1869 {
1870 jointType = PhysicsJointType.Hinge;
1871 }
1872 else if (IsBallJoint())
1873 {
1874 jointType = PhysicsJointType.Ball;
1875 }
1876 else
1877 {
1878 jointType = PhysicsJointType.Ball;
1879 }
1880  
1881 List<string> bodyNames = new List<string>();
1882 string RawParams = Description;
1883 string[] jointParams = RawParams.Split(" ".ToCharArray(), System.StringSplitOptions.RemoveEmptyEntries);
1884 string trackedBodyName = null;
1885 if (jointParams.Length >= 2)
1886 {
1887 for (int iBodyName = 0; iBodyName < 2; iBodyName++)
1888 {
1889 string bodyName = jointParams[iBodyName];
1890 bodyNames.Add(bodyName);
1891 if (bodyName != "NULL")
1892 {
1893 if (trackedBodyName == null)
1894 {
1895 trackedBodyName = bodyName;
1896 }
1897 }
1898 }
1899 }
1900  
1901 SceneObjectPart trackedBody = ParentGroup.Scene.GetSceneObjectPart(trackedBodyName); // FIXME: causes a sequential lookup
1902 Quaternion localRotation = Quaternion.Identity;
1903 if (trackedBody != null)
1904 {
1905 localRotation = Quaternion.Inverse(trackedBody.RotationOffset) * this.RotationOffset;
1906 }
1907 else
1908 {
1909 // error, output it below
1910 }
1911  
1912 PhysicsJoint joint;
1913  
1914 joint = ParentGroup.Scene.PhysicsScene.RequestJointCreation(Name, jointType,
1915 AbsolutePosition,
1916 this.RotationOffset,
1917 Description,
1918 bodyNames,
1919 trackedBodyName,
1920 localRotation);
1921  
1922 if (trackedBody == null)
1923 {
1924 ParentGroup.Scene.jointErrorMessage(joint, "warning: tracked body name not found! joint location will not be updated properly. joint: " + Name);
1925 }
1926 }
1927 else
1928 {
1929 if (isNew)
1930 {
1931 // if the joint proxy is new, and it is not physical, do nothing. There is no joint in ODE to
1932 // delete, and if we try to delete it, due to asynchronous processing, the deletion request
1933 // will get processed later at an indeterminate time, which could cancel a later-arriving
1934 // joint creation request.
1935 }
1936 else
1937 {
1938 // here we turn off the joint object, so remove the joint from the physics scene
1939 ParentGroup.Scene.PhysicsScene.RequestJointDeletion(Name); // FIXME: what if the name changed?
1940  
1941 // make sure client isn't interpolating the joint proxy object
1942 Velocity = Vector3.Zero;
1943 AngularVelocity = Vector3.Zero;
1944 Acceleration = Vector3.Zero;
1945 }
1946 }
1947 }
1948  
1949 /// <summary>
1950 /// Do a physics propery update for this part.
1951 /// </summary>
1952 /// <param name="UsePhysics"></param>
1953 /// <param name="isNew"></param>
1954 public void DoPhysicsPropertyUpdate(bool UsePhysics, bool isNew)
1955 {
1956 if (ParentGroup.Scene == null)
1957 return;
1958  
1959 if (!ParentGroup.Scene.PhysicalPrims && UsePhysics)
1960 return;
1961  
1962 if (IsJoint())
1963 {
1964 DoPhysicsPropertyUpdateForNinjaJoint(UsePhysics, isNew);
1965 }
1966 else
1967 {
1968 PhysicsActor pa = PhysActor;
1969  
1970 if (pa != null)
1971 {
1972 if (UsePhysics != pa.IsPhysical || isNew)
1973 {
1974 if (pa.IsPhysical) // implies UsePhysics==false for this block
1975 {
1976 if (!isNew)
1977 ParentGroup.Scene.RemovePhysicalPrim(1);
1978  
1979 pa.OnRequestTerseUpdate -= PhysicsRequestingTerseUpdate;
1980 pa.OnOutOfBounds -= PhysicsOutOfBounds;
1981 pa.delink();
1982  
1983 if (ParentGroup.Scene.PhysicsScene.SupportsNINJAJoints && (!isNew))
1984 {
1985 // destroy all joints connected to this now deactivated body
1986 ParentGroup.Scene.PhysicsScene.RemoveAllJointsConnectedToActorThreadLocked(pa);
1987 }
1988  
1989 // stop client-side interpolation of all joint proxy objects that have just been deleted
1990 // this is done because RemoveAllJointsConnectedToActor invokes the OnJointDeactivated callback,
1991 // which stops client-side interpolation of deactivated joint proxy objects.
1992 }
1993  
1994 if (!UsePhysics && !isNew)
1995 {
1996 // reset velocity to 0 on physics switch-off. Without that, the client thinks the
1997 // prim still has velocity and continues to interpolate its position along the old
1998 // velocity-vector.
1999 Velocity = new Vector3(0, 0, 0);
2000 Acceleration = new Vector3(0, 0, 0);
2001 AngularVelocity = new Vector3(0, 0, 0);
2002 //RotationalVelocity = new Vector3(0, 0, 0);
2003 }
2004  
2005 pa.IsPhysical = UsePhysics;
2006  
2007 // If we're not what we're supposed to be in the physics scene, recreate ourselves.
2008 //m_parentGroup.Scene.PhysicsScene.RemovePrim(PhysActor);
2009 /// that's not wholesome. Had to make Scene public
2010 //PhysActor = null;
2011  
2012 if ((Flags & PrimFlags.Phantom) == 0)
2013 {
2014 if (UsePhysics)
2015 {
2016 if (ParentGroup.RootPart.KeyframeMotion != null)
2017 ParentGroup.RootPart.KeyframeMotion.Stop();
2018 ParentGroup.RootPart.KeyframeMotion = null;
2019 ParentGroup.Scene.AddPhysicalPrim(1);
2020  
2021 pa.OnRequestTerseUpdate += PhysicsRequestingTerseUpdate;
2022 pa.OnOutOfBounds += PhysicsOutOfBounds;
2023 if (ParentID != 0 && ParentID != LocalId)
2024 {
2025 PhysicsActor parentPa = ParentGroup.RootPart.PhysActor;
2026  
2027 if (parentPa != null)
2028 {
2029 pa.link(parentPa);
2030 }
2031 }
2032 }
2033 }
2034 }
2035  
2036 // If this part is a sculpt then delay the physics update until we've asynchronously loaded the
2037 // mesh data.
2038 // if (Shape.SculptEntry)
2039 // CheckSculptAndLoad();
2040 // else
2041 ParentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(pa);
2042 }
2043 }
2044 }
2045  
2046 /// <summary>
2047 /// Restore this part from the serialized xml representation.
2048 /// </summary>
2049 /// <param name="xmlReader"></param>
2050 /// <returns></returns>
2051 public static SceneObjectPart FromXml(XmlTextReader xmlReader)
2052 {
2053 SceneObjectPart part = SceneObjectSerializer.Xml2ToSOP(xmlReader);
2054  
2055 // for tempOnRez objects, we have to fix the Expire date.
2056 if ((part.Flags & PrimFlags.TemporaryOnRez) != 0)
2057 part.ResetExpire();
2058  
2059 return part;
2060 }
2061  
2062 public bool GetDieAtEdge()
2063 {
2064 if (ParentGroup.IsDeleted)
2065 return false;
2066  
2067 return ParentGroup.RootPart.DIE_AT_EDGE;
2068 }
2069  
2070 public bool GetReturnAtEdge()
2071 {
2072 if (ParentGroup.IsDeleted)
2073 return false;
2074  
2075 return ParentGroup.RootPart.RETURN_AT_EDGE;
2076 }
2077  
2078 public void SetReturnAtEdge(bool p)
2079 {
2080 if (ParentGroup.IsDeleted)
2081 return;
2082  
2083 ParentGroup.RootPart.RETURN_AT_EDGE = p;
2084 }
2085  
2086 public bool GetBlockGrab()
2087 {
2088 if (ParentGroup.IsDeleted)
2089 return false;
2090  
2091 return ParentGroup.RootPart.BlockGrab;
2092 }
2093  
2094 public void SetBlockGrab(bool p)
2095 {
2096 if (ParentGroup.IsDeleted)
2097 return;
2098  
2099 ParentGroup.RootPart.BlockGrab = p;
2100 }
2101  
2102 public void SetStatusSandbox(bool p)
2103 {
2104 if (ParentGroup.IsDeleted)
2105 return;
2106 StatusSandboxPos = ParentGroup.RootPart.AbsolutePosition;
2107 ParentGroup.RootPart.StatusSandbox = p;
2108 }
2109  
2110 public bool GetStatusSandbox()
2111 {
2112 if (ParentGroup.IsDeleted)
2113 return false;
2114  
2115 return ParentGroup.RootPart.StatusSandbox;
2116 }
2117  
2118 public int GetAxisRotation(int axis)
2119 {
2120 //Cannot use ScriptBaseClass constants as no referance to it currently.
2121 if (axis == (int)SceneObjectGroup.axisSelect.STATUS_ROTATE_X)
2122 return STATUS_ROTATE_X;
2123 if (axis == (int)SceneObjectGroup.axisSelect.STATUS_ROTATE_Y)
2124 return STATUS_ROTATE_Y;
2125 if (axis == (int)SceneObjectGroup.axisSelect.STATUS_ROTATE_Z)
2126 return STATUS_ROTATE_Z;
2127  
2128 return 0;
2129 }
2130  
2131 public double GetDistanceTo(Vector3 a, Vector3 b)
2132 {
2133 float dx = a.X - b.X;
2134 float dy = a.Y - b.Y;
2135 float dz = a.Z - b.Z;
2136 return Math.Sqrt(dx * dx + dy * dy + dz * dz);
2137 }
2138  
2139 public uint GetEffectiveObjectFlags()
2140 {
2141 // Commenting this section of code out since it doesn't actually do anything, as enums are handled by
2142 // value rather than reference
2143 // PrimFlags f = _flags;
2144 // if (m_parentGroup == null || m_parentGroup.RootPart == this)
2145 // f &= ~(PrimFlags.Touch | PrimFlags.Money);
2146  
2147 return (uint)Flags | (uint)LocalFlags;
2148 }
2149  
2150 public Vector3 GetGeometricCenter()
2151 {
2152 PhysicsActor pa = PhysActor;
2153  
2154 if (pa != null)
2155 return pa.GeometricCenter;
2156 else
2157 return Vector3.Zero;
2158 }
2159  
2160 public Vector3 GetCenterOfMass()
2161 {
2162 PhysicsActor pa = PhysActor;
2163  
2164 if (pa != null)
2165 return pa.CenterOfMass;
2166 else
2167 return Vector3.Zero;
2168 }
2169  
2170 public float GetMass()
2171 {
2172 PhysicsActor pa = PhysActor;
2173  
2174 if (pa != null)
2175 return pa.Mass;
2176 else
2177 return 0;
2178 }
2179  
2180 public Vector3 GetForce()
2181 {
2182 PhysicsActor pa = PhysActor;
2183  
2184 if (pa != null)
2185 return pa.Force;
2186 else
2187 return Vector3.Zero;
2188 }
2189  
2190 /// <summary>
2191 /// Method for a prim to get it's world position from the group.
2192 /// </summary>
2193 /// <remarks>
2194 /// Remember, the Group Position simply gives the position of the group itself
2195 /// </remarks>
2196 /// <returns>A Linked Child Prim objects position in world</returns>
2197 public Vector3 GetWorldPosition()
2198 {
2199 Vector3 ret;
2200 if (_parentID == 0)
2201 // if a root SOP, my position is what it is
2202 ret = GroupPosition;
2203 else
2204 {
2205 // If a child SOP, my position is relative to the root SOP so take
2206 // my info and add the root's position and rotation to
2207 // get my world position.
2208 Quaternion parentRot = ParentGroup.RootPart.RotationOffset;
2209 Vector3 translationOffsetPosition = OffsetPosition * parentRot;
2210 ret = ParentGroup.AbsolutePosition + translationOffsetPosition;
2211 }
2212 return ret;
2213 }
2214  
2215 /// <summary>
2216 /// Gets the rotation of this prim offset by the group rotation
2217 /// </summary>
2218 /// <returns></returns>
2219 public Quaternion GetWorldRotation()
2220 {
2221 Quaternion newRot;
2222  
2223 if (this.LinkNum == 0 || this.LinkNum == 1)
2224 {
2225 newRot = RotationOffset;
2226 }
2227 else
2228 {
2229 // A child SOP's rotation is relative to the root SOP's rotation.
2230 // Combine them to get my absolute rotation.
2231 Quaternion parentRot = ParentGroup.RootPart.RotationOffset;
2232 Quaternion oldRot = RotationOffset;
2233 newRot = parentRot * oldRot;
2234 }
2235  
2236 return newRot;
2237 }
2238  
2239 public void MoveToTarget(Vector3 target, float tau)
2240 {
2241 if (tau > 0)
2242 {
2243 ParentGroup.moveToTarget(target, tau);
2244 }
2245 else
2246 {
2247 StopMoveToTarget();
2248 }
2249 }
2250  
2251 /// <summary>
2252 /// Uses a PID to attempt to clamp the object on the Z axis at the given height over tau seconds.
2253 /// </summary>
2254 /// <param name="height">Height to hover. Height of zero disables hover.</param>
2255 /// <param name="hoverType">Determines what the height is relative to </param>
2256 /// <param name="tau">Number of seconds over which to reach target</param>
2257 public void SetHoverHeight(float height, PIDHoverType hoverType, float tau)
2258 {
2259 ParentGroup.SetHoverHeight(height, hoverType, tau);
2260 }
2261  
2262 public void StopHover()
2263 {
2264 ParentGroup.SetHoverHeight(0f, PIDHoverType.Ground, 0f);
2265 }
2266  
2267 public virtual void OnGrab(Vector3 offsetPos, IClientAPI remoteClient)
2268 {
2269 }
2270  
2271 public bool CollisionFilteredOut(UUID objectID, string objectName)
2272 {
2273 if(CollisionFilter.Count == 0)
2274 return false;
2275  
2276 if (CollisionFilter.ContainsValue(objectID.ToString()) ||
2277 CollisionFilter.ContainsValue(objectID.ToString() + objectName) ||
2278 CollisionFilter.ContainsValue(UUID.Zero.ToString() + objectName))
2279 {
2280 if (CollisionFilter.ContainsKey(1))
2281 return false;
2282 return true;
2283 }
2284  
2285 if (CollisionFilter.ContainsKey(1))
2286 return true;
2287  
2288 return false;
2289 }
2290  
2291 private DetectedObject CreateDetObject(SceneObjectPart obj)
2292 {
2293 DetectedObject detobj = new DetectedObject();
2294 detobj.keyUUID = obj.UUID;
2295 detobj.nameStr = obj.Name;
2296 detobj.ownerUUID = obj.OwnerID;
2297 detobj.posVector = obj.AbsolutePosition;
2298 detobj.rotQuat = obj.GetWorldRotation();
2299 detobj.velVector = obj.Velocity;
2300 detobj.colliderType = 0;
2301 detobj.groupUUID = obj.GroupID;
2302  
2303 return detobj;
2304 }
2305  
2306 private DetectedObject CreateDetObject(ScenePresence av)
2307 {
2308 DetectedObject detobj = new DetectedObject();
2309 detobj.keyUUID = av.UUID;
2310 detobj.nameStr = av.ControllingClient.Name;
2311 detobj.ownerUUID = av.UUID;
2312 detobj.posVector = av.AbsolutePosition;
2313 detobj.rotQuat = av.Rotation;
2314 detobj.velVector = av.Velocity;
2315 detobj.colliderType = 0;
2316 detobj.groupUUID = av.ControllingClient.ActiveGroupId;
2317  
2318 return detobj;
2319 }
2320  
2321 private DetectedObject CreateDetObjectForGround()
2322 {
2323 DetectedObject detobj = new DetectedObject();
2324 detobj.keyUUID = UUID.Zero;
2325 detobj.nameStr = "";
2326 detobj.ownerUUID = UUID.Zero;
2327 detobj.posVector = ParentGroup.RootPart.AbsolutePosition;
2328 detobj.rotQuat = Quaternion.Identity;
2329 detobj.velVector = Vector3.Zero;
2330 detobj.colliderType = 0;
2331 detobj.groupUUID = UUID.Zero;
2332  
2333 return detobj;
2334 }
2335  
2336 private ColliderArgs CreateColliderArgs(SceneObjectPart dest, List<uint> colliders)
2337 {
2338 ColliderArgs colliderArgs = new ColliderArgs();
2339 List<DetectedObject> colliding = new List<DetectedObject>();
2340 foreach (uint localId in colliders)
2341 {
2342 if (localId == 0)
2343 continue;
2344  
2345 SceneObjectPart obj = ParentGroup.Scene.GetSceneObjectPart(localId);
2346 if (obj != null)
2347 {
2348 if (!dest.CollisionFilteredOut(obj.UUID, obj.Name))
2349 colliding.Add(CreateDetObject(obj));
2350 }
2351 else
2352 {
2353 ScenePresence av = ParentGroup.Scene.GetScenePresence(localId);
2354 if (av != null && (!av.IsChildAgent))
2355 {
2356 if (!dest.CollisionFilteredOut(av.UUID, av.Name))
2357 colliding.Add(CreateDetObject(av));
2358 }
2359 }
2360 }
2361  
2362 colliderArgs.Colliders = colliding;
2363  
2364 return colliderArgs;
2365 }
2366  
2367 private delegate void ScriptCollidingNotification(uint localID, ColliderArgs message);
2368  
2369 private void SendCollisionEvent(scriptEvents ev, List<uint> colliders, ScriptCollidingNotification notify)
2370 {
2371 bool sendToRoot = false;
2372 ColliderArgs CollidingMessage;
2373  
2374 if (colliders.Count > 0)
2375 {
2376 if ((ScriptEvents & ev) != 0)
2377 {
2378 CollidingMessage = CreateColliderArgs(this, colliders);
2379  
2380 if (CollidingMessage.Colliders.Count > 0)
2381 notify(LocalId, CollidingMessage);
2382  
2383 if (PassCollisions)
2384 sendToRoot = true;
2385 }
2386 else
2387 {
2388 if ((ParentGroup.RootPart.ScriptEvents & ev) != 0)
2389 sendToRoot = true;
2390 }
2391 if (sendToRoot && ParentGroup.RootPart != this)
2392 {
2393 CollidingMessage = CreateColliderArgs(ParentGroup.RootPart, colliders);
2394 if (CollidingMessage.Colliders.Count > 0)
2395 notify(ParentGroup.RootPart.LocalId, CollidingMessage);
2396 }
2397 }
2398 }
2399  
2400 private void SendLandCollisionEvent(scriptEvents ev, ScriptCollidingNotification notify)
2401 {
2402 if ((ParentGroup.RootPart.ScriptEvents & ev) != 0)
2403 {
2404 ColliderArgs LandCollidingMessage = new ColliderArgs();
2405 List<DetectedObject> colliding = new List<DetectedObject>();
2406  
2407 colliding.Add(CreateDetObjectForGround());
2408 LandCollidingMessage.Colliders = colliding;
2409  
2410 notify(LocalId, LandCollidingMessage);
2411 }
2412 }
2413  
2414 public void PhysicsCollision(EventArgs e)
2415 {
2416 if (ParentGroup.Scene == null || ParentGroup.IsDeleted)
2417 return;
2418  
2419 // single threaded here
2420 CollisionEventUpdate a = (CollisionEventUpdate)e;
2421 Dictionary<uint, ContactPoint> collissionswith = a.m_objCollisionList;
2422 List<uint> thisHitColliders = new List<uint>();
2423 List<uint> endedColliders = new List<uint>();
2424 List<uint> startedColliders = new List<uint>();
2425  
2426 // calculate things that started colliding this time
2427 // and build up list of colliders this time
2428 foreach (uint localid in collissionswith.Keys)
2429 {
2430 thisHitColliders.Add(localid);
2431 if (!m_lastColliders.Contains(localid))
2432 startedColliders.Add(localid);
2433 }
2434  
2435 // calculate things that ended colliding
2436 foreach (uint localID in m_lastColliders)
2437 {
2438 if (!thisHitColliders.Contains(localID))
2439 endedColliders.Add(localID);
2440 }
2441  
2442 //add the items that started colliding this time to the last colliders list.
2443 foreach (uint localID in startedColliders)
2444 m_lastColliders.Add(localID);
2445  
2446 // remove things that ended colliding from the last colliders list
2447 foreach (uint localID in endedColliders)
2448 m_lastColliders.Remove(localID);
2449  
2450 // play the sound.
2451 if (startedColliders.Count > 0 && CollisionSound != UUID.Zero && CollisionSoundVolume > 0.0f)
2452 {
2453 ISoundModule soundModule = ParentGroup.Scene.RequestModuleInterface<ISoundModule>();
2454 if (soundModule != null)
2455 {
2456 soundModule.SendSound(UUID, CollisionSound,
2457 CollisionSoundVolume, true, 0, 0, false,
2458 false);
2459 }
2460 }
2461  
2462 SendCollisionEvent(scriptEvents.collision_start, startedColliders, ParentGroup.Scene.EventManager.TriggerScriptCollidingStart);
2463 SendCollisionEvent(scriptEvents.collision , m_lastColliders , ParentGroup.Scene.EventManager.TriggerScriptColliding);
2464 SendCollisionEvent(scriptEvents.collision_end , endedColliders , ParentGroup.Scene.EventManager.TriggerScriptCollidingEnd);
2465  
2466 if (startedColliders.Contains(0))
2467 SendLandCollisionEvent(scriptEvents.land_collision_start, ParentGroup.Scene.EventManager.TriggerScriptLandCollidingStart);
2468 if (m_lastColliders.Contains(0))
2469 SendLandCollisionEvent(scriptEvents.land_collision, ParentGroup.Scene.EventManager.TriggerScriptLandColliding);
2470 if (endedColliders.Contains(0))
2471 SendLandCollisionEvent(scriptEvents.land_collision_end, ParentGroup.Scene.EventManager.TriggerScriptLandCollidingEnd);
2472 }
2473  
2474 public void PhysicsOutOfBounds(Vector3 pos)
2475 {
2476 // Note: This is only being called on the root prim at this time.
2477  
2478 m_log.ErrorFormat(
2479 "[SCENE OBJECT PART]: Physical object {0}, localID {1} went out of bounds at {2} in {3}. Stopping at {4} and making non-physical.",
2480 Name, LocalId, pos, ParentGroup.Scene.Name, AbsolutePosition);
2481  
2482 RemFlag(PrimFlags.Physics);
2483 DoPhysicsPropertyUpdate(false, true);
2484 }
2485  
2486 public void PhysicsRequestingTerseUpdate()
2487 {
2488 PhysicsActor pa = PhysActor;
2489  
2490 if (pa != null)
2491 {
2492 Vector3 newpos = new Vector3(pa.Position.GetBytes(), 0);
2493  
2494 if (ParentGroup.Scene.TestBorderCross(newpos, Cardinals.N)
2495 | ParentGroup.Scene.TestBorderCross(newpos, Cardinals.S)
2496 | ParentGroup.Scene.TestBorderCross(newpos, Cardinals.E)
2497 | ParentGroup.Scene.TestBorderCross(newpos, Cardinals.W))
2498 {
2499 ParentGroup.AbsolutePosition = newpos;
2500 return;
2501 }
2502 //ParentGroup.RootPart.m_groupPosition = newpos;
2503 }
2504  
2505 if (pa != null && ParentID != 0 && ParentGroup != null)
2506 {
2507 // Special case where a child object is requesting property updates.
2508 // This happens when linksets are modified to use flexible links rather than
2509 // the default links.
2510 // The simulator code presumes that child parts are only modified by scripts
2511 // so the logic for changing position/rotation/etc does not take into
2512 // account the physical object actually moving.
2513 // This code updates the offset position and rotation of the child and then
2514 // lets the update code push the update to the viewer.
2515 // Since physics engines do not normally generate this event for linkset children,
2516 // this code will not be active unless you have a specially configured
2517 // physics engine.
2518 Quaternion invRootRotation = Quaternion.Normalize(Quaternion.Inverse(ParentGroup.RootPart.RotationOffset));
2519 m_offsetPosition = pa.Position - m_groupPosition;
2520 RotationOffset = pa.Orientation * invRootRotation;
2521 // m_log.DebugFormat("{0} PhysicsRequestingTerseUpdate child: pos={1}, rot={2}, offPos={3}, offRot={4}",
2522 // "[SCENE OBJECT PART]", pa.Position, pa.Orientation, m_offsetPosition, RotationOffset);
2523 }
2524  
2525 ScheduleTerseUpdate();
2526 }
2527  
2528 public void RemFlag(PrimFlags flag)
2529 {
2530 // PrimFlags prevflag = Flags;
2531 if ((Flags & flag) != 0)
2532 {
2533 //m_log.Debug("Removing flag: " + ((PrimFlags)flag).ToString());
2534 Flags &= ~flag;
2535 }
2536 //m_log.Debug("prev: " + prevflag.ToString() + " curr: " + Flags.ToString());
2537 //ScheduleFullUpdate();
2538 }
2539  
2540 public void RemoveScriptEvents(UUID scriptid)
2541 {
2542 lock (m_scriptEvents)
2543 {
2544 if (m_scriptEvents.ContainsKey(scriptid))
2545 {
2546 scriptEvents oldparts = scriptEvents.None;
2547 oldparts = (scriptEvents) m_scriptEvents[scriptid];
2548  
2549 // remove values from aggregated script events
2550 AggregateScriptEvents &= ~oldparts;
2551 m_scriptEvents.Remove(scriptid);
2552 aggregateScriptEvents();
2553 }
2554 }
2555 }
2556  
2557 /// <summary>
2558 /// Reset UUIDs for this part. This involves generate this part's own UUID and
2559 /// generating new UUIDs for all the items in the inventory.
2560 /// </summary>
2561 /// <param name="linkNum">Link number for the part</param>
2562 public void ResetIDs(int linkNum)
2563 {
2564 UUID = UUID.Random();
2565 LinkNum = linkNum;
2566 LocalId = 0;
2567 Inventory.ResetInventoryIDs();
2568 }
2569  
2570 /// <summary>
2571 /// Set the scale of this part.
2572 /// </summary>
2573 /// <remarks>
2574 /// Unlike the scale property, this checks the new size against scene limits and schedules a full property
2575 /// update to viewers.
2576 /// </remarks>
2577 /// <param name="scale"></param>
2578 public void Resize(Vector3 scale)
2579 {
2580 PhysicsActor pa = PhysActor;
2581  
2582 if (ParentGroup.Scene != null)
2583 {
2584 scale.X = Math.Max(ParentGroup.Scene.m_minNonphys, Math.Min(ParentGroup.Scene.m_maxNonphys, scale.X));
2585 scale.Y = Math.Max(ParentGroup.Scene.m_minNonphys, Math.Min(ParentGroup.Scene.m_maxNonphys, scale.Y));
2586 scale.Z = Math.Max(ParentGroup.Scene.m_minNonphys, Math.Min(ParentGroup.Scene.m_maxNonphys, scale.Z));
2587  
2588 if (pa != null && pa.IsPhysical)
2589 {
2590 scale.X = Math.Max(ParentGroup.Scene.m_minPhys, Math.Min(ParentGroup.Scene.m_maxPhys, scale.X));
2591 scale.Y = Math.Max(ParentGroup.Scene.m_minPhys, Math.Min(ParentGroup.Scene.m_maxPhys, scale.Y));
2592 scale.Z = Math.Max(ParentGroup.Scene.m_minPhys, Math.Min(ParentGroup.Scene.m_maxPhys, scale.Z));
2593 }
2594 }
2595  
2596 // m_log.DebugFormat("[SCENE OBJECT PART]: Resizing {0} {1} to {2}", Name, LocalId, scale);
2597  
2598 Scale = scale;
2599  
2600 ParentGroup.HasGroupChanged = true;
2601 ScheduleFullUpdate();
2602 }
2603  
2604 public void RotLookAt(Quaternion target, float strength, float damping)
2605 {
2606 if (ParentGroup.IsAttachment)
2607 {
2608 /*
2609 ScenePresence avatar = m_scene.GetScenePresence(rootpart.AttachedAvatar);
2610 if (avatar != null)
2611 {
2612 Rotate the Av?
2613 } */
2614 }
2615 else
2616 {
2617 APIDDamp = damping;
2618 APIDStrength = strength;
2619 APIDTarget = target;
2620  
2621 if (APIDStrength <= 0)
2622 {
2623 m_log.WarnFormat("[SceneObjectPart] Invalid rotation strength {0}",APIDStrength);
2624 return;
2625 }
2626  
2627 m_APIDIterations = 1 + (int)(Math.PI * APIDStrength);
2628 }
2629  
2630 // Necessary to get the lookat deltas applied
2631 ParentGroup.QueueForUpdateCheck();
2632 }
2633  
2634 public void StartLookAt(Quaternion target, float strength, float damping)
2635 {
2636 RotLookAt(target,strength,damping);
2637 }
2638  
2639 public void StopLookAt()
2640 {
2641 APIDTarget = Quaternion.Identity;
2642 }
2643  
2644  
2645  
2646 public void ScheduleFullUpdateIfNone()
2647 {
2648 if (ParentGroup == null)
2649 return;
2650  
2651 // ??? ParentGroup.HasGroupChanged = true;
2652  
2653 if (UpdateFlag != UpdateRequired.FULL)
2654 ScheduleFullUpdate();
2655 }
2656  
2657 /// <summary>
2658 /// Schedules this prim for a full update
2659 /// </summary>
2660 public void ScheduleFullUpdate()
2661 {
2662 // m_log.DebugFormat("[SCENE OBJECT PART]: Scheduling full update for {0} {1}", Name, LocalId);
2663  
2664 if (ParentGroup == null)
2665 return;
2666  
2667 ParentGroup.QueueForUpdateCheck();
2668  
2669 int timeNow = Util.UnixTimeSinceEpoch();
2670  
2671 // If multiple updates are scheduled on the same second, we still need to perform all of them
2672 // So we'll force the issue by bumping up the timestamp so that later processing sees these need
2673 // to be performed.
2674 if (timeNow <= TimeStampFull)
2675 {
2676 TimeStampFull += 1;
2677 }
2678 else
2679 {
2680 TimeStampFull = (uint)timeNow;
2681 }
2682  
2683 UpdateFlag = UpdateRequired.FULL;
2684  
2685 // m_log.DebugFormat(
2686 // "[SCENE OBJECT PART]: Scheduling full update for {0}, {1} at {2}",
2687 // UUID, Name, TimeStampFull);
2688  
2689 if (ParentGroup.Scene != null)
2690 ParentGroup.Scene.EventManager.TriggerSceneObjectPartUpdated(this, true);
2691 }
2692  
2693 /// <summary>
2694 /// Schedule a terse update for this prim. Terse updates only send position,
2695 /// rotation, velocity and rotational velocity information.
2696 /// </summary>
2697 public void ScheduleTerseUpdate()
2698 {
2699 if (ParentGroup == null)
2700 return;
2701  
2702 // This was pulled from SceneViewer. Attachments always receive full updates.
2703 // I could not verify if this is a requirement but this maintains existing behavior
2704 if (ParentGroup.IsAttachment)
2705 {
2706 ScheduleFullUpdate();
2707 return;
2708 }
2709  
2710 if (UpdateFlag == UpdateRequired.NONE)
2711 {
2712 ParentGroup.HasGroupChanged = true;
2713 ParentGroup.QueueForUpdateCheck();
2714  
2715 TimeStampTerse = (uint) Util.UnixTimeSinceEpoch();
2716 UpdateFlag = UpdateRequired.TERSE;
2717  
2718 // m_log.DebugFormat(
2719 // "[SCENE OBJECT PART]: Scheduling terse update for {0}, {1} at {2}",
2720 // UUID, Name, TimeStampTerse);
2721 }
2722  
2723 if (ParentGroup.Scene != null)
2724 ParentGroup.Scene.EventManager.TriggerSceneObjectPartUpdated(this, false);
2725 }
2726  
2727 public void ScriptSetPhysicsStatus(bool UsePhysics)
2728 {
2729 ParentGroup.ScriptSetPhysicsStatus(UsePhysics);
2730 }
2731  
2732 /// <summary>
2733 /// Set sculpt and mesh data, and tell the physics engine to process the change.
2734 /// </summary>
2735 /// <param name="texture">The mesh itself.</param>
2736 /*
2737 public void SculptTextureCallback(AssetBase texture)
2738 {
2739 if (m_shape.SculptEntry)
2740 {
2741 // commented out for sculpt map caching test - null could mean a cached sculpt map has been found
2742 //if (texture != null)
2743 {
2744 if (texture != null)
2745 {
2746 // m_log.DebugFormat(
2747 // "[SCENE OBJECT PART]: Setting sculpt data for {0} on SculptTextureCallback()", Name);
2748  
2749 m_shape.SculptData = texture.Data;
2750 }
2751  
2752 PhysicsActor pa = PhysActor;
2753  
2754 if (pa != null)
2755 {
2756 // Update the physics actor with the new loaded sculpt data and set the taint signal.
2757 pa.Shape = m_shape;
2758  
2759 ParentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(pa);
2760 }
2761 }
2762 }
2763 }
2764 */
2765 /// <summary>
2766 /// Send a full update to the client for the given part
2767 /// </summary>
2768 /// <param name="remoteClient"></param>
2769 protected internal void SendFullUpdate(IClientAPI remoteClient)
2770 {
2771 if (ParentGroup == null)
2772 return;
2773  
2774 // m_log.DebugFormat(
2775 // "[SOG]: Sendinging part full update to {0} for {1} {2}", remoteClient.Name, part.Name, part.LocalId);
2776  
2777 if (IsRoot)
2778 {
2779 if (ParentGroup.IsAttachment)
2780 {
2781 SendFullUpdateToClient(remoteClient, AttachedPos);
2782 }
2783 else
2784 {
2785 SendFullUpdateToClient(remoteClient, AbsolutePosition);
2786 }
2787 }
2788 else
2789 {
2790 SendFullUpdateToClient(remoteClient);
2791 }
2792 }
2793  
2794 /// <summary>
2795 /// Send a full update for this part to all clients.
2796 /// </summary>
2797 public void SendFullUpdateToAllClients()
2798 {
2799 if (ParentGroup == null)
2800 return;
2801  
2802 ParentGroup.Scene.ForEachScenePresence(delegate(ScenePresence avatar)
2803 {
2804 SendFullUpdate(avatar.ControllingClient);
2805 });
2806 }
2807  
2808 /// <summary>
2809 /// Sends a full update to the client
2810 /// </summary>
2811 /// <param name="remoteClient"></param>
2812 public void SendFullUpdateToClient(IClientAPI remoteClient)
2813 {
2814 SendFullUpdateToClient(remoteClient, OffsetPosition);
2815 }
2816  
2817 /// <summary>
2818 /// Sends a full update to the client
2819 /// </summary>
2820 /// <param name="remoteClient"></param>
2821 /// <param name="lPos"></param>
2822 public void SendFullUpdateToClient(IClientAPI remoteClient, Vector3 lPos)
2823 {
2824 if (ParentGroup == null)
2825 return;
2826  
2827 // Suppress full updates during attachment editing
2828 //
2829 if (ParentGroup.IsSelected && ParentGroup.IsAttachment)
2830 return;
2831  
2832 if (ParentGroup.IsDeleted)
2833 return;
2834  
2835 if (ParentGroup.IsAttachment
2836 && ParentGroup.AttachedAvatar != remoteClient.AgentId
2837 && ParentGroup.HasPrivateAttachmentPoint)
2838 return;
2839  
2840 if (remoteClient.AgentId == OwnerID)
2841 {
2842 if ((Flags & PrimFlags.CreateSelected) != 0)
2843 Flags &= ~PrimFlags.CreateSelected;
2844 }
2845 //bool isattachment = IsAttachment;
2846 //if (LocalId != ParentGroup.RootPart.LocalId)
2847 //isattachment = ParentGroup.RootPart.IsAttachment;
2848  
2849 remoteClient.SendEntityUpdate(this, PrimUpdateFlags.FullUpdate);
2850 ParentGroup.Scene.StatsReporter.AddObjectUpdates(1);
2851 }
2852  
2853 /// <summary>
2854 /// Tell all the prims which have had updates scheduled
2855 /// </summary>
2856 public void SendScheduledUpdates()
2857 {
2858 const float ROTATION_TOLERANCE = 0.01f;
2859 const float VELOCITY_TOLERANCE = 0.001f;
2860 const float POSITION_TOLERANCE = 0.05f;
2861 const int TIME_MS_TOLERANCE = 3000;
2862  
2863 switch (UpdateFlag)
2864 {
2865 case UpdateRequired.TERSE:
2866 {
2867 ClearUpdateSchedule();
2868 // Throw away duplicate or insignificant updates
2869 if (!RotationOffset.ApproxEquals(m_lastRotation, ROTATION_TOLERANCE) ||
2870 !Acceleration.Equals(m_lastAcceleration) ||
2871 !Velocity.ApproxEquals(m_lastVelocity, VELOCITY_TOLERANCE) ||
2872 Velocity.ApproxEquals(Vector3.Zero, VELOCITY_TOLERANCE) ||
2873 !AngularVelocity.ApproxEquals(m_lastAngularVelocity, VELOCITY_TOLERANCE) ||
2874 !OffsetPosition.ApproxEquals(m_lastPosition, POSITION_TOLERANCE) ||
2875 Environment.TickCount - m_lastTerseSent > TIME_MS_TOLERANCE)
2876 {
2877 SendTerseUpdateToAllClients();
2878  
2879 // Update the "last" values
2880 m_lastPosition = OffsetPosition;
2881 m_lastRotation = RotationOffset;
2882 m_lastVelocity = Velocity;
2883 m_lastAcceleration = Acceleration;
2884 m_lastAngularVelocity = AngularVelocity;
2885 m_lastTerseSent = Environment.TickCount;
2886 }
2887 break;
2888 }
2889 case UpdateRequired.FULL:
2890 {
2891 ClearUpdateSchedule();
2892 SendFullUpdateToAllClients();
2893 break;
2894 }
2895 }
2896 }
2897  
2898 /// <summary>
2899 /// Send a terse update to all clients
2900 /// </summary>
2901 public void SendTerseUpdateToAllClients()
2902 {
2903 ParentGroup.Scene.ForEachClient(delegate(IClientAPI client)
2904 {
2905 SendTerseUpdateToClient(client);
2906 });
2907 }
2908  
2909 public void SetAxisRotation(int axis, int rotate)
2910 {
2911 ParentGroup.SetAxisRotation(axis, rotate);
2912  
2913 //Cannot use ScriptBaseClass constants as no referance to it currently.
2914 if ((axis & (int)SceneObjectGroup.axisSelect.STATUS_ROTATE_X) != 0)
2915 STATUS_ROTATE_X = rotate;
2916  
2917 if ((axis & (int)SceneObjectGroup.axisSelect.STATUS_ROTATE_Y) != 0)
2918 STATUS_ROTATE_Y = rotate;
2919  
2920 if ((axis & (int)SceneObjectGroup.axisSelect.STATUS_ROTATE_Z) != 0)
2921 STATUS_ROTATE_Z = rotate;
2922 }
2923  
2924 public void SetBuoyancy(float fvalue)
2925 {
2926 PhysicsActor pa = PhysActor;
2927  
2928 if (pa != null)
2929 pa.Buoyancy = fvalue;
2930 }
2931  
2932 public void SetDieAtEdge(bool p)
2933 {
2934 if (ParentGroup.IsDeleted)
2935 return;
2936  
2937 ParentGroup.RootPart.DIE_AT_EDGE = p;
2938 }
2939  
2940 public void SetFloatOnWater(int floatYN)
2941 {
2942 PhysicsActor pa = PhysActor;
2943  
2944 if (pa != null)
2945 pa.FloatOnWater = floatYN == 1;
2946 }
2947  
2948 public void SetForce(Vector3 force)
2949 {
2950 PhysicsActor pa = PhysActor;
2951  
2952 if (pa != null)
2953 pa.Force = force;
2954 }
2955  
2956 public void SetVehicleType(int type)
2957 {
2958 PhysicsActor pa = PhysActor;
2959  
2960 if (pa != null)
2961 pa.VehicleType = type;
2962 }
2963  
2964 public void SetVehicleFloatParam(int param, float value)
2965 {
2966 PhysicsActor pa = PhysActor;
2967  
2968 if (pa != null)
2969 pa.VehicleFloatParam(param, value);
2970 }
2971  
2972 public void SetVehicleVectorParam(int param, Vector3 value)
2973 {
2974 PhysicsActor pa = PhysActor;
2975  
2976 if (pa != null)
2977 pa.VehicleVectorParam(param, value);
2978 }
2979  
2980 public void SetVehicleRotationParam(int param, Quaternion rotation)
2981 {
2982 PhysicsActor pa = PhysActor;
2983  
2984 if (pa != null)
2985 pa.VehicleRotationParam(param, rotation);
2986 }
2987  
2988 /// <summary>
2989 /// Set the color & alpha of prim faces
2990 /// </summary>
2991 /// <param name="face"></param>
2992 /// <param name="color"></param>
2993 /// <param name="alpha"></param>
2994 public void SetFaceColorAlpha(int face, Vector3 color, double ?alpha)
2995 {
2996 Vector3 clippedColor = Util.Clip(color, 0.0f, 1.0f);
2997 float clippedAlpha = alpha.HasValue ?
2998 Util.Clip((float)alpha.Value, 0.0f, 1.0f) : 0;
2999  
3000 // The only way to get a deep copy/ If we don't do this, we can
3001 // never detect color changes further down.
3002 Byte[] buf = Shape.Textures.GetBytes();
3003 Primitive.TextureEntry tex = new Primitive.TextureEntry(buf, 0, buf.Length);
3004 Color4 texcolor;
3005 if (face >= 0 && face < GetNumberOfSides())
3006 {
3007 texcolor = tex.CreateFace((uint)face).RGBA;
3008 texcolor.R = clippedColor.X;
3009 texcolor.G = clippedColor.Y;
3010 texcolor.B = clippedColor.Z;
3011 if (alpha.HasValue)
3012 {
3013 texcolor.A = clippedAlpha;
3014 }
3015 tex.FaceTextures[face].RGBA = texcolor;
3016 UpdateTextureEntry(tex.GetBytes());
3017 return;
3018 }
3019 else if (face == ALL_SIDES)
3020 {
3021 for (uint i = 0; i < GetNumberOfSides(); i++)
3022 {
3023 if (tex.FaceTextures[i] != null)
3024 {
3025 texcolor = tex.FaceTextures[i].RGBA;
3026 texcolor.R = clippedColor.X;
3027 texcolor.G = clippedColor.Y;
3028 texcolor.B = clippedColor.Z;
3029 if (alpha.HasValue)
3030 {
3031 texcolor.A = clippedAlpha;
3032 }
3033 tex.FaceTextures[i].RGBA = texcolor;
3034 }
3035 texcolor = tex.DefaultTexture.RGBA;
3036 texcolor.R = clippedColor.X;
3037 texcolor.G = clippedColor.Y;
3038 texcolor.B = clippedColor.Z;
3039 if (alpha.HasValue)
3040 {
3041 texcolor.A = clippedAlpha;
3042 }
3043 tex.DefaultTexture.RGBA = texcolor;
3044 }
3045 UpdateTextureEntry(tex.GetBytes());
3046 return;
3047 }
3048 }
3049  
3050 /// <summary>
3051 /// Get the number of sides that this part has.
3052 /// </summary>
3053 /// <returns></returns>
3054 public int GetNumberOfSides()
3055 {
3056 int ret = 0;
3057 bool hasCut;
3058 bool hasHollow;
3059 bool hasDimple;
3060 bool hasProfileCut;
3061  
3062 PrimType primType = GetPrimType();
3063 HasCutHollowDimpleProfileCut(primType, Shape, out hasCut, out hasHollow, out hasDimple, out hasProfileCut);
3064  
3065 switch (primType)
3066 {
3067 case PrimType.BOX:
3068 ret = 6;
3069 if (hasCut) ret += 2;
3070 if (hasHollow) ret += 1;
3071 break;
3072 case PrimType.CYLINDER:
3073 ret = 3;
3074 if (hasCut) ret += 2;
3075 if (hasHollow) ret += 1;
3076 break;
3077 case PrimType.PRISM:
3078 ret = 5;
3079 if (hasCut) ret += 2;
3080 if (hasHollow) ret += 1;
3081 break;
3082 case PrimType.SPHERE:
3083 ret = 1;
3084 if (hasCut) ret += 2;
3085 if (hasDimple) ret += 2;
3086 if (hasHollow) ret += 1;
3087 break;
3088 case PrimType.TORUS:
3089 ret = 1;
3090 if (hasCut) ret += 2;
3091 if (hasProfileCut) ret += 2;
3092 if (hasHollow) ret += 1;
3093 break;
3094 case PrimType.TUBE:
3095 ret = 4;
3096 if (hasCut) ret += 2;
3097 if (hasProfileCut) ret += 2;
3098 if (hasHollow) ret += 1;
3099 break;
3100 case PrimType.RING:
3101 ret = 3;
3102 if (hasCut) ret += 2;
3103 if (hasProfileCut) ret += 2;
3104 if (hasHollow) ret += 1;
3105 break;
3106 case PrimType.SCULPT:
3107 // Special mesh handling
3108 if (Shape.SculptType == (byte)SculptType.Mesh)
3109 ret = 8; // if it's a mesh then max 8 faces
3110 else
3111 ret = 1; // if it's a sculpt then max 1 face
3112 break;
3113 }
3114  
3115 return ret;
3116 }
3117  
3118 /// <summary>
3119 /// Tell us what type this prim is
3120 /// </summary>
3121 /// <param name="primShape"></param>
3122 /// <returns></returns>
3123 public PrimType GetPrimType()
3124 {
3125 if (Shape.SculptEntry)
3126 return PrimType.SCULPT;
3127  
3128 if ((Shape.ProfileCurve & 0x07) == (byte)ProfileShape.Square)
3129 {
3130 if (Shape.PathCurve == (byte)Extrusion.Straight)
3131 return PrimType.BOX;
3132 else if (Shape.PathCurve == (byte)Extrusion.Curve1)
3133 return PrimType.TUBE;
3134 }
3135 else if ((Shape.ProfileCurve & 0x07) == (byte)ProfileShape.Circle)
3136 {
3137 if (Shape.PathCurve == (byte)Extrusion.Straight)
3138 return PrimType.CYLINDER;
3139 // ProfileCurve seems to combine hole shape and profile curve so we need to only compare against the lower 3 bits
3140 else if (Shape.PathCurve == (byte)Extrusion.Curve1)
3141 return PrimType.TORUS;
3142 }
3143 else if ((Shape.ProfileCurve & 0x07) == (byte)ProfileShape.HalfCircle)
3144 {
3145 if (Shape.PathCurve == (byte)Extrusion.Curve1 || Shape.PathCurve == (byte)Extrusion.Curve2)
3146 return PrimType.SPHERE;
3147 }
3148 else if ((Shape.ProfileCurve & 0x07) == (byte)ProfileShape.EquilateralTriangle)
3149 {
3150 if (Shape.PathCurve == (byte)Extrusion.Straight)
3151 return PrimType.PRISM;
3152 else if (Shape.PathCurve == (byte)Extrusion.Curve1)
3153 return PrimType.RING;
3154 }
3155  
3156 return PrimType.BOX;
3157 }
3158  
3159 /// <summary>
3160 /// Tell us if this object has cut, hollow, dimple, and other factors affecting the number of faces
3161 /// </summary>
3162 /// <param name="primType"></param>
3163 /// <param name="shape"></param>
3164 /// <param name="hasCut"></param>
3165 /// <param name="hasHollow"></param>
3166 /// <param name="hasDimple"></param>
3167 /// <param name="hasProfileCut"></param>
3168 protected static void HasCutHollowDimpleProfileCut(PrimType primType, PrimitiveBaseShape shape, out bool hasCut, out bool hasHollow,
3169 out bool hasDimple, out bool hasProfileCut)
3170 {
3171 if (primType == PrimType.BOX
3172 ||
3173 primType == PrimType.CYLINDER
3174 ||
3175 primType == PrimType.PRISM)
3176  
3177 hasCut = (shape.ProfileBegin > 0) || (shape.ProfileEnd > 0);
3178 else
3179 hasCut = (shape.PathBegin > 0) || (shape.PathEnd > 0);
3180  
3181 hasHollow = shape.ProfileHollow > 0;
3182 hasDimple = (shape.ProfileBegin > 0) || (shape.ProfileEnd > 0); // taken from llSetPrimitiveParms
3183 hasProfileCut = hasDimple; // is it the same thing?
3184 }
3185  
3186 public void SetVehicleFlags(int param, bool remove)
3187 {
3188 PhysicsActor pa = PhysActor;
3189  
3190 if (pa != null)
3191 pa.VehicleFlags(param, remove);
3192 }
3193  
3194 public void SetGroup(UUID groupID, IClientAPI client)
3195 {
3196 // Scene.AddNewPrims() calls with client == null so can't use this.
3197 // m_log.DebugFormat(
3198 // "[SCENE OBJECT PART]: Setting group for {0} to {1} for {2}",
3199 // Name, groupID, OwnerID);
3200  
3201 GroupID = groupID;
3202 if (client != null)
3203 SendPropertiesToClient(client);
3204 UpdateFlag = UpdateRequired.FULL;
3205 }
3206  
3207 /// <summary>
3208 /// Set the parent group of this prim.
3209 /// </summary>
3210 public void SetParent(SceneObjectGroup parent)
3211 {
3212 ParentGroup = parent;
3213 }
3214  
3215 // Use this for attachments! LocalID should be avatar's localid
3216 public void SetParentLocalId(uint localID)
3217 {
3218 ParentID = localID;
3219 }
3220  
3221 public void SetPhysicsAxisRotation()
3222 {
3223 PhysicsActor pa = PhysActor;
3224  
3225 if (pa != null)
3226 {
3227 pa.LockAngularMotion(RotationAxis);
3228 ParentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(pa);
3229 }
3230 }
3231  
3232 /// <summary>
3233 /// Set the events that this part will pass on to listeners.
3234 /// </summary>
3235 /// <param name="scriptid"></param>
3236 /// <param name="events"></param>
3237 public void SetScriptEvents(UUID scriptid, int events)
3238 {
3239 // m_log.DebugFormat(
3240 // "[SCENE OBJECT PART]: Set script events for script with id {0} on {1}/{2} to {3} in {4}",
3241 // scriptid, Name, ParentGroup.Name, events, ParentGroup.Scene.Name);
3242  
3243 // scriptEvents oldparts;
3244 lock (m_scriptEvents)
3245 {
3246 if (m_scriptEvents.ContainsKey(scriptid))
3247 {
3248 // oldparts = m_scriptEvents[scriptid];
3249  
3250 // remove values from aggregated script events
3251 if (m_scriptEvents[scriptid] == (scriptEvents) events)
3252 return;
3253 m_scriptEvents[scriptid] = (scriptEvents) events;
3254 }
3255 else
3256 {
3257 m_scriptEvents.Add(scriptid, (scriptEvents) events);
3258 }
3259 }
3260 aggregateScriptEvents();
3261 }
3262  
3263 /// <summary>
3264 /// Set the text displayed for this part.
3265 /// </summary>
3266 /// <param name="text"></param>
3267 public void SetText(string text)
3268 {
3269 Text = text;
3270  
3271 if (ParentGroup != null)
3272 {
3273 ParentGroup.HasGroupChanged = true;
3274 ScheduleFullUpdate();
3275 }
3276 }
3277  
3278 /// <summary>
3279 /// Set the text displayed for this part.
3280 /// </summary>
3281 /// <param name="text"></param>
3282 /// <param name="color"></param>
3283 /// <param name="alpha"></param>
3284 public void SetText(string text, Vector3 color, double alpha)
3285 {
3286 Color = Color.FromArgb((int) (alpha*0xff),
3287 (int) (color.X*0xff),
3288 (int) (color.Y*0xff),
3289 (int) (color.Z*0xff));
3290 SetText(text);
3291 }
3292  
3293 public void StopMoveToTarget()
3294 {
3295 ParentGroup.stopMoveToTarget();
3296  
3297 ParentGroup.ScheduleGroupForTerseUpdate();
3298 //ParentGroup.ScheduleGroupForFullUpdate();
3299 }
3300  
3301 public void StoreUndoState()
3302 {
3303 StoreUndoState(false);
3304 }
3305  
3306 public void StoreUndoState(bool forGroup)
3307 {
3308 if (ParentGroup == null || ParentGroup.Scene == null)
3309 return;
3310  
3311 if (Undoing)
3312 {
3313 // m_log.DebugFormat(
3314 // "[SCENE OBJECT PART]: Ignoring undo store for {0} {1} since already undoing", Name, LocalId);
3315 return;
3316 }
3317  
3318 if (IgnoreUndoUpdate)
3319 {
3320 // m_log.DebugFormat("[SCENE OBJECT PART]: Ignoring undo store for {0} {1}", Name, LocalId);
3321 return;
3322 }
3323  
3324 lock (m_undo)
3325 {
3326 if (m_undo.Count > 0)
3327 {
3328 UndoState last = m_undo[m_undo.Count - 1];
3329 if (last != null)
3330 {
3331 // TODO: May need to fix for group comparison
3332 if (last.Compare(this))
3333 {
3334 // m_log.DebugFormat(
3335 // "[SCENE OBJECT PART]: Not storing undo for {0} {1} since current state is same as last undo state, initial stack size {2}",
3336 // Name, LocalId, m_undo.Count);
3337  
3338 return;
3339 }
3340 }
3341 }
3342  
3343 // m_log.DebugFormat(
3344 // "[SCENE OBJECT PART]: Storing undo state for {0} {1}, forGroup {2}, initial stack size {3}",
3345 // Name, LocalId, forGroup, m_undo.Count);
3346  
3347 if (ParentGroup.Scene.MaxUndoCount > 0)
3348 {
3349 UndoState nUndo = new UndoState(this, forGroup);
3350  
3351 m_undo.Add(nUndo);
3352  
3353 if (m_undo.Count > ParentGroup.Scene.MaxUndoCount)
3354 m_undo.RemoveAt(0);
3355  
3356 if (m_redo.Count > 0)
3357 m_redo.Clear();
3358  
3359 // m_log.DebugFormat(
3360 // "[SCENE OBJECT PART]: Stored undo state for {0} {1}, forGroup {2}, stack size now {3}",
3361 // Name, LocalId, forGroup, m_undo.Count);
3362 }
3363 }
3364 }
3365  
3366 /// <summary>
3367 /// Return number of undos on the stack. Here temporarily pending a refactor.
3368 /// </summary>
3369 public int UndoCount
3370 {
3371 get
3372 {
3373 lock (m_undo)
3374 return m_undo.Count;
3375 }
3376 }
3377  
3378 public void Undo()
3379 {
3380 lock (m_undo)
3381 {
3382 // m_log.DebugFormat(
3383 // "[SCENE OBJECT PART]: Handling undo request for {0} {1}, stack size {2}",
3384 // Name, LocalId, m_undo.Count);
3385  
3386 if (m_undo.Count > 0)
3387 {
3388 UndoState goback = m_undo[m_undo.Count - 1];
3389 m_undo.RemoveAt(m_undo.Count - 1);
3390  
3391 UndoState nUndo = null;
3392  
3393 if (ParentGroup.Scene.MaxUndoCount > 0)
3394 {
3395 nUndo = new UndoState(this, goback.ForGroup);
3396 }
3397  
3398 goback.PlaybackState(this);
3399  
3400 if (nUndo != null)
3401 {
3402 m_redo.Add(nUndo);
3403  
3404 if (m_redo.Count > ParentGroup.Scene.MaxUndoCount)
3405 m_redo.RemoveAt(0);
3406 }
3407 }
3408  
3409 // m_log.DebugFormat(
3410 // "[SCENE OBJECT PART]: Handled undo request for {0} {1}, stack size now {2}",
3411 // Name, LocalId, m_undo.Count);
3412 }
3413 }
3414  
3415 public void Redo()
3416 {
3417 lock (m_undo)
3418 {
3419 // m_log.DebugFormat(
3420 // "[SCENE OBJECT PART]: Handling redo request for {0} {1}, stack size {2}",
3421 // Name, LocalId, m_redo.Count);
3422  
3423 if (m_redo.Count > 0)
3424 {
3425 UndoState gofwd = m_redo[m_redo.Count - 1];
3426 m_redo.RemoveAt(m_redo.Count - 1);
3427  
3428 if (ParentGroup.Scene.MaxUndoCount > 0)
3429 {
3430 UndoState nUndo = new UndoState(this, gofwd.ForGroup);
3431  
3432 m_undo.Add(nUndo);
3433  
3434 if (m_undo.Count > ParentGroup.Scene.MaxUndoCount)
3435 m_undo.RemoveAt(0);
3436 }
3437  
3438 gofwd.PlayfwdState(this);
3439  
3440 // m_log.DebugFormat(
3441 // "[SCENE OBJECT PART]: Handled redo request for {0} {1}, stack size now {2}",
3442 // Name, LocalId, m_redo.Count);
3443 }
3444 }
3445 }
3446  
3447 public void ClearUndoState()
3448 {
3449 // m_log.DebugFormat("[SCENE OBJECT PART]: Clearing undo and redo stacks in {0} {1}", Name, LocalId);
3450  
3451 lock (m_undo)
3452 {
3453 m_undo.Clear();
3454 m_redo.Clear();
3455 }
3456 }
3457  
3458 public EntityIntersection TestIntersection(Ray iray, Quaternion parentrot)
3459 {
3460 // In this case we're using a sphere with a radius of the largest dimension of the prim
3461 // TODO: Change to take shape into account
3462  
3463 EntityIntersection result = new EntityIntersection();
3464 Vector3 vAbsolutePosition = AbsolutePosition;
3465 Vector3 vScale = Scale;
3466 Vector3 rOrigin = iray.Origin;
3467 Vector3 rDirection = iray.Direction;
3468  
3469 //rDirection = rDirection.Normalize();
3470 // Buidling the first part of the Quadratic equation
3471 Vector3 r2ndDirection = rDirection*rDirection;
3472 float itestPart1 = r2ndDirection.X + r2ndDirection.Y + r2ndDirection.Z;
3473  
3474 // Buidling the second part of the Quadratic equation
3475 Vector3 tmVal2 = rOrigin - vAbsolutePosition;
3476 Vector3 r2Direction = rDirection*2.0f;
3477 Vector3 tmVal3 = r2Direction*tmVal2;
3478  
3479 float itestPart2 = tmVal3.X + tmVal3.Y + tmVal3.Z;
3480  
3481 // Buidling the third part of the Quadratic equation
3482 Vector3 tmVal4 = rOrigin*rOrigin;
3483 Vector3 tmVal5 = vAbsolutePosition*vAbsolutePosition;
3484  
3485 Vector3 tmVal6 = vAbsolutePosition*rOrigin;
3486  
3487 // Set Radius to the largest dimension of the prim
3488 float radius = 0f;
3489 if (vScale.X > radius)
3490 radius = vScale.X;
3491 if (vScale.Y > radius)
3492 radius = vScale.Y;
3493 if (vScale.Z > radius)
3494 radius = vScale.Z;
3495  
3496 // the second part of this is the default prim size
3497 // once we factor in the aabb of the prim we're adding we can
3498 // change this to;
3499 // radius = (radius / 2) - 0.01f;
3500 //
3501 radius = (radius / 2) + (0.5f / 2) - 0.1f;
3502  
3503 //radius = radius;
3504  
3505 float itestPart3 = tmVal4.X + tmVal4.Y + tmVal4.Z + tmVal5.X + tmVal5.Y + tmVal5.Z -
3506 (2.0f*(tmVal6.X + tmVal6.Y + tmVal6.Z + (radius*radius)));
3507  
3508 // Yuk Quadradrics.. Solve first
3509 float rootsqr = (itestPart2*itestPart2) - (4.0f*itestPart1*itestPart3);
3510 if (rootsqr < 0.0f)
3511 {
3512 // No intersection
3513 return result;
3514 }
3515 float root = ((-itestPart2) - (float) Math.Sqrt((double) rootsqr))/(itestPart1*2.0f);
3516  
3517 if (root < 0.0f)
3518 {
3519 // perform second quadratic root solution
3520 root = ((-itestPart2) + (float) Math.Sqrt((double) rootsqr))/(itestPart1*2.0f);
3521  
3522 // is there any intersection?
3523 if (root < 0.0f)
3524 {
3525 // nope, no intersection
3526 return result;
3527 }
3528 }
3529  
3530 // We got an intersection. putting together an EntityIntersection object with the
3531 // intersection information
3532 Vector3 ipoint =
3533 new Vector3(iray.Origin.X + (iray.Direction.X*root), iray.Origin.Y + (iray.Direction.Y*root),
3534 iray.Origin.Z + (iray.Direction.Z*root));
3535  
3536 result.HitTF = true;
3537 result.ipoint = ipoint;
3538  
3539 // Normal is calculated by the difference and then normalizing the result
3540 Vector3 normalpart = ipoint - vAbsolutePosition;
3541 result.normal = normalpart / normalpart.Length();
3542  
3543 // It's funny how the Vector3 object has a Distance function, but the Axiom.Math object doesn't.
3544 // I can write a function to do it.. but I like the fact that this one is Static.
3545  
3546 Vector3 distanceConvert1 = new Vector3(iray.Origin.X, iray.Origin.Y, iray.Origin.Z);
3547 Vector3 distanceConvert2 = new Vector3(ipoint.X, ipoint.Y, ipoint.Z);
3548 float distance = (float) Util.GetDistanceTo(distanceConvert1, distanceConvert2);
3549  
3550 result.distance = distance;
3551  
3552 return result;
3553 }
3554  
3555 public EntityIntersection TestIntersectionOBB(Ray iray, Quaternion parentrot, bool frontFacesOnly, bool faceCenters)
3556 {
3557 // In this case we're using a rectangular prism, which has 6 faces and therefore 6 planes
3558 // This breaks down into the ray---> plane equation.
3559 // TODO: Change to take shape into account
3560 Vector3[] vertexes = new Vector3[8];
3561  
3562 // float[] distance = new float[6];
3563 Vector3[] FaceA = new Vector3[6]; // vertex A for Facei
3564 Vector3[] FaceB = new Vector3[6]; // vertex B for Facei
3565 Vector3[] FaceC = new Vector3[6]; // vertex C for Facei
3566 Vector3[] FaceD = new Vector3[6]; // vertex D for Facei
3567  
3568 Vector3[] normals = new Vector3[6]; // Normal for Facei
3569 Vector3[] AAfacenormals = new Vector3[6]; // Axis Aligned face normals
3570  
3571 AAfacenormals[0] = new Vector3(1, 0, 0);
3572 AAfacenormals[1] = new Vector3(0, 1, 0);
3573 AAfacenormals[2] = new Vector3(-1, 0, 0);
3574 AAfacenormals[3] = new Vector3(0, -1, 0);
3575 AAfacenormals[4] = new Vector3(0, 0, 1);
3576 AAfacenormals[5] = new Vector3(0, 0, -1);
3577  
3578 Vector3 AmBa = new Vector3(0, 0, 0); // Vertex A - Vertex B
3579 Vector3 AmBb = new Vector3(0, 0, 0); // Vertex B - Vertex C
3580 Vector3 cross = new Vector3();
3581  
3582 Vector3 pos = GetWorldPosition();
3583 Quaternion rot = GetWorldRotation();
3584  
3585 // Variables prefixed with AX are Axiom.Math copies of the LL variety.
3586  
3587 Quaternion AXrot = rot;
3588 AXrot.Normalize();
3589  
3590 Vector3 AXpos = pos;
3591  
3592 // tScale is the offset to derive the vertex based on the scale.
3593 // it's different for each vertex because we've got to rotate it
3594 // to get the world position of the vertex to produce the Oriented Bounding Box
3595  
3596 Vector3 tScale = Vector3.Zero;
3597  
3598 Vector3 AXscale = new Vector3(m_shape.Scale.X * 0.5f, m_shape.Scale.Y * 0.5f, m_shape.Scale.Z * 0.5f);
3599  
3600 //Vector3 pScale = (AXscale) - (AXrot.Inverse() * (AXscale));
3601 //Vector3 nScale = (AXscale * -1) - (AXrot.Inverse() * (AXscale * -1));
3602  
3603 // rScale is the rotated offset to find a vertex based on the scale and the world rotation.
3604 Vector3 rScale = new Vector3();
3605  
3606 // Get Vertexes for Faces Stick them into ABCD for each Face
3607 // Form: Face<vertex>[face] that corresponds to the below diagram
3608 #region ABCD Face Vertex Map Comment Diagram
3609 // A _________ B
3610 // | |
3611 // | 4 top |
3612 // |_________|
3613 // C D
3614  
3615 // A _________ B
3616 // | Back |
3617 // | 3 |
3618 // |_________|
3619 // C D
3620  
3621 // A _________ B B _________ A
3622 // | Left | | Right |
3623 // | 0 | | 2 |
3624 // |_________| |_________|
3625 // C D D C
3626  
3627 // A _________ B
3628 // | Front |
3629 // | 1 |
3630 // |_________|
3631 // C D
3632  
3633 // C _________ D
3634 // | |
3635 // | 5 bot |
3636 // |_________|
3637 // A B
3638 #endregion
3639  
3640 #region Plane Decomposition of Oriented Bounding Box
3641 tScale = new Vector3(AXscale.X, -AXscale.Y, AXscale.Z);
3642 rScale = tScale * AXrot;
3643 vertexes[0] = (new Vector3((pos.X + rScale.X), (pos.Y + rScale.Y), (pos.Z + rScale.Z)));
3644 // vertexes[0].X = pos.X + vertexes[0].X;
3645 //vertexes[0].Y = pos.Y + vertexes[0].Y;
3646 //vertexes[0].Z = pos.Z + vertexes[0].Z;
3647  
3648 FaceA[0] = vertexes[0];
3649 FaceB[3] = vertexes[0];
3650 FaceA[4] = vertexes[0];
3651  
3652 tScale = AXscale;
3653 rScale = tScale * AXrot;
3654 vertexes[1] = (new Vector3((pos.X + rScale.X), (pos.Y + rScale.Y), (pos.Z + rScale.Z)));
3655  
3656 // vertexes[1].X = pos.X + vertexes[1].X;
3657 // vertexes[1].Y = pos.Y + vertexes[1].Y;
3658 //vertexes[1].Z = pos.Z + vertexes[1].Z;
3659  
3660 FaceB[0] = vertexes[1];
3661 FaceA[1] = vertexes[1];
3662 FaceC[4] = vertexes[1];
3663  
3664 tScale = new Vector3(AXscale.X, -AXscale.Y, -AXscale.Z);
3665 rScale = tScale * AXrot;
3666  
3667 vertexes[2] = (new Vector3((pos.X + rScale.X), (pos.Y + rScale.Y), (pos.Z + rScale.Z)));
3668  
3669 //vertexes[2].X = pos.X + vertexes[2].X;
3670 //vertexes[2].Y = pos.Y + vertexes[2].Y;
3671 //vertexes[2].Z = pos.Z + vertexes[2].Z;
3672  
3673 FaceC[0] = vertexes[2];
3674 FaceD[3] = vertexes[2];
3675 FaceC[5] = vertexes[2];
3676  
3677 tScale = new Vector3(AXscale.X, AXscale.Y, -AXscale.Z);
3678 rScale = tScale * AXrot;
3679 vertexes[3] = (new Vector3((pos.X + rScale.X), (pos.Y + rScale.Y), (pos.Z + rScale.Z)));
3680  
3681 //vertexes[3].X = pos.X + vertexes[3].X;
3682 // vertexes[3].Y = pos.Y + vertexes[3].Y;
3683 // vertexes[3].Z = pos.Z + vertexes[3].Z;
3684  
3685 FaceD[0] = vertexes[3];
3686 FaceC[1] = vertexes[3];
3687 FaceA[5] = vertexes[3];
3688  
3689 tScale = new Vector3(-AXscale.X, AXscale.Y, AXscale.Z);
3690 rScale = tScale * AXrot;
3691 vertexes[4] = (new Vector3((pos.X + rScale.X), (pos.Y + rScale.Y), (pos.Z + rScale.Z)));
3692  
3693 // vertexes[4].X = pos.X + vertexes[4].X;
3694 // vertexes[4].Y = pos.Y + vertexes[4].Y;
3695 // vertexes[4].Z = pos.Z + vertexes[4].Z;
3696  
3697 FaceB[1] = vertexes[4];
3698 FaceA[2] = vertexes[4];
3699 FaceD[4] = vertexes[4];
3700  
3701 tScale = new Vector3(-AXscale.X, AXscale.Y, -AXscale.Z);
3702 rScale = tScale * AXrot;
3703 vertexes[5] = (new Vector3((pos.X + rScale.X), (pos.Y + rScale.Y), (pos.Z + rScale.Z)));
3704  
3705 // vertexes[5].X = pos.X + vertexes[5].X;
3706 // vertexes[5].Y = pos.Y + vertexes[5].Y;
3707 // vertexes[5].Z = pos.Z + vertexes[5].Z;
3708  
3709 FaceD[1] = vertexes[5];
3710 FaceC[2] = vertexes[5];
3711 FaceB[5] = vertexes[5];
3712  
3713 tScale = new Vector3(-AXscale.X, -AXscale.Y, AXscale.Z);
3714 rScale = tScale * AXrot;
3715 vertexes[6] = (new Vector3((pos.X + rScale.X), (pos.Y + rScale.Y), (pos.Z + rScale.Z)));
3716  
3717 // vertexes[6].X = pos.X + vertexes[6].X;
3718 // vertexes[6].Y = pos.Y + vertexes[6].Y;
3719 // vertexes[6].Z = pos.Z + vertexes[6].Z;
3720  
3721 FaceB[2] = vertexes[6];
3722 FaceA[3] = vertexes[6];
3723 FaceB[4] = vertexes[6];
3724  
3725 tScale = new Vector3(-AXscale.X, -AXscale.Y, -AXscale.Z);
3726 rScale = tScale * AXrot;
3727 vertexes[7] = (new Vector3((pos.X + rScale.X), (pos.Y + rScale.Y), (pos.Z + rScale.Z)));
3728  
3729 // vertexes[7].X = pos.X + vertexes[7].X;
3730 // vertexes[7].Y = pos.Y + vertexes[7].Y;
3731 // vertexes[7].Z = pos.Z + vertexes[7].Z;
3732  
3733 FaceD[2] = vertexes[7];
3734 FaceC[3] = vertexes[7];
3735 FaceD[5] = vertexes[7];
3736 #endregion
3737  
3738 // Get our plane normals
3739 for (int i = 0; i < 6; i++)
3740 {
3741 //m_log.Info("[FACECALCULATION]: FaceA[" + i + "]=" + FaceA[i] + " FaceB[" + i + "]=" + FaceB[i] + " FaceC[" + i + "]=" + FaceC[i] + " FaceD[" + i + "]=" + FaceD[i]);
3742  
3743 // Our Plane direction
3744 AmBa = FaceA[i] - FaceB[i];
3745 AmBb = FaceB[i] - FaceC[i];
3746  
3747 cross = Vector3.Cross(AmBb, AmBa);
3748  
3749 // normalize the cross product to get the normal.
3750 normals[i] = cross / cross.Length();
3751  
3752 //m_log.Info("[NORMALS]: normals[ " + i + "]" + normals[i].ToString());
3753 //distance[i] = (normals[i].X * AmBa.X + normals[i].Y * AmBa.Y + normals[i].Z * AmBa.Z) * -1;
3754 }
3755  
3756 EntityIntersection result = new EntityIntersection();
3757  
3758 result.distance = 1024;
3759 float c = 0;
3760 float a = 0;
3761 float d = 0;
3762 Vector3 q = new Vector3();
3763  
3764 #region OBB Version 2 Experiment
3765 //float fmin = 999999;
3766 //float fmax = -999999;
3767 //float s = 0;
3768  
3769 //for (int i=0;i<6;i++)
3770 //{
3771 //s = iray.Direction.Dot(normals[i]);
3772 //d = normals[i].Dot(FaceB[i]);
3773  
3774 //if (s == 0)
3775 //{
3776 //if (iray.Origin.Dot(normals[i]) > d)
3777 //{
3778 //return result;
3779 //}
3780 // else
3781 //{
3782 //continue;
3783 //}
3784 //}
3785 //a = (d - iray.Origin.Dot(normals[i])) / s;
3786 //if (iray.Direction.Dot(normals[i]) < 0)
3787 //{
3788 //if (a > fmax)
3789 //{
3790 //if (a > fmin)
3791 //{
3792 //return result;
3793 //}
3794 //fmax = a;
3795 //}
3796  
3797 //}
3798 //else
3799 //{
3800 //if (a < fmin)
3801 //{
3802 //if (a < 0 || a < fmax)
3803 //{
3804 //return result;
3805 //}
3806 //fmin = a;
3807 //}
3808 //}
3809 //}
3810 //if (fmax > 0)
3811 // a= fmax;
3812 //else
3813 // a=fmin;
3814  
3815 //q = iray.Origin + a * iray.Direction;
3816 #endregion
3817  
3818 // Loop over faces (6 of them)
3819 for (int i = 0; i < 6; i++)
3820 {
3821 AmBa = FaceA[i] - FaceB[i];
3822 AmBb = FaceB[i] - FaceC[i];
3823 d = Vector3.Dot(normals[i], FaceB[i]);
3824  
3825 //if (faceCenters)
3826 //{
3827 // c = normals[i].Dot(normals[i]);
3828 //}
3829 //else
3830 //{
3831 c = Vector3.Dot(iray.Direction, normals[i]);
3832 //}
3833 if (c == 0)
3834 continue;
3835  
3836 a = (d - Vector3.Dot(iray.Origin, normals[i])) / c;
3837  
3838 if (a < 0)
3839 continue;
3840  
3841 // If the normal is pointing outside the object
3842 if (Vector3.Dot(iray.Direction, normals[i]) < 0 || !frontFacesOnly)
3843 {
3844 //if (faceCenters)
3845 //{ //(FaceA[i] + FaceB[i] + FaceC[1] + FaceD[i]) / 4f;
3846 // q = iray.Origin + a * normals[i];
3847 //}
3848 //else
3849 //{
3850 q = iray.Origin + iray.Direction * a;
3851 //}
3852  
3853 float distance2 = (float)GetDistanceTo(q, AXpos);
3854 // Is this the closest hit to the object's origin?
3855 //if (faceCenters)
3856 //{
3857 // distance2 = (float)GetDistanceTo(q, iray.Origin);
3858 //}
3859  
3860 if (distance2 < result.distance)
3861 {
3862 result.distance = distance2;
3863 result.HitTF = true;
3864 result.ipoint = q;
3865 result.face = i;
3866 //m_log.Info("[FACE]:" + i.ToString());
3867 //m_log.Info("[POINT]: " + q.ToString());
3868 //m_log.Info("[DIST]: " + distance2.ToString());
3869 if (faceCenters)
3870 {
3871 result.normal = AAfacenormals[i] * AXrot;
3872  
3873 Vector3 scaleComponent = AAfacenormals[i];
3874 float ScaleOffset = 0.5f;
3875 if (scaleComponent.X != 0) ScaleOffset = AXscale.X;
3876 if (scaleComponent.Y != 0) ScaleOffset = AXscale.Y;
3877 if (scaleComponent.Z != 0) ScaleOffset = AXscale.Z;
3878 ScaleOffset = Math.Abs(ScaleOffset);
3879 Vector3 offset = result.normal * ScaleOffset;
3880 result.ipoint = AXpos + offset;
3881  
3882 ///pos = (intersectionpoint + offset);
3883 }
3884 else
3885 {
3886 result.normal = normals[i];
3887 }
3888 result.AAfaceNormal = AAfacenormals[i];
3889 }
3890 }
3891 }
3892 return result;
3893 }
3894  
3895 /// <summary>
3896 /// Serialize this part to xml.
3897 /// </summary>
3898 /// <param name="xmlWriter"></param>
3899 public void ToXml(XmlTextWriter xmlWriter)
3900 {
3901 SceneObjectSerializer.SOPToXml2(xmlWriter, this, new Dictionary<string, object>());
3902 }
3903  
3904 public void TriggerScriptChangedEvent(Changed val)
3905 {
3906 if (ParentGroup != null && ParentGroup.Scene != null)
3907 ParentGroup.Scene.EventManager.TriggerOnScriptChangedEvent(LocalId, (uint)val);
3908 }
3909  
3910 public void TrimPermissions()
3911 {
3912 BaseMask &= (uint)(PermissionMask.All | PermissionMask.Export);
3913 OwnerMask &= (uint)(PermissionMask.All | PermissionMask.Export);
3914 GroupMask &= (uint)PermissionMask.All;
3915 EveryoneMask &= (uint)(PermissionMask.All | PermissionMask.Export);
3916 NextOwnerMask &= (uint)PermissionMask.All;
3917 }
3918  
3919 public void UpdateExtraParam(ushort type, bool inUse, byte[] data)
3920 {
3921 m_shape.ReadInUpdateExtraParam(type, inUse, data);
3922 /*
3923 if (type == 0x30)
3924 {
3925 if (m_shape.SculptEntry && m_shape.SculptTexture != UUID.Zero)
3926 {
3927 ParentGroup.Scene.AssetService.Get(m_shape.SculptTexture.ToString(), this, AssetReceived);
3928 }
3929 }
3930 */
3931 if (ParentGroup != null)
3932 {
3933 ParentGroup.HasGroupChanged = true;
3934 ScheduleFullUpdate();
3935 }
3936 }
3937  
3938 public void UpdateGroupPosition(Vector3 newPos)
3939 {
3940 Vector3 oldPos = GroupPosition;
3941  
3942 if ((newPos.X != oldPos.X) ||
3943 (newPos.Y != oldPos.Y) ||
3944 (newPos.Z != oldPos.Z))
3945 {
3946 GroupPosition = newPos;
3947 ScheduleTerseUpdate();
3948 }
3949 }
3950  
3951 /// <summary>
3952 /// Update this part's offset position.
3953 /// </summary>
3954 /// <param name="pos"></param>
3955 public void UpdateOffSet(Vector3 newPos)
3956 {
3957 Vector3 oldPos = OffsetPosition;
3958  
3959 if ((newPos.X != oldPos.X) ||
3960 (newPos.Y != oldPos.Y) ||
3961 (newPos.Z != oldPos.Z))
3962 {
3963 if (ParentGroup.RootPart.GetStatusSandbox())
3964 {
3965 if (Util.GetDistanceTo(ParentGroup.RootPart.StatusSandboxPos, newPos) > 10)
3966 {
3967 ParentGroup.RootPart.ScriptSetPhysicsStatus(false);
3968 newPos = OffsetPosition;
3969 ParentGroup.Scene.SimChat(Utils.StringToBytes("Hit Sandbox Limit"),
3970 ChatTypeEnum.DebugChannel, 0x7FFFFFFF, ParentGroup.RootPart.AbsolutePosition, Name, UUID, false);
3971 }
3972 }
3973  
3974 OffsetPosition = newPos;
3975 ScheduleTerseUpdate();
3976 }
3977 }
3978  
3979 /// <summary>
3980 /// Update permissions on the SOP. Should only be called from SOG.UpdatePermissions because the SOG
3981 /// will handle the client notifications once all of its parts are updated.
3982 /// </summary>
3983 /// <param name="AgentID"></param>
3984 /// <param name="field"></param>
3985 /// <param name="localID"></param>
3986 /// <param name="mask"></param>
3987 /// <param name="addRemTF"></param>
3988 public void UpdatePermissions(UUID AgentID, byte field, uint localID, uint mask, byte addRemTF)
3989 {
3990 bool set = addRemTF == 1;
3991 bool god = ParentGroup.Scene.Permissions.IsGod(AgentID);
3992  
3993 uint baseMask = BaseMask;
3994 if (god)
3995 baseMask = 0x7ffffff0;
3996  
3997 // Are we the owner?
3998 if ((AgentID == OwnerID) || god)
3999 {
4000 switch (field)
4001 {
4002 case 1:
4003 if (god)
4004 {
4005 BaseMask = ApplyMask(BaseMask, set, mask);
4006 Inventory.ApplyGodPermissions(_baseMask);
4007 }
4008  
4009 break;
4010 case 2:
4011 OwnerMask = ApplyMask(OwnerMask, set, mask) &
4012 baseMask;
4013 break;
4014 case 4:
4015 GroupMask = ApplyMask(GroupMask, set, mask) &
4016 baseMask;
4017 break;
4018 case 8:
4019 // Trying to set export permissions - extra checks
4020 if (set && (mask & (uint)PermissionMask.Export) != 0)
4021 {
4022 if ((OwnerMask & (uint)PermissionMask.Export) == 0 || (BaseMask & (uint)PermissionMask.Export) == 0 || (NextOwnerMask & (uint)PermissionMask.All) != (uint)PermissionMask.All)
4023 mask &= ~(uint)PermissionMask.Export;
4024 }
4025 EveryoneMask = ApplyMask(EveryoneMask, set, mask) &
4026 baseMask;
4027 break;
4028 case 16:
4029 // Force full perm if export
4030 if ((EveryoneMask & (uint)PermissionMask.Export) != 0)
4031 {
4032 NextOwnerMask = (uint)PermissionMask.All;
4033 break;
4034 }
4035 NextOwnerMask = ApplyMask(NextOwnerMask, set, mask) &
4036 baseMask;
4037 // Prevent the client from creating no mod, no copy
4038 // objects
4039 if ((NextOwnerMask & (uint)PermissionMask.Copy) == 0)
4040 NextOwnerMask |= (uint)PermissionMask.Transfer;
4041  
4042 NextOwnerMask |= (uint)PermissionMask.Move;
4043  
4044 break;
4045 }
4046  
4047 SendFullUpdateToAllClients();
4048 }
4049 }
4050  
4051 public void ClonePermissions(SceneObjectPart source)
4052 {
4053 bool update = false;
4054  
4055 if (BaseMask != source.BaseMask ||
4056 OwnerMask != source.OwnerMask ||
4057 GroupMask != source.GroupMask ||
4058 EveryoneMask != source.EveryoneMask ||
4059 NextOwnerMask != source.NextOwnerMask)
4060 update = true;
4061  
4062 BaseMask = source.BaseMask;
4063 OwnerMask = source.OwnerMask;
4064 GroupMask = source.GroupMask;
4065 EveryoneMask = source.EveryoneMask;
4066 NextOwnerMask = source.NextOwnerMask;
4067  
4068 if (update)
4069 SendFullUpdateToAllClients();
4070 }
4071  
4072 public bool IsHingeJoint()
4073 {
4074 // For now, we use the NINJA naming scheme for identifying joints.
4075 // In the future, we can support other joint specification schemes such as a
4076 // custom checkbox in the viewer GUI.
4077 if (ParentGroup.Scene != null && ParentGroup.Scene.PhysicsScene.SupportsNINJAJoints)
4078 {
4079 string hingeString = "hingejoint";
4080 return (Name.Length >= hingeString.Length && Name.Substring(0, hingeString.Length) == hingeString);
4081 }
4082 else
4083 {
4084 return false;
4085 }
4086 }
4087  
4088 public bool IsBallJoint()
4089 {
4090 // For now, we use the NINJA naming scheme for identifying joints.
4091 // In the future, we can support other joint specification schemes such as a
4092 // custom checkbox in the viewer GUI.
4093 if (ParentGroup.Scene != null && ParentGroup.Scene.PhysicsScene.SupportsNINJAJoints)
4094 {
4095 string ballString = "balljoint";
4096 return (Name.Length >= ballString.Length && Name.Substring(0, ballString.Length) == ballString);
4097 }
4098 else
4099 {
4100 return false;
4101 }
4102 }
4103  
4104 public bool IsJoint()
4105 {
4106 // For now, we use the NINJA naming scheme for identifying joints.
4107 // In the future, we can support other joint specification schemes such as a
4108 // custom checkbox in the viewer GUI.
4109 if (ParentGroup.Scene != null && ParentGroup.Scene.PhysicsScene != null && ParentGroup.Scene.PhysicsScene.SupportsNINJAJoints)
4110 {
4111 return IsHingeJoint() || IsBallJoint();
4112 }
4113 else
4114 {
4115 return false;
4116 }
4117 }
4118  
4119 public void UpdateExtraPhysics(ExtraPhysicsData physdata)
4120 {
4121 if (physdata.PhysShapeType == PhysShapeType.invalid || ParentGroup == null)
4122 return;
4123  
4124 if (PhysicsShapeType != (byte)physdata.PhysShapeType)
4125 {
4126 PhysicsShapeType = (byte)physdata.PhysShapeType;
4127  
4128 }
4129  
4130 if(Density != physdata.Density)
4131 Density = physdata.Density;
4132 if(GravityModifier != physdata.GravitationModifier)
4133 GravityModifier = physdata.GravitationModifier;
4134 if(Friction != physdata.Friction)
4135 Friction = physdata.Friction;
4136 if(Restitution != physdata.Bounce)
4137 Restitution = physdata.Bounce;
4138 }
4139 /// <summary>
4140 /// Update the flags on this prim. This covers properties such as phantom, physics and temporary.
4141 /// </summary>
4142 /// <param name="UsePhysics"></param>
4143 /// <param name="SetTemporary"></param>
4144 /// <param name="SetPhantom"></param>
4145 /// <param name="SetVD"></param>
4146 public void UpdatePrimFlags(bool UsePhysics, bool SetTemporary, bool SetPhantom, bool SetVD)
4147 {
4148 bool wasUsingPhysics = ((Flags & PrimFlags.Physics) != 0);
4149 bool wasTemporary = ((Flags & PrimFlags.TemporaryOnRez) != 0);
4150 bool wasPhantom = ((Flags & PrimFlags.Phantom) != 0);
4151 bool wasVD = VolumeDetectActive;
4152  
4153 if ((UsePhysics == wasUsingPhysics) && (wasTemporary == SetTemporary) && (wasPhantom == SetPhantom) && (SetVD == wasVD))
4154 return;
4155  
4156 PhysicsActor pa = PhysActor;
4157  
4158 // Special cases for VD. VD can only be called from a script
4159 // and can't be combined with changes to other states. So we can rely
4160 // that...
4161 // ... if VD is changed, all others are not.
4162 // ... if one of the others is changed, VD is not.
4163 if (SetVD) // VD is active, special logic applies
4164 {
4165 // State machine logic for VolumeDetect
4166 // More logic below
4167 bool phanReset = (SetPhantom != wasPhantom) && !SetPhantom;
4168  
4169 if (phanReset) // Phantom changes from on to off switch VD off too
4170 {
4171 SetVD = false; // Switch it of for the course of this routine
4172 VolumeDetectActive = false; // and also permanently
4173  
4174 if (pa != null)
4175 pa.SetVolumeDetect(0); // Let physics know about it too
4176 }
4177 else
4178 {
4179 // If volumedetect is active we don't want phantom to be applied.
4180 // If this is a new call to VD out of the state "phantom"
4181 // this will also cause the prim to be visible to physics
4182 SetPhantom = false;
4183 }
4184 }
4185  
4186 if (UsePhysics && IsJoint())
4187 {
4188 SetPhantom = true;
4189 }
4190  
4191 if (UsePhysics)
4192 {
4193 AddFlag(PrimFlags.Physics);
4194 if (!wasUsingPhysics)
4195 {
4196 DoPhysicsPropertyUpdate(UsePhysics, false);
4197 }
4198 }
4199 else
4200 {
4201 RemFlag(PrimFlags.Physics);
4202 if (wasUsingPhysics)
4203 {
4204 DoPhysicsPropertyUpdate(UsePhysics, false);
4205 }
4206 }
4207  
4208 if (SetPhantom
4209 || ParentGroup.IsAttachment
4210 || PhysicsShapeType == (byte)PhysShapeType.none
4211 || (Shape.PathCurve == (byte)Extrusion.Flexible)) // note: this may have been changed above in the case of joints
4212 {
4213 AddFlag(PrimFlags.Phantom);
4214  
4215 if (PhysActor != null)
4216 {
4217 RemoveFromPhysics();
4218 pa = null;
4219 }
4220 }
4221 else // Not phantom
4222 {
4223 RemFlag(PrimFlags.Phantom);
4224  
4225 if (ParentGroup.Scene == null)
4226 return;
4227  
4228 if (ParentGroup.Scene.CollidablePrims && pa == null)
4229 {
4230 AddToPhysics(UsePhysics, SetPhantom, false);
4231 pa = PhysActor;
4232  
4233 if (pa != null)
4234 {
4235 pa.SetMaterial(Material);
4236 DoPhysicsPropertyUpdate(UsePhysics, true);
4237  
4238 SubscribeForCollisionEvents();
4239 }
4240 }
4241 else // it already has a physical representation
4242 {
4243 DoPhysicsPropertyUpdate(UsePhysics, false); // Update physical status. If it's phantom this will remove the prim
4244 }
4245 }
4246  
4247 if (SetVD)
4248 {
4249 // If the above logic worked (this is urgent candidate to unit tests!)
4250 // we now have a physicsactor.
4251 // Defensive programming calls for a check here.
4252 // Better would be throwing an exception that could be catched by a unit test as the internal
4253 // logic should make sure, this Physactor is always here.
4254 if (pa != null)
4255 {
4256 pa.SetVolumeDetect(1);
4257 AddFlag(PrimFlags.Phantom); // We set this flag also if VD is active
4258 VolumeDetectActive = true;
4259 }
4260 }
4261 else if (SetVD != wasVD)
4262 {
4263 // Remove VolumeDetect in any case. Note, it's safe to call SetVolumeDetect as often as you like
4264 // (mumbles, well, at least if you have infinte CPU powers :-))
4265 if (pa != null)
4266 pa.SetVolumeDetect(0);
4267  
4268 RemFlag(PrimFlags.Phantom);
4269 VolumeDetectActive = false;
4270 }
4271  
4272 if (SetTemporary)
4273 {
4274 AddFlag(PrimFlags.TemporaryOnRez);
4275 }
4276 else
4277 {
4278 RemFlag(PrimFlags.TemporaryOnRez);
4279 }
4280  
4281 // m_log.Debug("Update: PHY:" + UsePhysics.ToString() + ", T:" + IsTemporary.ToString() + ", PHA:" + IsPhantom.ToString() + " S:" + CastsShadows.ToString());
4282  
4283 if (ParentGroup != null)
4284 {
4285 ParentGroup.HasGroupChanged = true;
4286 ScheduleFullUpdate();
4287 }
4288  
4289 // m_log.DebugFormat("[SCENE OBJECT PART]: Updated PrimFlags on {0} {1} to {2}", Name, LocalId, Flags);
4290 }
4291  
4292 /// <summary>
4293 /// Subscribe for physics collision events if needed for scripts and sounds
4294 /// </summary>
4295 public void SubscribeForCollisionEvents()
4296 {
4297 PhysicsActor pa = PhysActor;
4298  
4299 if (pa != null)
4300 {
4301 if (
4302 ((AggregateScriptEvents & scriptEvents.collision) != 0) ||
4303 ((AggregateScriptEvents & scriptEvents.collision_end) != 0) ||
4304 ((AggregateScriptEvents & scriptEvents.collision_start) != 0) ||
4305 ((AggregateScriptEvents & scriptEvents.land_collision_start) != 0) ||
4306 ((AggregateScriptEvents & scriptEvents.land_collision) != 0) ||
4307 ((AggregateScriptEvents & scriptEvents.land_collision_end) != 0) ||
4308 ((ParentGroup.RootPart.AggregateScriptEvents & scriptEvents.collision) != 0) ||
4309 ((ParentGroup.RootPart.AggregateScriptEvents & scriptEvents.collision_end) != 0) ||
4310 ((ParentGroup.RootPart.AggregateScriptEvents & scriptEvents.collision_start) != 0) ||
4311 ((ParentGroup.RootPart.AggregateScriptEvents & scriptEvents.land_collision_start) != 0) ||
4312 ((ParentGroup.RootPart.AggregateScriptEvents & scriptEvents.land_collision) != 0) ||
4313 ((ParentGroup.RootPart.AggregateScriptEvents & scriptEvents.land_collision_end) != 0) ||
4314 (CollisionSound != UUID.Zero)
4315 )
4316 {
4317 if (!pa.SubscribedEvents())
4318 {
4319 // If not already subscribed for event, set up for a collision event.
4320 pa.OnCollisionUpdate += PhysicsCollision;
4321 pa.SubscribeEvents(1000);
4322 }
4323 }
4324 else
4325 {
4326 // There is no need to be subscribed to collisions so, if subscribed, remove subscription
4327 if (pa.SubscribedEvents())
4328 {
4329 pa.OnCollisionUpdate -= PhysicsCollision;
4330 pa.UnSubscribeEvents();
4331 }
4332 }
4333 }
4334 }
4335  
4336 /// <summary>
4337 /// Adds this part to the physics scene.
4338 /// </summary>
4339 /// <remarks>This method also sets the PhysActor property.</remarks>
4340 /// <param name="rigidBody">Add this prim with a rigid body.</param>
4341 /// <returns>
4342 /// The physics actor. null if there was a failure.
4343 /// </returns>
4344 private void AddToPhysics(bool isPhysical, bool isPhantom, bool applyDynamics)
4345 {
4346 PhysicsActor pa;
4347  
4348 Vector3 velocity = Velocity;
4349 Vector3 rotationalVelocity = AngularVelocity;;
4350  
4351 try
4352 {
4353 pa = ParentGroup.Scene.PhysicsScene.AddPrimShape(
4354 string.Format("{0}/{1}", Name, UUID),
4355 Shape,
4356 AbsolutePosition,
4357 Scale,
4358 GetWorldRotation(),
4359 isPhysical,
4360 isPhantom,
4361 PhysicsShapeType,
4362 m_localId);
4363 }
4364 catch (Exception e)
4365 {
4366 m_log.ErrorFormat("[SCENE]: caught exception meshing object {0}. Object set to phantom. e={1}", m_uuid, e);
4367 pa = null;
4368 }
4369  
4370 if (pa != null)
4371 {
4372 pa.SOPName = this.Name; // save object into the PhysActor so ODE internals know the joint/body info
4373 pa.SetMaterial(Material);
4374  
4375 pa.Density = Density;
4376 pa.GravModifier = GravityModifier;
4377 pa.Friction = Friction;
4378 pa.Restitution = Restitution;
4379  
4380 if (VolumeDetectActive) // change if not the default only
4381 pa.SetVolumeDetect(1);
4382 // we are going to tell rest of code about physics so better have this here
4383 PhysActor = pa;
4384  
4385 if (isPhysical)
4386 {
4387 if (ParentGroup.RootPart.KeyframeMotion != null)
4388 ParentGroup.RootPart.KeyframeMotion.Stop();
4389 ParentGroup.RootPart.KeyframeMotion = null;
4390 ParentGroup.Scene.AddPhysicalPrim(1);
4391  
4392 pa.OnRequestTerseUpdate += PhysicsRequestingTerseUpdate;
4393 pa.OnOutOfBounds += PhysicsOutOfBounds;
4394  
4395 if (ParentID != 0 && ParentID != LocalId)
4396 {
4397 PhysicsActor parentPa = ParentGroup.RootPart.PhysActor;
4398  
4399 if (parentPa != null)
4400 {
4401 pa.link(parentPa);
4402 }
4403 }
4404 }
4405  
4406 if (applyDynamics)
4407 // do independent of isphysical so parameters get setted (at least some)
4408 {
4409 Velocity = velocity;
4410 AngularVelocity = rotationalVelocity;
4411 // pa.Velocity = velocity;
4412 pa.RotationalVelocity = rotationalVelocity;
4413 }
4414  
4415 ParentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(pa);
4416 }
4417  
4418 PhysActor = pa;
4419 ParentGroup.Scene.EventManager.TriggerObjectAddedToPhysicalScene(this);
4420 }
4421  
4422 /// <summary>
4423 /// This removes the part from the physics scene.
4424 /// </summary>
4425 /// <remarks>
4426 /// This isn't the same as turning off physical, since even without being physical the prim has a physics
4427 /// representation for collision detection. Rather, this would be used in situations such as making a prim
4428 /// phantom.
4429 /// </remarks>
4430 public void RemoveFromPhysics()
4431 {
4432 ParentGroup.Scene.EventManager.TriggerObjectRemovedFromPhysicalScene(this);
4433 if (ParentGroup.Scene.PhysicsScene != null)
4434 ParentGroup.Scene.PhysicsScene.RemovePrim(PhysActor);
4435 PhysActor = null;
4436 }
4437  
4438 /// <summary>
4439 /// This updates the part's rotation and sends out an update to clients if necessary.
4440 /// </summary>
4441 /// <param name="rot"></param>
4442 public void UpdateRotation(Quaternion rot)
4443 {
4444 if (rot != RotationOffset)
4445 {
4446 RotationOffset = rot;
4447  
4448 if (ParentGroup != null)
4449 {
4450 ParentGroup.HasGroupChanged = true;
4451 ScheduleTerseUpdate();
4452 }
4453 }
4454 }
4455  
4456 /// <summary>
4457 /// Update the shape of this part.
4458 /// </summary>
4459 /// <param name="shapeBlock"></param>
4460 public void UpdateShape(ObjectShapePacket.ObjectDataBlock shapeBlock)
4461 {
4462 m_shape.PathBegin = shapeBlock.PathBegin;
4463 m_shape.PathEnd = shapeBlock.PathEnd;
4464 m_shape.PathScaleX = shapeBlock.PathScaleX;
4465 m_shape.PathScaleY = shapeBlock.PathScaleY;
4466 m_shape.PathShearX = shapeBlock.PathShearX;
4467 m_shape.PathShearY = shapeBlock.PathShearY;
4468 m_shape.PathSkew = shapeBlock.PathSkew;
4469 m_shape.ProfileBegin = shapeBlock.ProfileBegin;
4470 m_shape.ProfileEnd = shapeBlock.ProfileEnd;
4471 m_shape.PathCurve = shapeBlock.PathCurve;
4472 m_shape.ProfileCurve = shapeBlock.ProfileCurve;
4473 m_shape.ProfileHollow = shapeBlock.ProfileHollow;
4474 m_shape.PathRadiusOffset = shapeBlock.PathRadiusOffset;
4475 m_shape.PathRevolutions = shapeBlock.PathRevolutions;
4476 m_shape.PathTaperX = shapeBlock.PathTaperX;
4477 m_shape.PathTaperY = shapeBlock.PathTaperY;
4478 m_shape.PathTwist = shapeBlock.PathTwist;
4479 m_shape.PathTwistBegin = shapeBlock.PathTwistBegin;
4480  
4481 PhysicsActor pa = PhysActor;
4482  
4483 if (pa != null)
4484 {
4485 pa.Shape = m_shape;
4486 ParentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(pa);
4487 }
4488  
4489 // This is what makes vehicle trailers work
4490 // A script in a child prim re-issues
4491 // llSetPrimitiveParams(PRIM_TYPE) every few seconds. That
4492 // prevents autoreturn. This is not well known. It also works
4493 // in SL.
4494 //
4495 if (ParentGroup.RootPart != this)
4496 ParentGroup.RootPart.Rezzed = DateTime.UtcNow;
4497  
4498 ParentGroup.HasGroupChanged = true;
4499 TriggerScriptChangedEvent(Changed.SHAPE);
4500 ScheduleFullUpdate();
4501 }
4502  
4503 public void UpdateSlice(float begin, float end)
4504 {
4505 if (end < begin)
4506 {
4507 float temp = begin;
4508 begin = end;
4509 end = temp;
4510 }
4511 end = Math.Min(1f, Math.Max(0f, end));
4512 begin = Math.Min(Math.Min(1f, Math.Max(0f, begin)), end - 0.02f);
4513 if (begin < 0.02f && end < 0.02f)
4514 {
4515 begin = 0f;
4516 end = 0.02f;
4517 }
4518  
4519 ushort uBegin = (ushort)(50000.0 * begin);
4520 ushort uEnd = (ushort)(50000.0 * (1f - end));
4521 bool updatePossiblyNeeded = false;
4522 PrimType primType = GetPrimType();
4523 if (primType == PrimType.SPHERE || primType == PrimType.TORUS || primType == PrimType.TUBE || primType == PrimType.RING)
4524 {
4525 if (m_shape.ProfileBegin != uBegin || m_shape.ProfileEnd != uEnd)
4526 {
4527 m_shape.ProfileBegin = uBegin;
4528 m_shape.ProfileEnd = uEnd;
4529 updatePossiblyNeeded = true;
4530 }
4531 }
4532 else if (m_shape.PathBegin != uBegin || m_shape.PathEnd != uEnd)
4533 {
4534 m_shape.PathBegin = uBegin;
4535 m_shape.PathEnd = uEnd;
4536 updatePossiblyNeeded = true;
4537 }
4538  
4539 if (updatePossiblyNeeded && ParentGroup != null)
4540 {
4541 ParentGroup.HasGroupChanged = true;
4542 }
4543 if (updatePossiblyNeeded && PhysActor != null)
4544 {
4545 PhysActor.Shape = m_shape;
4546 ParentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(PhysActor);
4547 }
4548 if (updatePossiblyNeeded)
4549 {
4550 ScheduleFullUpdate();
4551 }
4552 }
4553  
4554 /// <summary>
4555 /// If the part is a sculpt/mesh, retrieve the mesh data and reinsert it into the shape so that the physics
4556 /// engine can use it.
4557 /// </summary>
4558 /// <remarks>
4559 /// When the physics engine has finished with it, the sculpt data is discarded to save memory.
4560 /// </remarks>
4561 /*
4562 public void CheckSculptAndLoad()
4563 {
4564 // m_log.DebugFormat("Processing CheckSculptAndLoad for {0} {1}", Name, LocalId);
4565  
4566 if (ParentGroup.IsDeleted)
4567 return;
4568  
4569 if ((ParentGroup.RootPart.GetEffectiveObjectFlags() & (uint)PrimFlags.Phantom) != 0)
4570 return;
4571  
4572 if (Shape.SculptEntry && Shape.SculptTexture != UUID.Zero)
4573 {
4574 // check if a previously decoded sculpt map has been cached
4575 // We don't read the file here - the meshmerizer will do that later.
4576 // TODO: Could we simplify the meshmerizer code by reading and setting the data here?
4577 if (File.Exists(System.IO.Path.Combine("j2kDecodeCache", "smap_" + Shape.SculptTexture.ToString())))
4578 {
4579 SculptTextureCallback(null);
4580 }
4581 else
4582 {
4583 ParentGroup.Scene.AssetService.Get(Shape.SculptTexture.ToString(), this, AssetReceived);
4584 }
4585 }
4586 }
4587 */
4588 /// <summary>
4589 /// Update the texture entry for this part.
4590 /// </summary>
4591 /// <param name="serializedTextureEntry"></param>
4592 public void UpdateTextureEntry(byte[] serializedTextureEntry)
4593 {
4594 UpdateTextureEntry(new Primitive.TextureEntry(serializedTextureEntry, 0, serializedTextureEntry.Length));
4595 }
4596  
4597 /// <summary>
4598 /// Update the texture entry for this part.
4599 /// </summary>
4600 /// <param name="newTex"></param>
4601 public void UpdateTextureEntry(Primitive.TextureEntry newTex)
4602 {
4603 Primitive.TextureEntry oldTex = Shape.Textures;
4604  
4605 Changed changeFlags = 0;
4606  
4607 Primitive.TextureEntryFace fallbackNewFace = newTex.DefaultTexture;
4608 Primitive.TextureEntryFace fallbackOldFace = oldTex.DefaultTexture;
4609  
4610 // On Incoming packets, sometimes newText.DefaultTexture is null. The assumption is that all
4611 // other prim-sides are set, but apparently that's not always the case. Lets assume packet/data corruption at this point.
4612 if (fallbackNewFace == null)
4613 {
4614 fallbackNewFace = new Primitive.TextureEntry(Util.BLANK_TEXTURE_UUID).CreateFace(0);
4615 newTex.DefaultTexture = fallbackNewFace;
4616 }
4617 if (fallbackOldFace == null)
4618 {
4619 fallbackOldFace = new Primitive.TextureEntry(Util.BLANK_TEXTURE_UUID).CreateFace(0);
4620 oldTex.DefaultTexture = fallbackOldFace;
4621 }
4622  
4623 // Materials capable viewers can send a ObjectImage packet
4624 // when nothing in TE has changed. MaterialID should be updated
4625 // by the RenderMaterials CAP handler, so updating it here may cause a
4626 // race condtion. Therefore, if no non-materials TE fields have changed,
4627 // we should ignore any changes and not update Shape.TextureEntry
4628  
4629 bool otherFieldsChanged = false;
4630  
4631 for (int i = 0 ; i < GetNumberOfSides(); i++)
4632 {
4633  
4634 Primitive.TextureEntryFace newFace = newTex.DefaultTexture;
4635 Primitive.TextureEntryFace oldFace = oldTex.DefaultTexture;
4636  
4637 if (oldTex.FaceTextures[i] != null)
4638 oldFace = oldTex.FaceTextures[i];
4639 if (newTex.FaceTextures[i] != null)
4640 newFace = newTex.FaceTextures[i];
4641  
4642 Color4 oldRGBA = oldFace.RGBA;
4643 Color4 newRGBA = newFace.RGBA;
4644  
4645 if (oldRGBA.R != newRGBA.R ||
4646 oldRGBA.G != newRGBA.G ||
4647 oldRGBA.B != newRGBA.B ||
4648 oldRGBA.A != newRGBA.A)
4649 changeFlags |= Changed.COLOR;
4650  
4651 if (oldFace.TextureID != newFace.TextureID)
4652 changeFlags |= Changed.TEXTURE;
4653  
4654 // Max change, skip the rest of testing
4655 if (changeFlags == (Changed.TEXTURE | Changed.COLOR))
4656 break;
4657  
4658 if (!otherFieldsChanged)
4659 {
4660 if (oldFace.Bump != newFace.Bump) otherFieldsChanged = true;
4661 if (oldFace.Fullbright != newFace.Fullbright) otherFieldsChanged = true;
4662 if (oldFace.Glow != newFace.Glow) otherFieldsChanged = true;
4663 if (oldFace.MediaFlags != newFace.MediaFlags) otherFieldsChanged = true;
4664 if (oldFace.OffsetU != newFace.OffsetU) otherFieldsChanged = true;
4665 if (oldFace.OffsetV != newFace.OffsetV) otherFieldsChanged = true;
4666 if (oldFace.RepeatU != newFace.RepeatU) otherFieldsChanged = true;
4667 if (oldFace.RepeatV != newFace.RepeatV) otherFieldsChanged = true;
4668 if (oldFace.Rotation != newFace.Rotation) otherFieldsChanged = true;
4669 if (oldFace.Shiny != newFace.Shiny) otherFieldsChanged = true;
4670 if (oldFace.TexMapType != newFace.TexMapType) otherFieldsChanged = true;
4671 }
4672 }
4673  
4674 if (changeFlags != 0 || otherFieldsChanged)
4675 {
4676 m_shape.TextureEntry = newTex.GetBytes();
4677 if (changeFlags != 0)
4678 TriggerScriptChangedEvent(changeFlags);
4679 UpdateFlag = UpdateRequired.FULL;
4680 ParentGroup.HasGroupChanged = true;
4681  
4682 //This is madness..
4683 //ParentGroup.ScheduleGroupForFullUpdate();
4684 //This is sparta
4685 ScheduleFullUpdate();
4686 }
4687 }
4688  
4689 public void aggregateScriptEvents()
4690 {
4691 if (ParentGroup == null || ParentGroup.RootPart == null)
4692 return;
4693  
4694 AggregateScriptEvents = 0;
4695  
4696 // Aggregate script events
4697 lock (m_scriptEvents)
4698 {
4699 foreach (scriptEvents s in m_scriptEvents.Values)
4700 {
4701 AggregateScriptEvents |= s;
4702 }
4703 }
4704  
4705 uint objectflagupdate = 0;
4706  
4707 if (
4708 ((AggregateScriptEvents & scriptEvents.touch) != 0) ||
4709 ((AggregateScriptEvents & scriptEvents.touch_end) != 0) ||
4710 ((AggregateScriptEvents & scriptEvents.touch_start) != 0)
4711 )
4712 {
4713 objectflagupdate |= (uint) PrimFlags.Touch;
4714 }
4715  
4716 if ((AggregateScriptEvents & scriptEvents.money) != 0)
4717 {
4718 objectflagupdate |= (uint) PrimFlags.Money;
4719 }
4720  
4721 if (AllowedDrop)
4722 {
4723 objectflagupdate |= (uint) PrimFlags.AllowInventoryDrop;
4724 }
4725  
4726 SubscribeForCollisionEvents();
4727  
4728 //if ((GetEffectiveObjectFlags() & (uint)PrimFlags.Scripted) != 0)
4729 //{
4730 // ParentGroup.Scene.EventManager.OnScriptTimerEvent += handleTimerAccounting;
4731 //}
4732 //else
4733 //{
4734 // ParentGroup.Scene.EventManager.OnScriptTimerEvent -= handleTimerAccounting;
4735 //}
4736  
4737 LocalFlags = (PrimFlags)objectflagupdate;
4738  
4739 if (ParentGroup != null && ParentGroup.RootPart == this)
4740 {
4741 ParentGroup.aggregateScriptEvents();
4742 }
4743 else
4744 {
4745 // m_log.DebugFormat(
4746 // "[SCENE OBJECT PART]: Scheduling part {0} {1} for full update in aggregateScriptEvents()", Name, LocalId);
4747 ScheduleFullUpdate();
4748 }
4749 }
4750  
4751 public void SetCameraAtOffset(Vector3 v)
4752 {
4753 m_cameraAtOffset = v;
4754 }
4755  
4756 public void SetCameraEyeOffset(Vector3 v)
4757 {
4758 m_cameraEyeOffset = v;
4759 }
4760  
4761 public void SetForceMouselook(bool force)
4762 {
4763 m_forceMouselook = force;
4764 }
4765  
4766 public Vector3 GetCameraAtOffset()
4767 {
4768 return m_cameraAtOffset;
4769 }
4770  
4771 public Vector3 GetCameraEyeOffset()
4772 {
4773 return m_cameraEyeOffset;
4774 }
4775  
4776 public bool GetForceMouselook()
4777 {
4778 return m_forceMouselook;
4779 }
4780  
4781 public override string ToString()
4782 {
4783 return String.Format("{0} {1} (parent {2}))", Name, UUID, ParentGroup);
4784 }
4785  
4786 #endregion Public Methods
4787  
4788 public void SendTerseUpdateToClient(IClientAPI remoteClient)
4789 {
4790 if (ParentGroup.IsDeleted)
4791 return;
4792  
4793 if (ParentGroup.IsAttachment
4794 && (ParentGroup.RootPart != this
4795 || ParentGroup.AttachedAvatar != remoteClient.AgentId && ParentGroup.HasPrivateAttachmentPoint))
4796 return;
4797  
4798 // Causes this thread to dig into the Client Thread Data.
4799 // Remember your locking here!
4800 remoteClient.SendEntityUpdate(
4801 this,
4802 PrimUpdateFlags.Position | PrimUpdateFlags.Rotation | PrimUpdateFlags.Velocity
4803 | PrimUpdateFlags.Acceleration | PrimUpdateFlags.AngularVelocity);
4804  
4805 ParentGroup.Scene.StatsReporter.AddObjectUpdates(1);
4806 }
4807  
4808 public void AddScriptLPS(int count)
4809 {
4810 ParentGroup.AddScriptLPS(count);
4811 }
4812  
4813 public void ApplyNextOwnerPermissions()
4814 {
4815 // Export needs to be preserved in the base and everyone
4816 // mask, but removed in the owner mask as a next owner
4817 // can never change the export status
4818 BaseMask &= NextOwnerMask | (uint)PermissionMask.Export;
4819 OwnerMask &= NextOwnerMask;
4820 EveryoneMask &= NextOwnerMask | (uint)PermissionMask.Export;
4821  
4822 Inventory.ApplyNextOwnerPermissions();
4823 }
4824  
4825 public void UpdateLookAt()
4826 {
4827 try
4828 {
4829 if (APIDTarget != Quaternion.Identity)
4830 {
4831 if (m_APIDIterations <= 1)
4832 {
4833 UpdateRotation(APIDTarget);
4834 APIDTarget = Quaternion.Identity;
4835 return;
4836 }
4837  
4838 Quaternion rot = Quaternion.Slerp(RotationOffset,APIDTarget,1.0f/(float)m_APIDIterations);
4839 rot.Normalize();
4840 UpdateRotation(rot);
4841  
4842 m_APIDIterations--;
4843  
4844 // This ensures that we'll check this object on the next iteration
4845 ParentGroup.QueueForUpdateCheck();
4846 }
4847 }
4848 catch (Exception ex)
4849 {
4850 m_log.Error("[Physics] " + ex);
4851 }
4852 }
4853  
4854 public Color4 GetTextColor()
4855 {
4856 Color color = Color;
4857 return new Color4(color.R, color.G, color.B, (byte)(0xFF - color.A));
4858 }
4859  
4860 /// <summary>
4861 /// Record an avatar sitting on this part.
4862 /// </summary>
4863 /// <remarks>This is called for all the sitting avatars whether there is a sit target set or not.</remarks>
4864 /// <returns>
4865 /// true if the avatar was not already recorded, false otherwise.
4866 /// </returns>
4867 /// <param name='avatarId'></param>
4868 protected internal bool AddSittingAvatar(UUID avatarId)
4869 {
4870 lock (ParentGroup.m_sittingAvatars)
4871 {
4872 if (IsSitTargetSet && SitTargetAvatar == UUID.Zero)
4873 SitTargetAvatar = avatarId;
4874  
4875 if (m_sittingAvatars == null)
4876 m_sittingAvatars = new HashSet<UUID>();
4877  
4878 if (m_sittingAvatars.Add(avatarId))
4879 {
4880 ParentGroup.m_sittingAvatars.Add(avatarId);
4881  
4882 return true;
4883 }
4884  
4885 return false;
4886 }
4887 }
4888  
4889 /// <summary>
4890 /// Remove an avatar recorded as sitting on this part.
4891 /// </summary>
4892 /// <remarks>This applies to all sitting avatars whether there is a sit target set or not.</remarks>
4893 /// <returns>
4894 /// true if the avatar was present and removed, false if it was not present.
4895 /// </returns>
4896 /// <param name='avatarId'></param>
4897 protected internal bool RemoveSittingAvatar(UUID avatarId)
4898 {
4899 lock (ParentGroup.m_sittingAvatars)
4900 {
4901 if (SitTargetAvatar == avatarId)
4902 SitTargetAvatar = UUID.Zero;
4903  
4904 if (m_sittingAvatars == null)
4905 return false;
4906  
4907 if (m_sittingAvatars.Remove(avatarId))
4908 {
4909 if (m_sittingAvatars.Count == 0)
4910 m_sittingAvatars = null;
4911  
4912 ParentGroup.m_sittingAvatars.Remove(avatarId);
4913  
4914 return true;
4915 }
4916  
4917 return false;
4918 }
4919 }
4920  
4921 /// <summary>
4922 /// Get a copy of the list of sitting avatars.
4923 /// </summary>
4924 /// <remarks>This applies to all sitting avatars whether there is a sit target set or not.</remarks>
4925 /// <returns>A hashset of the sitting avatars. Returns null if there are no sitting avatars.</returns>
4926 public HashSet<UUID> GetSittingAvatars()
4927 {
4928 lock (ParentGroup.m_sittingAvatars)
4929 {
4930 if (m_sittingAvatars == null)
4931 return null;
4932 else
4933 return new HashSet<UUID>(m_sittingAvatars);
4934 }
4935 }
4936  
4937 /// <summary>
4938 /// Gets the number of sitting avatars.
4939 /// </summary>
4940 /// <remarks>This applies to all sitting avatars whether there is a sit target set or not.</remarks>
4941 /// <returns></returns>
4942 public int GetSittingAvatarsCount()
4943 {
4944 lock (ParentGroup.m_sittingAvatars)
4945 {
4946 if (m_sittingAvatars == null)
4947 return 0;
4948 else
4949 return m_sittingAvatars.Count;
4950 }
4951 }
4952 }
4953 }