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