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