PageRenderTime 54ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs

https://bitbucket.org/AlethiaGrid/opensimulator-0.7-.x
C# | 3641 lines | 2323 code | 506 blank | 812 comment | 455 complexity | bf1bf34707346d156ea424dfa591c268 MD5 | raw file
Possible License(s): MIT, BSD-3-Clause

Large files files are truncated, but you can click here to view the full file

  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. using System;
  28. using System.ComponentModel;
  29. using System.Collections.Generic;
  30. using System.Drawing;
  31. using System.IO;
  32. using System.Linq;
  33. using System.Threading;
  34. using System.Xml;
  35. using System.Xml.Serialization;
  36. using OpenMetaverse;
  37. using OpenMetaverse.Packets;
  38. using OpenSim.Framework;
  39. using OpenSim.Region.Framework.Interfaces;
  40. using OpenSim.Region.Physics.Manager;
  41. using OpenSim.Region.Framework.Scenes.Serialization;
  42. namespace OpenSim.Region.Framework.Scenes
  43. {
  44. [Flags]
  45. public enum scriptEvents
  46. {
  47. None = 0,
  48. attach = 1,
  49. collision = 16,
  50. collision_end = 32,
  51. collision_start = 64,
  52. control = 128,
  53. dataserver = 256,
  54. email = 512,
  55. http_response = 1024,
  56. land_collision = 2048,
  57. land_collision_end = 4096,
  58. land_collision_start = 8192,
  59. at_target = 16384,
  60. at_rot_target = 16777216,
  61. listen = 32768,
  62. money = 65536,
  63. moving_end = 131072,
  64. moving_start = 262144,
  65. not_at_rot_target = 524288,
  66. not_at_target = 1048576,
  67. remote_data = 8388608,
  68. run_time_permissions = 268435456,
  69. state_entry = 1073741824,
  70. state_exit = 2,
  71. timer = 4,
  72. touch = 8,
  73. touch_end = 536870912,
  74. touch_start = 2097152,
  75. object_rez = 4194304
  76. }
  77. struct scriptPosTarget
  78. {
  79. public Vector3 targetPos;
  80. public float tolerance;
  81. public uint handle;
  82. }
  83. struct scriptRotTarget
  84. {
  85. public Quaternion targetRot;
  86. public float tolerance;
  87. public uint handle;
  88. }
  89. public delegate void PrimCountTaintedDelegate();
  90. /// <summary>
  91. /// A scene object group is conceptually an object in the scene. The object is constituted of SceneObjectParts
  92. /// (often known as prims), one of which is considered the root part.
  93. /// </summary>
  94. public partial class SceneObjectGroup : EntityBase, ISceneObject
  95. {
  96. // Axis selection bitmask used by SetAxisRotation()
  97. // Just happen to be the same bits used by llSetStatus() and defined in ScriptBaseClass.
  98. public enum axisSelect : int
  99. {
  100. STATUS_ROTATE_X = 0x002,
  101. STATUS_ROTATE_Y = 0x004,
  102. STATUS_ROTATE_Z = 0x008,
  103. }
  104. // private PrimCountTaintedDelegate handlerPrimCountTainted = null;
  105. /// <summary>
  106. /// Signal whether the non-inventory attributes of any prims in the group have changed
  107. /// since the group's last persistent backup
  108. /// </summary>
  109. private bool m_hasGroupChanged = false;
  110. private long timeFirstChanged;
  111. private long timeLastChanged;
  112. /// <summary>
  113. /// This indicates whether the object has changed such that it needs to be repersisted to permenant storage
  114. /// (the database).
  115. /// </summary>
  116. /// <remarks>
  117. /// Ultimately, this should be managed such that region modules can change it at the end of a set of operations
  118. /// so that either all changes are preserved or none at all. However, currently, a large amount of internal
  119. /// code will set this anyway when some object properties are changed.
  120. /// </remarks>
  121. public bool HasGroupChanged
  122. {
  123. set
  124. {
  125. if (value)
  126. {
  127. timeLastChanged = DateTime.Now.Ticks;
  128. if (!m_hasGroupChanged)
  129. timeFirstChanged = DateTime.Now.Ticks;
  130. }
  131. m_hasGroupChanged = value;
  132. // m_log.DebugFormat(
  133. // "[SCENE OBJECT GROUP]: HasGroupChanged set to {0} for {1} {2}", m_hasGroupChanged, Name, LocalId);
  134. }
  135. get { return m_hasGroupChanged; }
  136. }
  137. /// <summary>
  138. /// Has the group changed due to an unlink operation? We record this in order to optimize deletion, since
  139. /// an unlinked group currently has to be persisted to the database before we can perform an unlink operation.
  140. /// </summary>
  141. public bool HasGroupChangedDueToDelink { get; private set; }
  142. private bool isTimeToPersist()
  143. {
  144. if (IsSelected || IsDeleted || IsAttachment)
  145. return false;
  146. if (!m_hasGroupChanged)
  147. return false;
  148. if (m_scene.ShuttingDown)
  149. return true;
  150. long currentTime = DateTime.Now.Ticks;
  151. if (currentTime - timeLastChanged > m_scene.m_dontPersistBefore || currentTime - timeFirstChanged > m_scene.m_persistAfter)
  152. return true;
  153. return false;
  154. }
  155. /// <summary>
  156. /// Is this scene object acting as an attachment?
  157. /// </summary>
  158. public bool IsAttachment { get; set; }
  159. /// <summary>
  160. /// The avatar to which this scene object is attached.
  161. /// </summary>
  162. /// <remarks>
  163. /// If we're not attached to an avatar then this is UUID.Zero
  164. /// </remarks>
  165. public UUID AttachedAvatar { get; set; }
  166. /// <summary>
  167. /// Attachment point of this scene object to an avatar.
  168. /// </summary>
  169. /// <remarks>
  170. /// 0 if we're not attached to anything
  171. /// </remarks>
  172. public uint AttachmentPoint
  173. {
  174. get
  175. {
  176. return m_rootPart.Shape.State;
  177. }
  178. set
  179. {
  180. IsAttachment = value != 0;
  181. m_rootPart.Shape.State = (byte)value;
  182. }
  183. }
  184. /// <summary>
  185. /// If this scene object has an attachment point then indicate whether there is a point where
  186. /// attachments are perceivable by avatars other than the avatar to which this object is attached.
  187. /// </summary>
  188. /// <remarks>
  189. /// HUDs are not perceivable by other avatars.
  190. /// </remarks>
  191. public bool HasPrivateAttachmentPoint
  192. {
  193. get
  194. {
  195. return AttachmentPoint >= (uint)OpenMetaverse.AttachmentPoint.HUDCenter2
  196. && AttachmentPoint <= (uint)OpenMetaverse.AttachmentPoint.HUDBottomRight;
  197. }
  198. }
  199. public void ClearPartAttachmentData()
  200. {
  201. AttachmentPoint = 0;
  202. // Even though we don't use child part state parameters for attachments any more, we still need to set
  203. // these to zero since having them non-zero in rezzed scene objects will crash some clients. Even if
  204. // we store them correctly, scene objects that we receive from elsewhere might not.
  205. foreach (SceneObjectPart part in Parts)
  206. part.Shape.State = 0;
  207. }
  208. /// <summary>
  209. /// Is this scene object phantom?
  210. /// </summary>
  211. /// <remarks>
  212. /// Updating must currently take place through UpdatePrimFlags()
  213. /// </remarks>
  214. public bool IsPhantom
  215. {
  216. get { return (RootPart.Flags & PrimFlags.Phantom) != 0; }
  217. }
  218. /// <summary>
  219. /// Does this scene object use physics?
  220. /// </summary>
  221. /// <remarks>
  222. /// Updating must currently take place through UpdatePrimFlags()
  223. /// </remarks>
  224. public bool UsesPhysics
  225. {
  226. get { return (RootPart.Flags & PrimFlags.Physics) != 0; }
  227. }
  228. /// <summary>
  229. /// Is this scene object temporary?
  230. /// </summary>
  231. /// <remarks>
  232. /// Updating must currently take place through UpdatePrimFlags()
  233. /// </remarks>
  234. public bool IsTemporary
  235. {
  236. get { return (RootPart.Flags & PrimFlags.TemporaryOnRez) != 0; }
  237. }
  238. public bool IsVolumeDetect
  239. {
  240. get { return RootPart.VolumeDetectActive; }
  241. }
  242. private Vector3 lastPhysGroupPos;
  243. private Quaternion lastPhysGroupRot;
  244. private bool m_isBackedUp;
  245. protected MapAndArray<UUID, SceneObjectPart> m_parts = new MapAndArray<UUID, SceneObjectPart>();
  246. protected ulong m_regionHandle;
  247. protected SceneObjectPart m_rootPart;
  248. // private Dictionary<UUID, scriptEvents> m_scriptEvents = new Dictionary<UUID, scriptEvents>();
  249. private Dictionary<uint, scriptPosTarget> m_targets = new Dictionary<uint, scriptPosTarget>();
  250. private Dictionary<uint, scriptRotTarget> m_rotTargets = new Dictionary<uint, scriptRotTarget>();
  251. private bool m_scriptListens_atTarget;
  252. private bool m_scriptListens_notAtTarget;
  253. private bool m_scriptListens_atRotTarget;
  254. private bool m_scriptListens_notAtRotTarget;
  255. internal Dictionary<UUID, string> m_savedScriptState;
  256. #region Properties
  257. /// <summary>
  258. /// The name of an object grouping is always the same as its root part
  259. /// </summary>
  260. public override string Name
  261. {
  262. get { return RootPart.Name; }
  263. set { RootPart.Name = value; }
  264. }
  265. public string Description
  266. {
  267. get { return RootPart.Description; }
  268. set { RootPart.Description = value; }
  269. }
  270. /// <summary>
  271. /// Added because the Parcel code seems to use it
  272. /// but not sure a object should have this
  273. /// as what does it tell us? that some avatar has selected it (but not what Avatar/user)
  274. /// think really there should be a list (or whatever) in each scenepresence
  275. /// saying what prim(s) that user has selected.
  276. /// </summary>
  277. protected bool m_isSelected = false;
  278. /// <summary>
  279. /// Number of prims in this group
  280. /// </summary>
  281. public int PrimCount
  282. {
  283. get { return m_parts.Count; }
  284. }
  285. public Quaternion GroupRotation
  286. {
  287. get { return m_rootPart.RotationOffset; }
  288. }
  289. public Vector3 GroupScale
  290. {
  291. get
  292. {
  293. Vector3 minScale = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionSize);
  294. Vector3 maxScale = Vector3.Zero;
  295. Vector3 finalScale = new Vector3(0.5f, 0.5f, 0.5f);
  296. SceneObjectPart[] parts = m_parts.GetArray();
  297. for (int i = 0; i < parts.Length; i++)
  298. {
  299. SceneObjectPart part = parts[i];
  300. Vector3 partscale = part.Scale;
  301. Vector3 partoffset = part.OffsetPosition;
  302. minScale.X = (partscale.X + partoffset.X < minScale.X) ? partscale.X + partoffset.X : minScale.X;
  303. minScale.Y = (partscale.Y + partoffset.Y < minScale.Y) ? partscale.Y + partoffset.Y : minScale.Y;
  304. minScale.Z = (partscale.Z + partoffset.Z < minScale.Z) ? partscale.Z + partoffset.Z : minScale.Z;
  305. maxScale.X = (partscale.X + partoffset.X > maxScale.X) ? partscale.X + partoffset.X : maxScale.X;
  306. maxScale.Y = (partscale.Y + partoffset.Y > maxScale.Y) ? partscale.Y + partoffset.Y : maxScale.Y;
  307. maxScale.Z = (partscale.Z + partoffset.Z > maxScale.Z) ? partscale.Z + partoffset.Z : maxScale.Z;
  308. }
  309. finalScale.X = (minScale.X > maxScale.X) ? minScale.X : maxScale.X;
  310. finalScale.Y = (minScale.Y > maxScale.Y) ? minScale.Y : maxScale.Y;
  311. finalScale.Z = (minScale.Z > maxScale.Z) ? minScale.Z : maxScale.Z;
  312. return finalScale;
  313. }
  314. }
  315. public UUID GroupID
  316. {
  317. get { return m_rootPart.GroupID; }
  318. set { m_rootPart.GroupID = value; }
  319. }
  320. public SceneObjectPart[] Parts
  321. {
  322. get { return m_parts.GetArray(); }
  323. }
  324. public bool ContainsPart(UUID partID)
  325. {
  326. return m_parts.ContainsKey(partID);
  327. }
  328. /// <summary>
  329. /// Does this group contain the given part?
  330. /// should be able to remove these methods once we have a entity index in scene
  331. /// </summary>
  332. /// <param name="localID"></param>
  333. /// <returns></returns>
  334. public bool ContainsPart(uint localID)
  335. {
  336. SceneObjectPart[] parts = m_parts.GetArray();
  337. for (int i = 0; i < parts.Length; i++)
  338. {
  339. if (parts[i].LocalId == localID)
  340. return true;
  341. }
  342. return false;
  343. }
  344. /// <value>
  345. /// The root part of this scene object
  346. /// </value>
  347. public SceneObjectPart RootPart
  348. {
  349. get { return m_rootPart; }
  350. }
  351. public ulong RegionHandle
  352. {
  353. get { return m_regionHandle; }
  354. set
  355. {
  356. m_regionHandle = value;
  357. SceneObjectPart[] parts = m_parts.GetArray();
  358. for (int i = 0; i < parts.Length; i++)
  359. parts[i].RegionHandle = value;
  360. }
  361. }
  362. /// <summary>
  363. /// Check both the attachment property and the relevant properties of the underlying root part.
  364. /// </summary>
  365. /// <remarks>
  366. /// This is necessary in some cases, particularly when a scene object has just crossed into a region and doesn't
  367. /// have the IsAttachment property yet checked.
  368. ///
  369. /// FIXME: However, this should be fixed so that this property
  370. /// propertly reflects the underlying status.
  371. /// </remarks>
  372. /// <returns></returns>
  373. public bool IsAttachmentCheckFull()
  374. {
  375. return (IsAttachment || (m_rootPart.Shape.PCode == 9 && m_rootPart.Shape.State != 0));
  376. }
  377. /// <summary>
  378. /// The absolute position of this scene object in the scene
  379. /// </summary>
  380. public override Vector3 AbsolutePosition
  381. {
  382. get { return m_rootPart.GroupPosition; }
  383. set
  384. {
  385. Vector3 val = value;
  386. if (Scene != null)
  387. {
  388. if (
  389. // (Scene.TestBorderCross(val - Vector3.UnitX, Cardinals.E)
  390. // || Scene.TestBorderCross(val + Vector3.UnitX, Cardinals.W)
  391. // || Scene.TestBorderCross(val - Vector3.UnitY, Cardinals.N)
  392. // || Scene.TestBorderCross(val + Vector3.UnitY, Cardinals.S))
  393. // Experimental change for better border crossings.
  394. // The commented out original lines above would, it seems, trigger
  395. // a border crossing a little early or late depending on which
  396. // direction the object was moving.
  397. (Scene.TestBorderCross(val, Cardinals.E)
  398. || Scene.TestBorderCross(val, Cardinals.W)
  399. || Scene.TestBorderCross(val, Cardinals.N)
  400. || Scene.TestBorderCross(val, Cardinals.S))
  401. && !IsAttachmentCheckFull() && (!Scene.LoadingPrims))
  402. {
  403. m_scene.CrossPrimGroupIntoNewRegion(val, this, true);
  404. }
  405. }
  406. if (RootPart.GetStatusSandbox())
  407. {
  408. if (Util.GetDistanceTo(RootPart.StatusSandboxPos, value) > 10)
  409. {
  410. RootPart.ScriptSetPhysicsStatus(false);
  411. if (Scene != null)
  412. Scene.SimChat(Utils.StringToBytes("Hit Sandbox Limit"),
  413. ChatTypeEnum.DebugChannel, 0x7FFFFFFF, RootPart.AbsolutePosition, Name, UUID, false);
  414. return;
  415. }
  416. }
  417. // Restuff the new GroupPosition into each SOP of the linkset.
  418. // This has the affect of resetting and tainting the physics actors.
  419. SceneObjectPart[] parts = m_parts.GetArray();
  420. for (int i = 0; i < parts.Length; i++)
  421. parts[i].GroupPosition = val;
  422. //if (m_rootPart.PhysActor != null)
  423. //{
  424. //m_rootPart.PhysActor.Position =
  425. //new PhysicsVector(m_rootPart.GroupPosition.X, m_rootPart.GroupPosition.Y,
  426. //m_rootPart.GroupPosition.Z);
  427. //m_scene.PhysicsScene.AddPhysicsActorTaint(m_rootPart.PhysActor);
  428. //}
  429. if (Scene != null)
  430. Scene.EventManager.TriggerParcelPrimCountTainted();
  431. }
  432. }
  433. public override uint LocalId
  434. {
  435. get { return m_rootPart.LocalId; }
  436. set { m_rootPart.LocalId = value; }
  437. }
  438. public override UUID UUID
  439. {
  440. get { return m_rootPart.UUID; }
  441. set
  442. {
  443. lock (m_parts.SyncRoot)
  444. {
  445. m_parts.Remove(m_rootPart.UUID);
  446. m_rootPart.UUID = value;
  447. m_parts.Add(value, m_rootPart);
  448. }
  449. }
  450. }
  451. public UUID LastOwnerID
  452. {
  453. get { return m_rootPart.LastOwnerID; }
  454. set { m_rootPart.LastOwnerID = value; }
  455. }
  456. public UUID OwnerID
  457. {
  458. get { return m_rootPart.OwnerID; }
  459. set { m_rootPart.OwnerID = value; }
  460. }
  461. public float Damage
  462. {
  463. get { return m_rootPart.Damage; }
  464. set { m_rootPart.Damage = value; }
  465. }
  466. public Color Color
  467. {
  468. get { return m_rootPart.Color; }
  469. set { m_rootPart.Color = value; }
  470. }
  471. public string Text
  472. {
  473. get {
  474. string returnstr = m_rootPart.Text;
  475. if (returnstr.Length > 255)
  476. {
  477. returnstr = returnstr.Substring(0, 255);
  478. }
  479. return returnstr;
  480. }
  481. set { m_rootPart.Text = value; }
  482. }
  483. protected virtual bool InSceneBackup
  484. {
  485. get { return true; }
  486. }
  487. public bool IsSelected
  488. {
  489. get { return m_isSelected; }
  490. set
  491. {
  492. m_isSelected = value;
  493. // Tell physics engine that group is selected
  494. PhysicsActor pa = m_rootPart.PhysActor;
  495. if (pa != null)
  496. {
  497. pa.Selected = value;
  498. // Pass it on to the children.
  499. SceneObjectPart[] parts = m_parts.GetArray();
  500. for (int i = 0; i < parts.Length; i++)
  501. {
  502. SceneObjectPart child = parts[i];
  503. PhysicsActor childPa = child.PhysActor;
  504. if (childPa != null)
  505. childPa.Selected = value;
  506. }
  507. }
  508. }
  509. }
  510. private SceneObjectPart m_PlaySoundMasterPrim = null;
  511. public SceneObjectPart PlaySoundMasterPrim
  512. {
  513. get { return m_PlaySoundMasterPrim; }
  514. set { m_PlaySoundMasterPrim = value; }
  515. }
  516. private List<SceneObjectPart> m_PlaySoundSlavePrims = new List<SceneObjectPart>();
  517. public List<SceneObjectPart> PlaySoundSlavePrims
  518. {
  519. get { return m_PlaySoundSlavePrims; }
  520. set { m_PlaySoundSlavePrims = value; }
  521. }
  522. private SceneObjectPart m_LoopSoundMasterPrim = null;
  523. public SceneObjectPart LoopSoundMasterPrim
  524. {
  525. get { return m_LoopSoundMasterPrim; }
  526. set { m_LoopSoundMasterPrim = value; }
  527. }
  528. private List<SceneObjectPart> m_LoopSoundSlavePrims = new List<SceneObjectPart>();
  529. public List<SceneObjectPart> LoopSoundSlavePrims
  530. {
  531. get { return m_LoopSoundSlavePrims; }
  532. set { m_LoopSoundSlavePrims = value; }
  533. }
  534. /// <summary>
  535. /// The UUID for the region this object is in.
  536. /// </summary>
  537. public UUID RegionUUID
  538. {
  539. get
  540. {
  541. if (m_scene != null)
  542. {
  543. return m_scene.RegionInfo.RegionID;
  544. }
  545. return UUID.Zero;
  546. }
  547. }
  548. /// <summary>
  549. /// The item ID that this object was rezzed from, if applicable.
  550. /// </summary>
  551. /// <remarks>
  552. /// If not applicable will be UUID.Zero
  553. /// </remarks>
  554. public UUID FromItemID { get; set; }
  555. /// <summary>
  556. /// Refers to the SceneObjectPart.UUID property of the object that this object was rezzed from, if applicable.
  557. /// </summary>
  558. /// <remarks>
  559. /// If not applicable will be UUID.Zero
  560. /// </remarks>
  561. public UUID FromPartID { get; set; }
  562. /// <summary>
  563. /// The folder ID that this object was rezzed from, if applicable.
  564. /// </summary>
  565. /// <remarks>
  566. /// If not applicable will be UUID.Zero
  567. /// </remarks>
  568. public UUID FromFolderID { get; set; }
  569. /// <summary>
  570. /// IDs of all avatars sat on this scene object.
  571. /// </summary>
  572. /// <remarks>
  573. /// We need this so that we can maintain a linkset wide ordering of avatars sat on different parts.
  574. /// This must be locked before it is read or written.
  575. /// SceneObjectPart sitting avatar add/remove code also locks on this object to avoid race conditions.
  576. /// No avatar should appear more than once in this list.
  577. /// Do not manipulate this list directly - use the Add/Remove sitting avatar methods on SceneObjectPart.
  578. /// </remarks>
  579. protected internal List<UUID> m_sittingAvatars = new List<UUID>();
  580. #endregion
  581. // ~SceneObjectGroup()
  582. // {
  583. // //m_log.DebugFormat("[SCENE OBJECT GROUP]: Destructor called for {0}, local id {1}", Name, LocalId);
  584. // Console.WriteLine("Destructor called for {0}, local id {1}", Name, LocalId);
  585. // }
  586. #region Constructors
  587. /// <summary>
  588. /// Constructor
  589. /// </summary>
  590. public SceneObjectGroup()
  591. {
  592. }
  593. /// <summary>
  594. /// This constructor creates a SceneObjectGroup using a pre-existing SceneObjectPart.
  595. /// The original SceneObjectPart will be used rather than a copy, preserving
  596. /// its existing localID and UUID.
  597. /// </summary>
  598. /// <param name='part'>Root part for this scene object.</param>
  599. public SceneObjectGroup(SceneObjectPart part) : this()
  600. {
  601. SetRootPart(part);
  602. }
  603. /// <summary>
  604. /// Constructor. This object is added to the scene later via AttachToScene()
  605. /// </summary>
  606. public SceneObjectGroup(UUID ownerID, Vector3 pos, Quaternion rot, PrimitiveBaseShape shape)
  607. :this(new SceneObjectPart(ownerID, shape, pos, rot, Vector3.Zero))
  608. {
  609. }
  610. /// <summary>
  611. /// Constructor.
  612. /// </summary>
  613. public SceneObjectGroup(UUID ownerID, Vector3 pos, PrimitiveBaseShape shape)
  614. : this(ownerID, pos, Quaternion.Identity, shape)
  615. {
  616. }
  617. public void LoadScriptState(XmlDocument doc)
  618. {
  619. XmlNodeList nodes = doc.GetElementsByTagName("SavedScriptState");
  620. if (nodes.Count > 0)
  621. {
  622. if (m_savedScriptState == null)
  623. m_savedScriptState = new Dictionary<UUID, string>();
  624. foreach (XmlNode node in nodes)
  625. {
  626. if (node.Attributes["UUID"] != null)
  627. {
  628. UUID itemid = new UUID(node.Attributes["UUID"].Value);
  629. if (itemid != UUID.Zero)
  630. m_savedScriptState[itemid] = node.InnerXml;
  631. }
  632. }
  633. }
  634. }
  635. /// <summary>
  636. /// Hooks this object up to the backup event so that it is persisted to the database when the update thread executes.
  637. /// </summary>
  638. public virtual void AttachToBackup()
  639. {
  640. if (InSceneBackup)
  641. {
  642. //m_log.DebugFormat(
  643. // "[SCENE OBJECT GROUP]: Attaching object {0} {1} to scene presistence sweep", Name, UUID);
  644. if (!m_isBackedUp)
  645. m_scene.EventManager.OnBackup += ProcessBackup;
  646. m_isBackedUp = true;
  647. }
  648. }
  649. /// <summary>
  650. /// Attach this object to a scene. It will also now appear to agents.
  651. /// </summary>
  652. /// <param name="scene"></param>
  653. public void AttachToScene(Scene scene)
  654. {
  655. m_scene = scene;
  656. RegionHandle = m_scene.RegionInfo.RegionHandle;
  657. if (m_rootPart.Shape.PCode != 9 || m_rootPart.Shape.State == 0)
  658. m_rootPart.ParentID = 0;
  659. if (m_rootPart.LocalId == 0)
  660. m_rootPart.LocalId = m_scene.AllocateLocalId();
  661. SceneObjectPart[] parts = m_parts.GetArray();
  662. for (int i = 0; i < parts.Length; i++)
  663. {
  664. SceneObjectPart part = parts[i];
  665. if (Object.ReferenceEquals(part, m_rootPart))
  666. continue;
  667. if (part.LocalId == 0)
  668. part.LocalId = m_scene.AllocateLocalId();
  669. part.ParentID = m_rootPart.LocalId;
  670. //m_log.DebugFormat("[SCENE]: Given local id {0} to part {1}, linknum {2}, parent {3} {4}", part.LocalId, part.UUID, part.LinkNum, part.ParentID, part.ParentUUID);
  671. }
  672. ApplyPhysics();
  673. // Don't trigger the update here - otherwise some client issues occur when multiple updates are scheduled
  674. // for the same object with very different properties. The caller must schedule the update.
  675. //ScheduleGroupForFullUpdate();
  676. }
  677. public EntityIntersection TestIntersection(Ray hRay, bool frontFacesOnly, bool faceCenters)
  678. {
  679. // We got a request from the inner_scene to raytrace along the Ray hRay
  680. // We're going to check all of the prim in this group for intersection with the ray
  681. // If we get a result, we're going to find the closest result to the origin of the ray
  682. // and send back the intersection information back to the innerscene.
  683. EntityIntersection result = new EntityIntersection();
  684. SceneObjectPart[] parts = m_parts.GetArray();
  685. for (int i = 0; i < parts.Length; i++)
  686. {
  687. SceneObjectPart part = parts[i];
  688. // Temporary commented to stop compiler warning
  689. //Vector3 partPosition =
  690. // new Vector3(part.AbsolutePosition.X, part.AbsolutePosition.Y, part.AbsolutePosition.Z);
  691. Quaternion parentrotation = GroupRotation;
  692. // Telling the prim to raytrace.
  693. //EntityIntersection inter = part.TestIntersection(hRay, parentrotation);
  694. EntityIntersection inter = part.TestIntersectionOBB(hRay, parentrotation, frontFacesOnly, faceCenters);
  695. // This may need to be updated to the maximum draw distance possible..
  696. // We might (and probably will) be checking for prim creation from other sims
  697. // when the camera crosses the border.
  698. float idist = Constants.RegionSize;
  699. if (inter.HitTF)
  700. {
  701. // We need to find the closest prim to return to the testcaller along the ray
  702. if (inter.distance < idist)
  703. {
  704. result.HitTF = true;
  705. result.ipoint = inter.ipoint;
  706. result.obj = part;
  707. result.normal = inter.normal;
  708. result.distance = inter.distance;
  709. }
  710. }
  711. }
  712. return result;
  713. }
  714. /// <summary>
  715. /// Gets a vector representing the size of the bounding box containing all the prims in the group
  716. /// Treats all prims as rectangular, so no shape (cut etc) is taken into account
  717. /// offsetHeight is the offset in the Z axis from the centre of the bounding box to the centre of the root prim
  718. /// </summary>
  719. /// <returns></returns>
  720. public void GetAxisAlignedBoundingBoxRaw(out float minX, out float maxX, out float minY, out float maxY, out float minZ, out float maxZ)
  721. {
  722. maxX = -256f;
  723. maxY = -256f;
  724. maxZ = -256f;
  725. minX = 256f;
  726. minY = 256f;
  727. minZ = 8192f;
  728. SceneObjectPart[] parts = m_parts.GetArray();
  729. for (int i = 0; i < parts.Length; i++)
  730. {
  731. SceneObjectPart part = parts[i];
  732. Vector3 worldPos = part.GetWorldPosition();
  733. Vector3 offset = worldPos - AbsolutePosition;
  734. Quaternion worldRot;
  735. if (part.ParentID == 0)
  736. worldRot = part.RotationOffset;
  737. else
  738. worldRot = part.GetWorldRotation();
  739. Vector3 frontTopLeft;
  740. Vector3 frontTopRight;
  741. Vector3 frontBottomLeft;
  742. Vector3 frontBottomRight;
  743. Vector3 backTopLeft;
  744. Vector3 backTopRight;
  745. Vector3 backBottomLeft;
  746. Vector3 backBottomRight;
  747. Vector3 orig = Vector3.Zero;
  748. frontTopLeft.X = orig.X - (part.Scale.X / 2);
  749. frontTopLeft.Y = orig.Y - (part.Scale.Y / 2);
  750. frontTopLeft.Z = orig.Z + (part.Scale.Z / 2);
  751. frontTopRight.X = orig.X - (part.Scale.X / 2);
  752. frontTopRight.Y = orig.Y + (part.Scale.Y / 2);
  753. frontTopRight.Z = orig.Z + (part.Scale.Z / 2);
  754. frontBottomLeft.X = orig.X - (part.Scale.X / 2);
  755. frontBottomLeft.Y = orig.Y - (part.Scale.Y / 2);
  756. frontBottomLeft.Z = orig.Z - (part.Scale.Z / 2);
  757. frontBottomRight.X = orig.X - (part.Scale.X / 2);
  758. frontBottomRight.Y = orig.Y + (part.Scale.Y / 2);
  759. frontBottomRight.Z = orig.Z - (part.Scale.Z / 2);
  760. backTopLeft.X = orig.X + (part.Scale.X / 2);
  761. backTopLeft.Y = orig.Y - (part.Scale.Y / 2);
  762. backTopLeft.Z = orig.Z + (part.Scale.Z / 2);
  763. backTopRight.X = orig.X + (part.Scale.X / 2);
  764. backTopRight.Y = orig.Y + (part.Scale.Y / 2);
  765. backTopRight.Z = orig.Z + (part.Scale.Z / 2);
  766. backBottomLeft.X = orig.X + (part.Scale.X / 2);
  767. backBottomLeft.Y = orig.Y - (part.Scale.Y / 2);
  768. backBottomLeft.Z = orig.Z - (part.Scale.Z / 2);
  769. backBottomRight.X = orig.X + (part.Scale.X / 2);
  770. backBottomRight.Y = orig.Y + (part.Scale.Y / 2);
  771. backBottomRight.Z = orig.Z - (part.Scale.Z / 2);
  772. frontTopLeft = frontTopLeft * worldRot;
  773. frontTopRight = frontTopRight * worldRot;
  774. frontBottomLeft = frontBottomLeft * worldRot;
  775. frontBottomRight = frontBottomRight * worldRot;
  776. backBottomLeft = backBottomLeft * worldRot;
  777. backBottomRight = backBottomRight * worldRot;
  778. backTopLeft = backTopLeft * worldRot;
  779. backTopRight = backTopRight * worldRot;
  780. frontTopLeft += offset;
  781. frontTopRight += offset;
  782. frontBottomLeft += offset;
  783. frontBottomRight += offset;
  784. backBottomLeft += offset;
  785. backBottomRight += offset;
  786. backTopLeft += offset;
  787. backTopRight += offset;
  788. if (frontTopRight.X > maxX)
  789. maxX = frontTopRight.X;
  790. if (frontTopLeft.X > maxX)
  791. maxX = frontTopLeft.X;
  792. if (frontBottomRight.X > maxX)
  793. maxX = frontBottomRight.X;
  794. if (frontBottomLeft.X > maxX)
  795. maxX = frontBottomLeft.X;
  796. if (backTopRight.X > maxX)
  797. maxX = backTopRight.X;
  798. if (backTopLeft.X > maxX)
  799. maxX = backTopLeft.X;
  800. if (backBottomRight.X > maxX)
  801. maxX = backBottomRight.X;
  802. if (backBottomLeft.X > maxX)
  803. maxX = backBottomLeft.X;
  804. if (frontTopRight.X < minX)
  805. minX = frontTopRight.X;
  806. if (frontTopLeft.X < minX)
  807. minX = frontTopLeft.X;
  808. if (frontBottomRight.X < minX)
  809. minX = frontBottomRight.X;
  810. if (frontBottomLeft.X < minX)
  811. minX = frontBottomLeft.X;
  812. if (backTopRight.X < minX)
  813. minX = backTopRight.X;
  814. if (backTopLeft.X < minX)
  815. minX = backTopLeft.X;
  816. if (backBottomRight.X < minX)
  817. minX = backBottomRight.X;
  818. if (backBottomLeft.X < minX)
  819. minX = backBottomLeft.X;
  820. //
  821. if (frontTopRight.Y > maxY)
  822. maxY = frontTopRight.Y;
  823. if (frontTopLeft.Y > maxY)
  824. maxY = frontTopLeft.Y;
  825. if (frontBottomRight.Y > maxY)
  826. maxY = frontBottomRight.Y;
  827. if (frontBottomLeft.Y > maxY)
  828. maxY = frontBottomLeft.Y;
  829. if (backTopRight.Y > maxY)
  830. maxY = backTopRight.Y;
  831. if (backTopLeft.Y > maxY)
  832. maxY = backTopLeft.Y;
  833. if (backBottomRight.Y > maxY)
  834. maxY = backBottomRight.Y;
  835. if (backBottomLeft.Y > maxY)
  836. maxY = backBottomLeft.Y;
  837. if (frontTopRight.Y < minY)
  838. minY = frontTopRight.Y;
  839. if (frontTopLeft.Y < minY)
  840. minY = frontTopLeft.Y;
  841. if (frontBottomRight.Y < minY)
  842. minY = frontBottomRight.Y;
  843. if (frontBottomLeft.Y < minY)
  844. minY = frontBottomLeft.Y;
  845. if (backTopRight.Y < minY)
  846. minY = backTopRight.Y;
  847. if (backTopLeft.Y < minY)
  848. minY = backTopLeft.Y;
  849. if (backBottomRight.Y < minY)
  850. minY = backBottomRight.Y;
  851. if (backBottomLeft.Y < minY)
  852. minY = backBottomLeft.Y;
  853. //
  854. if (frontTopRight.Z > maxZ)
  855. maxZ = frontTopRight.Z;
  856. if (frontTopLeft.Z > maxZ)
  857. maxZ = frontTopLeft.Z;
  858. if (frontBottomRight.Z > maxZ)
  859. maxZ = frontBottomRight.Z;
  860. if (frontBottomLeft.Z > maxZ)
  861. maxZ = frontBottomLeft.Z;
  862. if (backTopRight.Z > maxZ)
  863. maxZ = backTopRight.Z;
  864. if (backTopLeft.Z > maxZ)
  865. maxZ = backTopLeft.Z;
  866. if (backBottomRight.Z > maxZ)
  867. maxZ = backBottomRight.Z;
  868. if (backBottomLeft.Z > maxZ)
  869. maxZ = backBottomLeft.Z;
  870. if (frontTopRight.Z < minZ)
  871. minZ = frontTopRight.Z;
  872. if (frontTopLeft.Z < minZ)
  873. minZ = frontTopLeft.Z;
  874. if (frontBottomRight.Z < minZ)
  875. minZ = frontBottomRight.Z;
  876. if (frontBottomLeft.Z < minZ)
  877. minZ = frontBottomLeft.Z;
  878. if (backTopRight.Z < minZ)
  879. minZ = backTopRight.Z;
  880. if (backTopLeft.Z < minZ)
  881. minZ = backTopLeft.Z;
  882. if (backBottomRight.Z < minZ)
  883. minZ = backBottomRight.Z;
  884. if (backBottomLeft.Z < minZ)
  885. minZ = backBottomLeft.Z;
  886. }
  887. }
  888. public Vector3 GetAxisAlignedBoundingBox(out float offsetHeight)
  889. {
  890. float minX;
  891. float maxX;
  892. float minY;
  893. float maxY;
  894. float minZ;
  895. float maxZ;
  896. GetAxisAlignedBoundingBoxRaw(out minX, out maxX, out minY, out maxY, out minZ, out maxZ);
  897. Vector3 boundingBox = new Vector3(maxX - minX, maxY - minY, maxZ - minZ);
  898. offsetHeight = 0;
  899. float lower = (minZ * -1);
  900. if (lower > maxZ)
  901. {
  902. offsetHeight = lower - (boundingBox.Z / 2);
  903. }
  904. else if (maxZ > lower)
  905. {
  906. offsetHeight = maxZ - (boundingBox.Z / 2);
  907. offsetHeight *= -1;
  908. }
  909. // m_log.InfoFormat("BoundingBox is {0} , {1} , {2} ", boundingBox.X, boundingBox.Y, boundingBox.Z);
  910. return boundingBox;
  911. }
  912. #endregion
  913. public void SaveScriptedState(XmlTextWriter writer)
  914. {
  915. XmlDocument doc = new XmlDocument();
  916. Dictionary<UUID,string> states = new Dictionary<UUID,string>();
  917. SceneObjectPart[] parts = m_parts.GetArray();
  918. for (int i = 0; i < parts.Length; i++)
  919. {
  920. Dictionary<UUID, string> pstates = parts[i].Inventory.GetScriptStates();
  921. foreach (KeyValuePair<UUID, string> kvp in pstates)
  922. states.Add(kvp.Key, kvp.Value);
  923. }
  924. if (states.Count > 0)
  925. {
  926. // Now generate the necessary XML wrappings
  927. writer.WriteStartElement(String.Empty, "GroupScriptStates", String.Empty);
  928. foreach (UUID itemid in states.Keys)
  929. {
  930. doc.LoadXml(states[itemid]);
  931. writer.WriteStartElement(String.Empty, "SavedScriptState", String.Empty);
  932. writer.WriteAttributeString(String.Empty, "UUID", String.Empty, itemid.ToString());
  933. writer.WriteRaw(doc.DocumentElement.OuterXml); // Writes ScriptState element
  934. writer.WriteEndElement(); // End of SavedScriptState
  935. }
  936. writer.WriteEndElement(); // End of GroupScriptStates
  937. }
  938. }
  939. /// <summary>
  940. ///
  941. /// </summary>
  942. /// <param name="part"></param>
  943. private void SetPartAsNonRoot(SceneObjectPart part)
  944. {
  945. part.ParentID = m_rootPart.LocalId;
  946. part.ClearUndoState();
  947. }
  948. public ushort GetTimeDilation()
  949. {
  950. return Utils.FloatToUInt16(m_scene.TimeDilation, 0.0f, 1.0f);
  951. }
  952. /// <summary>
  953. /// Set a part to act as the root part for this scene object
  954. /// </summary>
  955. /// <param name="part"></param>
  956. public void SetRootPart(SceneObjectPart part)
  957. {
  958. if (part == null)
  959. throw new ArgumentNullException("Cannot give SceneObjectGroup a null root SceneObjectPart");
  960. part.SetParent(this);
  961. m_rootPart = part;
  962. if (!IsAttachment)
  963. part.ParentID = 0;
  964. part.LinkNum = 0;
  965. m_parts.Add(m_rootPart.UUID, m_rootPart);
  966. }
  967. /// <summary>
  968. /// Add a new part to this scene object. The part must already be correctly configured.
  969. /// </summary>
  970. /// <param name="part"></param>
  971. public void AddPart(SceneObjectPart part)
  972. {
  973. part.SetParent(this);
  974. part.LinkNum = m_parts.Add(part.UUID, part);
  975. if (part.LinkNum == 2)
  976. RootPart.LinkNum = 1;
  977. }
  978. /// <summary>
  979. /// Make sure that every non root part has the proper parent root part local id
  980. /// </summary>
  981. private void UpdateParentIDs()
  982. {
  983. SceneObjectPart[] parts = m_parts.GetArray();
  984. for (int i = 0; i < parts.Length; i++)
  985. {
  986. SceneObjectPart part = parts[i];
  987. if (part.UUID != m_rootPart.UUID)
  988. part.ParentID = m_rootPart.LocalId;
  989. }
  990. }
  991. public void RegenerateFullIDs()
  992. {
  993. SceneObjectPart[] parts = m_parts.GetArray();
  994. for (int i = 0; i < parts.Length; i++)
  995. parts[i].UUID = UUID.Random();
  996. }
  997. // justincc: I don't believe this hack is needed any longer, especially since the physics
  998. // parts of set AbsolutePosition were already commented out. By changing HasGroupChanged to false
  999. // this method was preventing proper reload of scene objects.
  1000. // dahlia: I had to uncomment it, without it meshing was failing on some prims and objects
  1001. // at region startup
  1002. // teravus: After this was removed from the linking algorithm, Linked prims no longer collided
  1003. // properly when non-physical if they havn't been moved. This breaks ALL builds.
  1004. // see: http://opensimulator.org/mantis/view.php?id=3108
  1005. // Here's the deal, this is ABSOLUTELY CRITICAL so the physics scene gets the update about the
  1006. // position of linkset prims. IF YOU CHANGE THIS, YOU MUST TEST colliding with just linked and
  1007. // unmoved prims! As soon as you move a Prim/group, it will collide properly because Absolute
  1008. // Position has been set!
  1009. public void ResetChildPrimPhysicsPositions()
  1010. {
  1011. // Setting this SOG's absolute position also loops through and sets the positions
  1012. // of the SOP's in this SOG's linkset. This has the side affect of making sure
  1013. // the physics world matches the simulated world.
  1014. AbsolutePosition = AbsolutePosition; // could someone in the know please explain how this works?
  1015. // teravus: AbsolutePosition is NOT a normal property!
  1016. // the code in the getter of AbsolutePosition is significantly different then the code in the setter!
  1017. // jhurliman: Then why is it a property instead of two methods?
  1018. }
  1019. public UUID GetPartsFullID(uint localID)
  1020. {
  1021. SceneObjectPart part = GetPart(localID);
  1022. if (part != null)
  1023. {
  1024. return part.UUID;
  1025. }
  1026. return UUID.Zero;
  1027. }
  1028. public void ObjectGrabHandler(uint localId, Vector3 offsetPos, IClientAPI remoteClient)
  1029. {
  1030. if (m_rootPart.LocalId == localId)
  1031. {
  1032. OnGrabGroup(offsetPos, remoteClient);
  1033. }
  1034. else
  1035. {
  1036. SceneObjectPart part = GetPart(localId);
  1037. OnGrabPart(part, offsetPos, remoteClient);
  1038. }
  1039. }
  1040. public virtual void OnGrabPart(SceneObjectPart part, Vector3 offsetPos, IClientAPI remoteClient)
  1041. {
  1042. // m_log.DebugFormat(
  1043. // "[SCENE OBJECT GROUP]: Processing OnGrabPart for {0} on {1} {2}, offsetPos {3}",
  1044. // remoteClient.Name, part.Name, part.LocalId, offsetPos);
  1045. part.StoreUndoState();
  1046. part.OnGrab(offsetPos, remoteClient);
  1047. }
  1048. public virtual void OnGrabGroup(Vector3 offsetPos, IClientAPI remoteClient)
  1049. {
  1050. m_scene.EventManager.TriggerGroupGrab(UUID, offsetPos, remoteClient.AgentId);
  1051. }
  1052. /// <summary>
  1053. /// Delete this group from its scene.
  1054. /// </summary>
  1055. ///
  1056. /// This only handles the in-world consequences of deletion (e.g. any avatars sitting on it are forcibly stood
  1057. /// up and all avatars receive notification of its removal. Removal of the scene object from database backup
  1058. /// must be handled by the caller.
  1059. ///
  1060. /// <param name="silent">If true then deletion is not broadcast to clients</param>
  1061. public void DeleteGroupFromScene(bool silent)
  1062. {
  1063. SceneObjectPart[] parts = m_parts.GetArray();
  1064. for (int i = 0; i < parts.Length; i++)
  1065. {
  1066. SceneObjectPart part = parts[i];
  1067. Scene.ForEachRootScenePresence(delegate(ScenePresence avatar)
  1068. {
  1069. if (avatar.ParentID == LocalId)
  1070. avatar.StandUp();
  1071. if (!silent)
  1072. {
  1073. part.ClearUpdateSchedule();
  1074. if (part == m_rootPart)
  1075. {
  1076. if (!IsAttachment
  1077. || AttachedAvatar == avatar.ControllingClient.AgentId
  1078. || !HasPrivateAttachmentPoint)
  1079. avatar.ControllingClient.SendKillObject(m_regionHandle, new List<uint> { part.LocalId });
  1080. }
  1081. }
  1082. });
  1083. }
  1084. }
  1085. public void AddScriptLPS(int count)
  1086. {
  1087. m_scene.SceneGraph.AddToScriptLPS(count);
  1088. }
  1089. public void AddActiveScriptCount(int count)
  1090. {
  1091. SceneGraph d = m_scene.SceneGraph;
  1092. d.AddActiveScripts(count);
  1093. }
  1094. public void aggregateScriptEvents()
  1095. {
  1096. PrimFlags objectflagupdate = (PrimFlags)RootPart.GetEffectiveObjectFlags();
  1097. scriptEvents aggregateScriptEvents = 0;
  1098. SceneObjectPart[] parts = m_parts.GetArray();
  1099. for (int i = 0; i < parts.Length; i++)
  1100. {
  1101. SceneObjectPart part = parts[i];
  1102. if (part == null)
  1103. continue;
  1104. if (part != RootPart)
  1105. part.Flags = objectflagupdate;
  1106. aggregateScriptEvents |= part.AggregateScriptEvents;
  1107. }
  1108. m_scriptListens_atTarget = ((aggregateScriptEvents & scriptEvents.at_target) != 0);
  1109. m_scriptListens_notAtTarget = ((aggregateScriptEvents & scriptEvents.not_at_target) != 0);
  1110. if (!m_scriptListens_atTarget && !m_scriptListens_notAtTarget)
  1111. {
  1112. lock (m_targets)
  1113. m_targets.Clear();
  1114. m_scene.RemoveGroupTarget(this);
  1115. }
  1116. m_scriptListens_atRotTarget = ((aggregateScriptEvents & scriptEvents.at_rot_target) != 0);
  1117. m_scriptListens_notAtRotTarget = ((aggregateScriptEvents & scriptEvents.not_at_rot_target) != 0);
  1118. if (!m_scriptListens_atRotTarget && !m_scriptListens_notAtRotTarget)
  1119. {
  1120. lock (m_rotTargets)
  1121. m_rotTargets.Clear();
  1122. m_scene.RemoveGroupTarget(this);
  1123. }
  1124. ScheduleGroupForFullUpdate();
  1125. }
  1126. public void SetText(string text, Vector3 color, double alpha)
  1127. {
  1128. Color = Color.FromArgb(0xff - (int) (alpha * 0xff),
  1129. (int) (color.X * 0xff),
  1130. (int) (color.Y * 0xff),
  1131. (int) (color.Z * 0xff));
  1132. Text = text;
  1133. HasGroupChanged = true;
  1134. m_rootPart.ScheduleFullUpdate();
  1135. }
  1136. /// <summary>
  1137. /// Apply physics to this group
  1138. /// </summary>
  1139. public void ApplyPhysics()
  1140. {
  1141. // Apply physics to the root prim
  1142. m_rootPart.ApplyPhysics(m_rootPart.GetEffectiveObjectFlags(), m_rootPart.VolumeDetectActive);
  1143. // Apply physics to child prims
  1144. SceneObjectPart[] parts = m_parts.GetArray();
  1145. if (parts.Length > 1)
  1146. {
  1147. for (int i = 0; i

Large files files are truncated, but you can click here to view the full file