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