aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Framework/Scenes
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/Framework/Scenes')
-rw-r--r--OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs400
1 files changed, 277 insertions, 123 deletions
diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs
index 26a843e..fb34ddc 100644
--- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs
+++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs
@@ -98,6 +98,66 @@ namespace OpenSim.Region.Framework.Scenes
98 private bool m_hasGroupChanged = false; 98 private bool m_hasGroupChanged = false;
99 private long timeFirstChanged; 99 private long timeFirstChanged;
100 private long timeLastChanged; 100 private long timeLastChanged;
101 private System.Threading.ReaderWriterLockSlim m_partsLock = new System.Threading.ReaderWriterLockSlim();
102
103 public void lockPartsForRead(bool locked)
104 {
105 if (locked)
106 {
107 if (m_partsLock.RecursiveReadCount > 0)
108 {
109 m_log.Error("[SceneObjectGroup.m_parts] Recursive read lock requested. This should not happen and means something needs to be fixed. For now though, it's safe to continue.");
110 m_partsLock.ExitReadLock();
111 }
112 if (m_partsLock.RecursiveWriteCount > 0)
113 {
114 m_log.Error("[SceneObjectGroup.m_parts] Recursive read lock requested. This should not happen and means something needs to be fixed.");
115 m_partsLock.ExitWriteLock();
116 }
117
118 while (!m_partsLock.TryEnterReadLock(60000))
119 {
120 m_log.Error("[SceneObjectGroup.m_parts] Thread lock detected while trying to aquire READ lock of m_parts in SceneObjectGroup. I'm going to try to solve the thread lock automatically to preserve region stability, but this needs to be fixed.");
121 if (m_partsLock.IsWriteLockHeld)
122 {
123 m_partsLock = new System.Threading.ReaderWriterLockSlim();
124 }
125 }
126 }
127 else
128 {
129 m_partsLock.ExitReadLock();
130 }
131 }
132 public void lockPartsForWrite(bool locked)
133 {
134 if (locked)
135 {
136 if (m_partsLock.RecursiveReadCount > 0)
137 {
138 m_log.Error("[SceneObjectGroup.m_parts] Recursive write lock requested. This should not happen and means something needs to be fixed. For now though, it's safe to continue.");
139 m_partsLock.ExitReadLock();
140 }
141 if (m_partsLock.RecursiveWriteCount > 0)
142 {
143 m_log.Error("[SceneObjectGroup.m_parts] Recursive write lock requested. This should not happen and means something needs to be fixed.");
144 m_partsLock.ExitWriteLock();
145 }
146
147 while (!m_partsLock.TryEnterWriteLock(60000))
148 {
149 m_log.Error("[SceneObjectGroup.m_parts] Thread lock detected while trying to aquire WRITE lock of m_scripts in XEngine. I'm going to try to solve the thread lock automatically to preserve region stability, but this needs to be fixed.");
150 if (m_partsLock.IsWriteLockHeld)
151 {
152 m_partsLock = new System.Threading.ReaderWriterLockSlim();
153 }
154 }
155 }
156 else
157 {
158 m_partsLock.ExitWriteLock();
159 }
160 }
101 161
102 public bool HasGroupChanged 162 public bool HasGroupChanged
103 { 163 {
@@ -243,13 +303,16 @@ namespace OpenSim.Region.Framework.Scenes
243 set 303 set
244 { 304 {
245 m_regionHandle = value; 305 m_regionHandle = value;
246 lock (m_parts) 306 lockPartsForRead(true);
247 { 307 {
248 foreach (SceneObjectPart part in m_parts.Values) 308 foreach (SceneObjectPart part in m_parts.Values)
249 { 309 {
310
250 part.RegionHandle = m_regionHandle; 311 part.RegionHandle = m_regionHandle;
312
251 } 313 }
252 } 314 }
315 lockPartsForRead(false);
253 } 316 }
254 } 317 }
255 318
@@ -275,13 +338,16 @@ namespace OpenSim.Region.Framework.Scenes
275 m_scene.CrossPrimGroupIntoNewRegion(val, this, true); 338 m_scene.CrossPrimGroupIntoNewRegion(val, this, true);
276 } 339 }
277 340
278 lock (m_parts) 341 lockPartsForRead(true);
279 { 342 {
280 foreach (SceneObjectPart part in m_parts.Values) 343 foreach (SceneObjectPart part in m_parts.Values)
281 { 344 {
345
282 part.GroupPosition = val; 346 part.GroupPosition = val;
347
283 } 348 }
284 } 349 }
350 lockPartsForRead(false);
285 351
286 //if (m_rootPart.PhysActor != null) 352 //if (m_rootPart.PhysActor != null)
287 //{ 353 //{
@@ -432,13 +498,16 @@ namespace OpenSim.Region.Framework.Scenes
432 498
433 public void SetFromItemID(UUID AssetId) 499 public void SetFromItemID(UUID AssetId)
434 { 500 {
435 lock (m_parts) 501 lockPartsForRead(true);
436 { 502 {
437 foreach (SceneObjectPart part in m_parts.Values) 503 foreach (SceneObjectPart part in m_parts.Values)
438 { 504 {
505
439 part.FromItemID = AssetId; 506 part.FromItemID = AssetId;
507
440 } 508 }
441 } 509 }
510 lockPartsForRead(false);
442 } 511 }
443 512
444 public UUID GetFromItemID() 513 public UUID GetFromItemID()
@@ -505,10 +574,11 @@ namespace OpenSim.Region.Framework.Scenes
505 Vector3 maxScale = Vector3.Zero; 574 Vector3 maxScale = Vector3.Zero;
506 Vector3 finalScale = new Vector3(0.5f, 0.5f, 0.5f); 575 Vector3 finalScale = new Vector3(0.5f, 0.5f, 0.5f);
507 576
508 lock (m_parts) 577 lockPartsForRead(true);
509 { 578 {
510 foreach (SceneObjectPart part in m_parts.Values) 579 foreach (SceneObjectPart part in m_parts.Values)
511 { 580 {
581
512 Vector3 partscale = part.Scale; 582 Vector3 partscale = part.Scale;
513 Vector3 partoffset = part.OffsetPosition; 583 Vector3 partoffset = part.OffsetPosition;
514 584
@@ -519,8 +589,11 @@ namespace OpenSim.Region.Framework.Scenes
519 maxScale.X = (partscale.X + partoffset.X > maxScale.X) ? partscale.X + partoffset.X : maxScale.X; 589 maxScale.X = (partscale.X + partoffset.X > maxScale.X) ? partscale.X + partoffset.X : maxScale.X;
520 maxScale.Y = (partscale.Y + partoffset.Y > maxScale.Y) ? partscale.Y + partoffset.Y : maxScale.Y; 590 maxScale.Y = (partscale.Y + partoffset.Y > maxScale.Y) ? partscale.Y + partoffset.Y : maxScale.Y;
521 maxScale.Z = (partscale.Z + partoffset.Z > maxScale.Z) ? partscale.Z + partoffset.Z : maxScale.Z; 591 maxScale.Z = (partscale.Z + partoffset.Z > maxScale.Z) ? partscale.Z + partoffset.Z : maxScale.Z;
592
522 } 593 }
523 } 594 }
595 lockPartsForRead(false);
596
524 finalScale.X = (minScale.X > maxScale.X) ? minScale.X : maxScale.X; 597 finalScale.X = (minScale.X > maxScale.X) ? minScale.X : maxScale.X;
525 finalScale.Y = (minScale.Y > maxScale.Y) ? minScale.Y : maxScale.Y; 598 finalScale.Y = (minScale.Y > maxScale.Y) ? minScale.Y : maxScale.Y;
526 finalScale.Z = (minScale.Z > maxScale.Z) ? minScale.Z : maxScale.Z; 599 finalScale.Z = (minScale.Z > maxScale.Z) ? minScale.Z : maxScale.Z;
@@ -536,10 +609,11 @@ namespace OpenSim.Region.Framework.Scenes
536 609
537 EntityIntersection result = new EntityIntersection(); 610 EntityIntersection result = new EntityIntersection();
538 611
539 lock (m_parts) 612 lockPartsForRead(true);
540 { 613 {
541 foreach (SceneObjectPart part in m_parts.Values) 614 foreach (SceneObjectPart part in m_parts.Values)
542 { 615 {
616
543 // Temporary commented to stop compiler warning 617 // Temporary commented to stop compiler warning
544 //Vector3 partPosition = 618 //Vector3 partPosition =
545 // new Vector3(part.AbsolutePosition.X, part.AbsolutePosition.Y, part.AbsolutePosition.Z); 619 // new Vector3(part.AbsolutePosition.X, part.AbsolutePosition.Y, part.AbsolutePosition.Z);
@@ -567,8 +641,10 @@ namespace OpenSim.Region.Framework.Scenes
567 result.distance = inter.distance; 641 result.distance = inter.distance;
568 } 642 }
569 } 643 }
644
570 } 645 }
571 } 646 }
647 lockPartsForRead(false);
572 return result; 648 return result;
573 } 649 }
574 650
@@ -581,10 +657,11 @@ namespace OpenSim.Region.Framework.Scenes
581 public Vector3 GetAxisAlignedBoundingBox(out float offsetHeight) 657 public Vector3 GetAxisAlignedBoundingBox(out float offsetHeight)
582 { 658 {
583 float maxX = -256f, maxY = -256f, maxZ = -256f, minX = 256f, minY = 256f, minZ = 256f; 659 float maxX = -256f, maxY = -256f, maxZ = -256f, minX = 256f, minY = 256f, minZ = 256f;
584 lock (m_parts) 660 lockPartsForRead(true);
585 { 661 {
586 foreach (SceneObjectPart part in m_parts.Values) 662 foreach (SceneObjectPart part in m_parts.Values)
587 { 663 {
664
588 Vector3 worldPos = part.GetWorldPosition(); 665 Vector3 worldPos = part.GetWorldPosition();
589 Vector3 offset = worldPos - AbsolutePosition; 666 Vector3 offset = worldPos - AbsolutePosition;
590 Quaternion worldRot; 667 Quaternion worldRot;
@@ -643,6 +720,8 @@ namespace OpenSim.Region.Framework.Scenes
643 backBottomRight.Y = orig.Y + (part.Scale.Y / 2); 720 backBottomRight.Y = orig.Y + (part.Scale.Y / 2);
644 backBottomRight.Z = orig.Z - (part.Scale.Z / 2); 721 backBottomRight.Z = orig.Z - (part.Scale.Z / 2);
645 722
723
724
646 //m_log.InfoFormat("pre corner 1 is {0} {1} {2}", frontTopLeft.X, frontTopLeft.Y, frontTopLeft.Z); 725 //m_log.InfoFormat("pre corner 1 is {0} {1} {2}", frontTopLeft.X, frontTopLeft.Y, frontTopLeft.Z);
647 //m_log.InfoFormat("pre corner 2 is {0} {1} {2}", frontTopRight.X, frontTopRight.Y, frontTopRight.Z); 726 //m_log.InfoFormat("pre corner 2 is {0} {1} {2}", frontTopRight.X, frontTopRight.Y, frontTopRight.Z);
648 //m_log.InfoFormat("pre corner 3 is {0} {1} {2}", frontBottomRight.X, frontBottomRight.Y, frontBottomRight.Z); 727 //m_log.InfoFormat("pre corner 3 is {0} {1} {2}", frontBottomRight.X, frontBottomRight.Y, frontBottomRight.Z);
@@ -814,6 +893,7 @@ namespace OpenSim.Region.Framework.Scenes
814 minZ = backBottomLeft.Z; 893 minZ = backBottomLeft.Z;
815 } 894 }
816 } 895 }
896 lockPartsForRead(false);
817 897
818 Vector3 boundingBox = new Vector3(maxX - minX, maxY - minY, maxZ - minZ); 898 Vector3 boundingBox = new Vector3(maxX - minX, maxY - minY, maxZ - minZ);
819 899
@@ -842,17 +922,20 @@ namespace OpenSim.Region.Framework.Scenes
842 Dictionary<UUID,string> states = new Dictionary<UUID,string>(); 922 Dictionary<UUID,string> states = new Dictionary<UUID,string>();
843 923
844 // Capture script state while holding the lock 924 // Capture script state while holding the lock
845 lock (m_parts) 925 lockPartsForRead(true);
846 { 926 {
847 foreach (SceneObjectPart part in m_parts.Values) 927 foreach (SceneObjectPart part in m_parts.Values)
848 { 928 {
929
849 Dictionary<UUID,string> pstates = part.Inventory.GetScriptStates(); 930 Dictionary<UUID,string> pstates = part.Inventory.GetScriptStates();
850 foreach (UUID itemid in pstates.Keys) 931 foreach (UUID itemid in pstates.Keys)
851 { 932 {
852 states.Add(itemid, pstates[itemid]); 933 states.Add(itemid, pstates[itemid]);
853 } 934 }
935
854 } 936 }
855 } 937 }
938 lockPartsForRead(false);
856 939
857 if (states.Count > 0) 940 if (states.Count > 0)
858 { 941 {
@@ -1014,13 +1097,16 @@ namespace OpenSim.Region.Framework.Scenes
1014 1097
1015 public override void UpdateMovement() 1098 public override void UpdateMovement()
1016 { 1099 {
1017 lock (m_parts) 1100 lockPartsForRead(true);
1018 { 1101 {
1019 foreach (SceneObjectPart part in m_parts.Values) 1102 foreach (SceneObjectPart part in m_parts.Values)
1020 { 1103 {
1104
1021 part.UpdateMovement(); 1105 part.UpdateMovement();
1106
1022 } 1107 }
1023 } 1108 }
1109 lockPartsForRead(false);
1024 } 1110 }
1025 1111
1026 public ushort GetTimeDilation() 1112 public ushort GetTimeDilation()
@@ -1064,7 +1150,7 @@ namespace OpenSim.Region.Framework.Scenes
1064 /// <param name="part"></param> 1150 /// <param name="part"></param>
1065 public void AddPart(SceneObjectPart part) 1151 public void AddPart(SceneObjectPart part)
1066 { 1152 {
1067 lock (m_parts) 1153 lockPartsForWrite(true);
1068 { 1154 {
1069 part.SetParent(this); 1155 part.SetParent(this);
1070 m_parts.Add(part.UUID, part); 1156 m_parts.Add(part.UUID, part);
@@ -1074,6 +1160,7 @@ namespace OpenSim.Region.Framework.Scenes
1074 if (part.LinkNum == 2 && RootPart != null) 1160 if (part.LinkNum == 2 && RootPart != null)
1075 RootPart.LinkNum = 1; 1161 RootPart.LinkNum = 1;
1076 } 1162 }
1163 lockPartsForWrite(false);
1077 } 1164 }
1078 1165
1079 /// <summary> 1166 /// <summary>
@@ -1081,28 +1168,33 @@ namespace OpenSim.Region.Framework.Scenes
1081 /// </summary> 1168 /// </summary>
1082 private void UpdateParentIDs() 1169 private void UpdateParentIDs()
1083 { 1170 {
1084 lock (m_parts) 1171 lockPartsForRead(true);
1085 { 1172 {
1086 foreach (SceneObjectPart part in m_parts.Values) 1173 foreach (SceneObjectPart part in m_parts.Values)
1087 { 1174 {
1175
1088 if (part.UUID != m_rootPart.UUID) 1176 if (part.UUID != m_rootPart.UUID)
1089 { 1177 {
1090 part.ParentID = m_rootPart.LocalId; 1178 part.ParentID = m_rootPart.LocalId;
1091 } 1179 }
1180
1092 } 1181 }
1093 } 1182 }
1183 lockPartsForRead(false);
1094 } 1184 }
1095 1185
1096 public void RegenerateFullIDs() 1186 public void RegenerateFullIDs()
1097 { 1187 {
1098 lock (m_parts) 1188 lockPartsForRead(true);
1099 { 1189 {
1100 foreach (SceneObjectPart part in m_parts.Values) 1190 foreach (SceneObjectPart part in m_parts.Values)
1101 { 1191 {
1192
1102 part.UUID = UUID.Random(); 1193 part.UUID = UUID.Random();
1103 1194
1104 } 1195 }
1105 } 1196 }
1197 lockPartsForRead(false);
1106 } 1198 }
1107 1199
1108 // helper provided for parts. 1200 // helper provided for parts.
@@ -1183,29 +1275,33 @@ namespace OpenSim.Region.Framework.Scenes
1183 1275
1184 DetachFromBackup(); 1276 DetachFromBackup();
1185 1277
1186 lock (m_parts) 1278 lockPartsForRead(true);
1279 List<SceneObjectPart> values = new List<SceneObjectPart>(m_parts.Values);
1280 lockPartsForRead(false);
1281
1282 foreach (SceneObjectPart part in values)
1187 { 1283 {
1188 foreach (SceneObjectPart part in m_parts.Values)
1189 {
1190// part.Inventory.RemoveScriptInstances(); 1284// part.Inventory.RemoveScriptInstances();
1191 1285
1192 ScenePresence[] avatars = Scene.GetScenePresences(); 1286 ScenePresence[] avatars = Scene.GetScenePresences();
1193 for (int i = 0; i < avatars.Length; i++) 1287 for (int i = 0; i < avatars.Length; i++)
1288 {
1289 if (avatars[i].ParentID == LocalId)
1194 { 1290 {
1195 if (avatars[i].ParentID == LocalId) 1291 avatars[i].StandUp();
1196 { 1292 }
1197 avatars[i].StandUp();
1198 }
1199 1293
1200 if (!silent) 1294 if (!silent)
1201 { 1295 {
1202 part.UpdateFlag = 0; 1296 part.UpdateFlag = 0;
1203 if (part == m_rootPart) 1297 if (part == m_rootPart)
1204 avatars[i].ControllingClient.SendKillObject(m_regionHandle, part.LocalId); 1298 avatars[i].ControllingClient.SendKillObject(m_regionHandle, part.LocalId);
1205 }
1206 } 1299 }
1207 } 1300 }
1301
1208 } 1302 }
1303
1304
1209 } 1305 }
1210 1306
1211 public void AddScriptLPS(int count) 1307 public void AddScriptLPS(int count)
@@ -1230,17 +1326,20 @@ namespace OpenSim.Region.Framework.Scenes
1230 1326
1231 scriptEvents aggregateScriptEvents=0; 1327 scriptEvents aggregateScriptEvents=0;
1232 1328
1233 lock (m_parts) 1329 lockPartsForRead(true);
1234 { 1330 {
1235 foreach (SceneObjectPart part in m_parts.Values) 1331 foreach (SceneObjectPart part in m_parts.Values)
1236 { 1332 {
1333
1237 if (part == null) 1334 if (part == null)
1238 continue; 1335 continue;
1239 if (part != RootPart) 1336 if (part != RootPart)
1240 part.ObjectFlags = objectflagupdate; 1337 part.ObjectFlags = objectflagupdate;
1241 aggregateScriptEvents |= part.AggregateScriptEvents; 1338 aggregateScriptEvents |= part.AggregateScriptEvents;
1339
1242 } 1340 }
1243 } 1341 }
1342 lockPartsForRead(false);
1244 1343
1245 m_scriptListens_atTarget = ((aggregateScriptEvents & scriptEvents.at_target) != 0); 1344 m_scriptListens_atTarget = ((aggregateScriptEvents & scriptEvents.at_target) != 0);
1246 m_scriptListens_notAtTarget = ((aggregateScriptEvents & scriptEvents.not_at_target) != 0); 1345 m_scriptListens_notAtTarget = ((aggregateScriptEvents & scriptEvents.not_at_target) != 0);
@@ -1273,42 +1372,52 @@ namespace OpenSim.Region.Framework.Scenes
1273 /// <param name="m_physicalPrim"></param> 1372 /// <param name="m_physicalPrim"></param>
1274 public void ApplyPhysics(bool m_physicalPrim) 1373 public void ApplyPhysics(bool m_physicalPrim)
1275 { 1374 {
1276 lock (m_parts) 1375 lockPartsForRead(true);
1376
1377 if (m_parts.Count > 1)
1277 { 1378 {
1278 if (m_parts.Count > 1) 1379 List<SceneObjectPart> values = new List<SceneObjectPart>(m_parts.Values);
1380 lockPartsForRead(false);
1381 m_rootPart.ApplyPhysics(m_rootPart.GetEffectiveObjectFlags(), m_rootPart.VolumeDetectActive, m_physicalPrim);
1382 foreach (SceneObjectPart part in values)
1279 { 1383 {
1280 m_rootPart.ApplyPhysics(m_rootPart.GetEffectiveObjectFlags(), m_rootPart.VolumeDetectActive, m_physicalPrim); 1384
1281 foreach (SceneObjectPart part in m_parts.Values) 1385 if (part.LocalId != m_rootPart.LocalId)
1282 { 1386 {
1283 if (part.LocalId != m_rootPart.LocalId) 1387 part.ApplyPhysics(m_rootPart.GetEffectiveObjectFlags(), part.VolumeDetectActive, m_physicalPrim);
1284 {
1285 part.ApplyPhysics(m_rootPart.GetEffectiveObjectFlags(), part.VolumeDetectActive, m_physicalPrim);
1286 }
1287 } 1388 }
1288 1389
1289 // Hack to get the physics scene geometries in the right spot
1290 ResetChildPrimPhysicsPositions();
1291 }
1292 else
1293 {
1294 m_rootPart.ApplyPhysics(m_rootPart.GetEffectiveObjectFlags(), m_rootPart.VolumeDetectActive, m_physicalPrim);
1295 } 1390 }
1391 // Hack to get the physics scene geometries in the right spot
1392 ResetChildPrimPhysicsPositions();
1393 }
1394 else
1395 {
1396 lockPartsForRead(false);
1397 m_rootPart.ApplyPhysics(m_rootPart.GetEffectiveObjectFlags(), m_rootPart.VolumeDetectActive, m_physicalPrim);
1296 } 1398 }
1297 } 1399 }
1298 1400
1299 public void SetOwnerId(UUID userId) 1401 public void SetOwnerId(UUID userId)
1300 { 1402 {
1301 ForEachPart(delegate(SceneObjectPart part) { part.OwnerID = userId; }); 1403 ForEachPart(delegate(SceneObjectPart part)
1404 {
1405
1406 part.OwnerID = userId;
1407
1408 });
1302 } 1409 }
1303 1410
1304 public void ForEachPart(Action<SceneObjectPart> whatToDo) 1411 public void ForEachPart(Action<SceneObjectPart> whatToDo)
1305 { 1412 {
1306 lock (m_parts) 1413 lockPartsForRead(true);
1414 List<SceneObjectPart> values = new List<SceneObjectPart>(m_parts.Values);
1415 lockPartsForRead(false);
1416 foreach (SceneObjectPart part in values)
1307 { 1417 {
1308 foreach (SceneObjectPart part in m_parts.Values) 1418
1309 { 1419 whatToDo(part);
1310 whatToDo(part); 1420
1311 }
1312 } 1421 }
1313 } 1422 }
1314 1423
@@ -1407,14 +1516,17 @@ namespace OpenSim.Region.Framework.Scenes
1407 { 1516 {
1408 SendPartFullUpdate(remoteClient, RootPart, m_scene.Permissions.GenerateClientFlags(remoteClient.AgentId, RootPart.UUID)); 1517 SendPartFullUpdate(remoteClient, RootPart, m_scene.Permissions.GenerateClientFlags(remoteClient.AgentId, RootPart.UUID));
1409 1518
1410 lock (m_parts) 1519 lockPartsForRead(true);
1411 { 1520 {
1412 foreach (SceneObjectPart part in m_parts.Values) 1521 foreach (SceneObjectPart part in m_parts.Values)
1413 { 1522 {
1523
1414 if (part != RootPart) 1524 if (part != RootPart)
1415 SendPartFullUpdate(remoteClient, part, m_scene.Permissions.GenerateClientFlags(remoteClient.AgentId, part.UUID)); 1525 SendPartFullUpdate(remoteClient, part, m_scene.Permissions.GenerateClientFlags(remoteClient.AgentId, part.UUID));
1526
1416 } 1527 }
1417 } 1528 }
1529 lockPartsForRead(false);
1418 } 1530 }
1419 1531
1420 /// <summary> 1532 /// <summary>
@@ -1509,10 +1621,11 @@ namespace OpenSim.Region.Framework.Scenes
1509 1621
1510 List<SceneObjectPart> partList; 1622 List<SceneObjectPart> partList;
1511 1623
1512 lock (m_parts) 1624 lockPartsForRead(true);
1513 { 1625
1514 partList = new List<SceneObjectPart>(m_parts.Values); 1626 partList = new List<SceneObjectPart>(m_parts.Values);
1515 } 1627
1628 lockPartsForRead(false);
1516 1629
1517 partList.Sort(delegate(SceneObjectPart p1, SceneObjectPart p2) 1630 partList.Sort(delegate(SceneObjectPart p1, SceneObjectPart p2)
1518 { 1631 {
@@ -1835,10 +1948,11 @@ namespace OpenSim.Region.Framework.Scenes
1835 SceneObjectPart newPart = part.Copy(m_scene.AllocateLocalId(), OwnerID, GroupID, m_parts.Count, userExposed); 1948 SceneObjectPart newPart = part.Copy(m_scene.AllocateLocalId(), OwnerID, GroupID, m_parts.Count, userExposed);
1836 newPart.SetParent(this); 1949 newPart.SetParent(this);
1837 1950
1838 lock (m_parts) 1951 lockPartsForWrite(true);
1839 { 1952 {
1840 m_parts.Add(newPart.UUID, newPart); 1953 m_parts.Add(newPart.UUID, newPart);
1841 } 1954 }
1955 lockPartsForWrite(false);
1842 1956
1843 SetPartAsNonRoot(newPart); 1957 SetPartAsNonRoot(newPart);
1844 1958
@@ -1901,7 +2015,7 @@ namespace OpenSim.Region.Framework.Scenes
1901 //if ((RootPart.Flags & PrimFlags.TemporaryOnRez) != 0) 2015 //if ((RootPart.Flags & PrimFlags.TemporaryOnRez) != 0)
1902 // return; 2016 // return;
1903 2017
1904 lock (m_parts) 2018 lockPartsForRead(true);
1905 { 2019 {
1906 bool UsePhysics = ((RootPart.Flags & PrimFlags.Physics) != 0); 2020 bool UsePhysics = ((RootPart.Flags & PrimFlags.Physics) != 0);
1907 2021
@@ -1919,34 +2033,43 @@ namespace OpenSim.Region.Framework.Scenes
1919 2033
1920 foreach (SceneObjectPart part in m_parts.Values) 2034 foreach (SceneObjectPart part in m_parts.Values)
1921 { 2035 {
2036
1922 part.SendScheduledUpdates(); 2037 part.SendScheduledUpdates();
2038
1923 } 2039 }
1924 } 2040 }
2041 lockPartsForRead(false);
1925 } 2042 }
1926 2043
1927 public void ScheduleFullUpdateToAvatar(ScenePresence presence) 2044 public void ScheduleFullUpdateToAvatar(ScenePresence presence)
1928 { 2045 {
1929 RootPart.AddFullUpdateToAvatar(presence); 2046 RootPart.AddFullUpdateToAvatar(presence);
1930 2047
1931 lock (m_parts) 2048 lockPartsForRead(true);
1932 { 2049 {
1933 foreach (SceneObjectPart part in m_parts.Values) 2050 foreach (SceneObjectPart part in m_parts.Values)
1934 { 2051 {
2052
1935 if (part != RootPart) 2053 if (part != RootPart)
1936 part.AddFullUpdateToAvatar(presence); 2054 part.AddFullUpdateToAvatar(presence);
2055
1937 } 2056 }
1938 } 2057 }
2058 lockPartsForRead(false);
1939 } 2059 }
1940 2060
1941 public void ScheduleTerseUpdateToAvatar(ScenePresence presence) 2061 public void ScheduleTerseUpdateToAvatar(ScenePresence presence)
1942 { 2062 {
1943 lock (m_parts) 2063 lockPartsForRead(true);
1944 { 2064 {
1945 foreach (SceneObjectPart part in m_parts.Values) 2065 foreach (SceneObjectPart part in m_parts.Values)
1946 { 2066 {
2067
1947 part.AddTerseUpdateToAvatar(presence); 2068 part.AddTerseUpdateToAvatar(presence);
2069
1948 } 2070 }
1949 } 2071 }
2072 lockPartsForRead(false);
1950 } 2073 }
1951 2074
1952 /// <summary> 2075 /// <summary>
@@ -1957,14 +2080,17 @@ namespace OpenSim.Region.Framework.Scenes
1957 checkAtTargets(); 2080 checkAtTargets();
1958 RootPart.ScheduleFullUpdate(); 2081 RootPart.ScheduleFullUpdate();
1959 2082
1960 lock (m_parts) 2083 lockPartsForRead(true);
1961 { 2084 {
1962 foreach (SceneObjectPart part in m_parts.Values) 2085 foreach (SceneObjectPart part in m_parts.Values)
1963 { 2086 {
2087
1964 if (part != RootPart) 2088 if (part != RootPart)
1965 part.ScheduleFullUpdate(); 2089 part.ScheduleFullUpdate();
2090
1966 } 2091 }
1967 } 2092 }
2093 lockPartsForRead(false);
1968 } 2094 }
1969 2095
1970 /// <summary> 2096 /// <summary>
@@ -1972,13 +2098,16 @@ namespace OpenSim.Region.Framework.Scenes
1972 /// </summary> 2098 /// </summary>
1973 public void ScheduleGroupForTerseUpdate() 2099 public void ScheduleGroupForTerseUpdate()
1974 { 2100 {
1975 lock (m_parts) 2101 lockPartsForRead(true);
1976 { 2102 {
1977 foreach (SceneObjectPart part in m_parts.Values) 2103 foreach (SceneObjectPart part in m_parts.Values)
1978 { 2104 {
2105
1979 part.ScheduleTerseUpdate(); 2106 part.ScheduleTerseUpdate();
2107
1980 } 2108 }
1981 } 2109 }
2110 lockPartsForRead(false);
1982 } 2111 }
1983 2112
1984 /// <summary> 2113 /// <summary>
@@ -1991,14 +2120,17 @@ namespace OpenSim.Region.Framework.Scenes
1991 2120
1992 RootPart.SendFullUpdateToAllClients(); 2121 RootPart.SendFullUpdateToAllClients();
1993 2122
1994 lock (m_parts) 2123 lockPartsForRead(true);
1995 { 2124 {
1996 foreach (SceneObjectPart part in m_parts.Values) 2125 foreach (SceneObjectPart part in m_parts.Values)
1997 { 2126 {
2127
1998 if (part != RootPart) 2128 if (part != RootPart)
1999 part.SendFullUpdateToAllClients(); 2129 part.SendFullUpdateToAllClients();
2130
2000 } 2131 }
2001 } 2132 }
2133 lockPartsForRead(false);
2002 } 2134 }
2003 2135
2004 /// <summary> 2136 /// <summary>
@@ -2029,14 +2161,15 @@ namespace OpenSim.Region.Framework.Scenes
2029 { 2161 {
2030 if (IsDeleted) 2162 if (IsDeleted)
2031 return; 2163 return;
2032 2164
2033 lock (m_parts) 2165 lockPartsForRead(true);
2034 { 2166 {
2035 foreach (SceneObjectPart part in m_parts.Values) 2167 foreach (SceneObjectPart part in m_parts.Values)
2036 { 2168 {
2037 part.SendTerseUpdateToAllClients(); 2169 part.SendTerseUpdateToAllClients();
2038 } 2170 }
2039 } 2171 }
2172 lockPartsForRead(false);
2040 } 2173 }
2041 2174
2042 #endregion 2175 #endregion
@@ -2050,16 +2183,18 @@ namespace OpenSim.Region.Framework.Scenes
2050 /// <returns>null if no child part with that linknum or child part</returns> 2183 /// <returns>null if no child part with that linknum or child part</returns>
2051 public SceneObjectPart GetLinkNumPart(int linknum) 2184 public SceneObjectPart GetLinkNumPart(int linknum)
2052 { 2185 {
2053 lock (m_parts) 2186 lockPartsForRead(true);
2054 { 2187 {
2055 foreach (SceneObjectPart part in m_parts.Values) 2188 foreach (SceneObjectPart part in m_parts.Values)
2056 { 2189 {
2057 if (part.LinkNum == linknum) 2190 if (part.LinkNum == linknum)
2058 { 2191 {
2192 lockPartsForRead(false);
2059 return part; 2193 return part;
2060 } 2194 }
2061 } 2195 }
2062 } 2196 }
2197 lockPartsForRead(false);
2063 2198
2064 return null; 2199 return null;
2065 } 2200 }
@@ -2087,17 +2222,19 @@ namespace OpenSim.Region.Framework.Scenes
2087 public SceneObjectPart GetChildPart(uint localID) 2222 public SceneObjectPart GetChildPart(uint localID)
2088 { 2223 {
2089 //m_log.DebugFormat("Entered looking for {0}", localID); 2224 //m_log.DebugFormat("Entered looking for {0}", localID);
2090 lock (m_parts) 2225 lockPartsForRead(true);
2091 { 2226 {
2092 foreach (SceneObjectPart part in m_parts.Values) 2227 foreach (SceneObjectPart part in m_parts.Values)
2093 { 2228 {
2094 //m_log.DebugFormat("Found {0}", part.LocalId); 2229 //m_log.DebugFormat("Found {0}", part.LocalId);
2095 if (part.LocalId == localID) 2230 if (part.LocalId == localID)
2096 { 2231 {
2232 lockPartsForRead(false);
2097 return part; 2233 return part;
2098 } 2234 }
2099 } 2235 }
2100 } 2236 }
2237 lockPartsForRead(false);
2101 2238
2102 return null; 2239 return null;
2103 } 2240 }
@@ -2127,17 +2264,19 @@ namespace OpenSim.Region.Framework.Scenes
2127 public bool HasChildPrim(uint localID) 2264 public bool HasChildPrim(uint localID)
2128 { 2265 {
2129 //m_log.DebugFormat("Entered HasChildPrim looking for {0}", localID); 2266 //m_log.DebugFormat("Entered HasChildPrim looking for {0}", localID);
2130 lock (m_parts) 2267 lockPartsForRead(true);
2131 { 2268 {
2132 foreach (SceneObjectPart part in m_parts.Values) 2269 foreach (SceneObjectPart part in m_parts.Values)
2133 { 2270 {
2134 //m_log.DebugFormat("Found {0}", part.LocalId); 2271 //m_log.DebugFormat("Found {0}", part.LocalId);
2135 if (part.LocalId == localID) 2272 if (part.LocalId == localID)
2136 { 2273 {
2274 lockPartsForRead(false);
2137 return true; 2275 return true;
2138 } 2276 }
2139 } 2277 }
2140 } 2278 }
2279 lockPartsForRead(false);
2141 2280
2142 return false; 2281 return false;
2143 } 2282 }
@@ -2187,53 +2326,57 @@ namespace OpenSim.Region.Framework.Scenes
2187 if (m_rootPart.LinkNum == 0) 2326 if (m_rootPart.LinkNum == 0)
2188 m_rootPart.LinkNum = 1; 2327 m_rootPart.LinkNum = 1;
2189 2328
2190 lock (m_parts) 2329 lockPartsForWrite(true);
2191 { 2330
2192 m_parts.Add(linkPart.UUID, linkPart); 2331 m_parts.Add(linkPart.UUID, linkPart);
2332
2333 lockPartsForWrite(false);
2193 2334
2194 // Insert in terms of link numbers, the new links 2335 // Insert in terms of link numbers, the new links
2195 // before the current ones (with the exception of 2336 // before the current ones (with the exception of
2196 // the root prim. Shuffle the old ones up 2337 // the root prim. Shuffle the old ones up
2197 foreach (KeyValuePair<UUID, SceneObjectPart> kvp in m_parts) 2338 lockPartsForRead(true);
2339 foreach (KeyValuePair<UUID, SceneObjectPart> kvp in m_parts)
2340 {
2341 if (kvp.Value.LinkNum != 1)
2198 { 2342 {
2199 if (kvp.Value.LinkNum != 1) 2343 // Don't update root prim link number
2200 { 2344 kvp.Value.LinkNum += objectGroup.PrimCount;
2201 // Don't update root prim link number
2202 kvp.Value.LinkNum += objectGroup.PrimCount;
2203 }
2204 } 2345 }
2346 }
2347 lockPartsForRead(false);
2205 2348
2206 linkPart.LinkNum = 2; 2349 linkPart.LinkNum = 2;
2207 2350
2208 linkPart.SetParent(this); 2351 linkPart.SetParent(this);
2209 linkPart.AddFlag(PrimFlags.CreateSelected); 2352 linkPart.AddFlag(PrimFlags.CreateSelected);
2210 2353
2211 //if (linkPart.PhysActor != null) 2354 //if (linkPart.PhysActor != null)
2212 //{ 2355 //{
2213 // m_scene.PhysicsScene.RemovePrim(linkPart.PhysActor); 2356 // m_scene.PhysicsScene.RemovePrim(linkPart.PhysActor);
2214 2357
2215 //linkPart.PhysActor = null; 2358 //linkPart.PhysActor = null;
2216 //} 2359 //}
2217 2360
2218 //TODO: rest of parts 2361 //TODO: rest of parts
2219 int linkNum = 3; 2362 int linkNum = 3;
2220 foreach (SceneObjectPart part in objectGroup.Children.Values) 2363 foreach (SceneObjectPart part in objectGroup.Children.Values)
2364 {
2365 if (part.UUID != objectGroup.m_rootPart.UUID)
2221 { 2366 {
2222 if (part.UUID != objectGroup.m_rootPart.UUID) 2367 LinkNonRootPart(part, oldGroupPosition, oldRootRotation, linkNum++);
2223 {
2224 LinkNonRootPart(part, oldGroupPosition, oldRootRotation, linkNum++);
2225 }
2226 part.ClearUndoState();
2227 } 2368 }
2369 part.ClearUndoState();
2228 } 2370 }
2229 2371
2230 m_scene.UnlinkSceneObject(objectGroup.UUID, true); 2372 m_scene.UnlinkSceneObject(objectGroup.UUID, true);
2231 objectGroup.m_isDeleted = true; 2373 objectGroup.m_isDeleted = true;
2374
2375 objectGroup.lockPartsForWrite(true);
2232 2376
2233 lock (objectGroup.m_parts) 2377 objectGroup.m_parts.Clear();
2234 { 2378
2235 objectGroup.m_parts.Clear(); 2379 objectGroup.lockPartsForWrite(false);
2236 }
2237 2380
2238 // Can't do this yet since backup still makes use of the root part without any synchronization 2381 // Can't do this yet since backup still makes use of the root part without any synchronization
2239// objectGroup.m_rootPart = null; 2382// objectGroup.m_rootPart = null;
@@ -2292,11 +2435,12 @@ namespace OpenSim.Region.Framework.Scenes
2292 Quaternion worldRot = linkPart.GetWorldRotation(); 2435 Quaternion worldRot = linkPart.GetWorldRotation();
2293 2436
2294 // Remove the part from this object 2437 // Remove the part from this object
2295 lock (m_parts) 2438 lockPartsForWrite(true);
2296 { 2439 {
2297 m_parts.Remove(linkPart.UUID); 2440 m_parts.Remove(linkPart.UUID);
2298 } 2441 }
2299 2442 lockPartsForWrite(false);
2443 lockPartsForRead(true);
2300 if (m_parts.Count == 1 && RootPart != null) //Single prim is left 2444 if (m_parts.Count == 1 && RootPart != null) //Single prim is left
2301 RootPart.LinkNum = 0; 2445 RootPart.LinkNum = 0;
2302 else 2446 else
@@ -2307,6 +2451,7 @@ namespace OpenSim.Region.Framework.Scenes
2307 p.LinkNum--; 2451 p.LinkNum--;
2308 } 2452 }
2309 } 2453 }
2454 lockPartsForRead(false);
2310 2455
2311 linkPart.ParentID = 0; 2456 linkPart.ParentID = 0;
2312 linkPart.LinkNum = 0; 2457 linkPart.LinkNum = 0;
@@ -2624,22 +2769,23 @@ namespace OpenSim.Region.Framework.Scenes
2624 2769
2625 if (selectionPart != null) 2770 if (selectionPart != null)
2626 { 2771 {
2627 lock (m_parts) 2772 lockPartsForRead(true);
2773 List<SceneObjectPart> parts = new List<SceneObjectPart>(m_parts.Values);
2774 lockPartsForRead(false);
2775 foreach (SceneObjectPart part in parts)
2628 { 2776 {
2629 foreach (SceneObjectPart part in m_parts.Values) 2777 if (part.Scale.X > 10.0 || part.Scale.Y > 10.0 || part.Scale.Z > 10.0)
2630 { 2778 {
2631 if (part.Scale.X > 10.0 || part.Scale.Y > 10.0 || part.Scale.Z > 10.0) 2779 UsePhysics = false; // Reset physics
2632 { 2780 break;
2633 UsePhysics = false; // Reset physics
2634 break;
2635 }
2636 } 2781 }
2782 }
2637 2783
2638 foreach (SceneObjectPart part in m_parts.Values) 2784 foreach (SceneObjectPart part in parts)
2639 { 2785 {
2640 part.UpdatePrimFlags(UsePhysics, IsTemporary, IsPhantom, IsVolumeDetect); 2786 part.UpdatePrimFlags(UsePhysics, IsTemporary, IsPhantom, IsVolumeDetect);
2641 }
2642 } 2787 }
2788
2643 } 2789 }
2644 } 2790 }
2645 2791
@@ -2863,7 +3009,7 @@ namespace OpenSim.Region.Framework.Scenes
2863 prevScale.Z *= z; 3009 prevScale.Z *= z;
2864 part.Resize(prevScale); 3010 part.Resize(prevScale);
2865 3011
2866 lock (m_parts) 3012 lockPartsForRead(true);
2867 { 3013 {
2868 foreach (SceneObjectPart obPart in m_parts.Values) 3014 foreach (SceneObjectPart obPart in m_parts.Values)
2869 { 3015 {
@@ -2882,6 +3028,7 @@ namespace OpenSim.Region.Framework.Scenes
2882 } 3028 }
2883 } 3029 }
2884 } 3030 }
3031 lockPartsForRead(false);
2885 3032
2886 if (part.PhysActor != null) 3033 if (part.PhysActor != null)
2887 { 3034 {
@@ -2962,7 +3109,7 @@ namespace OpenSim.Region.Framework.Scenes
2962 axDiff *= Quaternion.Inverse(partRotation); 3109 axDiff *= Quaternion.Inverse(partRotation);
2963 diff = axDiff; 3110 diff = axDiff;
2964 3111
2965 lock (m_parts) 3112 lockPartsForRead(true);
2966 { 3113 {
2967 foreach (SceneObjectPart obPart in m_parts.Values) 3114 foreach (SceneObjectPart obPart in m_parts.Values)
2968 { 3115 {
@@ -2972,6 +3119,7 @@ namespace OpenSim.Region.Framework.Scenes
2972 } 3119 }
2973 } 3120 }
2974 } 3121 }
3122 lockPartsForRead(false);
2975 3123
2976 AbsolutePosition = newPos; 3124 AbsolutePosition = newPos;
2977 3125
@@ -3089,7 +3237,7 @@ namespace OpenSim.Region.Framework.Scenes
3089 m_scene.PhysicsScene.AddPhysicsActorTaint(m_rootPart.PhysActor); 3237 m_scene.PhysicsScene.AddPhysicsActorTaint(m_rootPart.PhysActor);
3090 } 3238 }
3091 3239
3092 lock (m_parts) 3240 lockPartsForRead(true);
3093 { 3241 {
3094 foreach (SceneObjectPart prim in m_parts.Values) 3242 foreach (SceneObjectPart prim in m_parts.Values)
3095 { 3243 {
@@ -3107,6 +3255,7 @@ namespace OpenSim.Region.Framework.Scenes
3107 } 3255 }
3108 } 3256 }
3109 } 3257 }
3258 lockPartsForRead(false);
3110 3259
3111 m_rootPart.ScheduleTerseUpdate(); 3260 m_rootPart.ScheduleTerseUpdate();
3112 } 3261 }
@@ -3205,7 +3354,7 @@ namespace OpenSim.Region.Framework.Scenes
3205 if (atTargets.Count > 0) 3354 if (atTargets.Count > 0)
3206 { 3355 {
3207 uint[] localids = new uint[0]; 3356 uint[] localids = new uint[0];
3208 lock (m_parts) 3357 lockPartsForRead(true);
3209 { 3358 {
3210 localids = new uint[m_parts.Count]; 3359 localids = new uint[m_parts.Count];
3211 int cntr = 0; 3360 int cntr = 0;
@@ -3215,6 +3364,7 @@ namespace OpenSim.Region.Framework.Scenes
3215 cntr++; 3364 cntr++;
3216 } 3365 }
3217 } 3366 }
3367 lockPartsForRead(false);
3218 3368
3219 for (int ctr = 0; ctr < localids.Length; ctr++) 3369 for (int ctr = 0; ctr < localids.Length; ctr++)
3220 { 3370 {
@@ -3233,7 +3383,7 @@ namespace OpenSim.Region.Framework.Scenes
3233 { 3383 {
3234 //trigger not_at_target 3384 //trigger not_at_target
3235 uint[] localids = new uint[0]; 3385 uint[] localids = new uint[0];
3236 lock (m_parts) 3386 lockPartsForRead(true);
3237 { 3387 {
3238 localids = new uint[m_parts.Count]; 3388 localids = new uint[m_parts.Count];
3239 int cntr = 0; 3389 int cntr = 0;
@@ -3243,7 +3393,8 @@ namespace OpenSim.Region.Framework.Scenes
3243 cntr++; 3393 cntr++;
3244 } 3394 }
3245 } 3395 }
3246 3396 lockPartsForRead(false);
3397
3247 for (int ctr = 0; ctr < localids.Length; ctr++) 3398 for (int ctr = 0; ctr < localids.Length; ctr++)
3248 { 3399 {
3249 m_scene.EventManager.TriggerNotAtTargetEvent(localids[ctr]); 3400 m_scene.EventManager.TriggerNotAtTargetEvent(localids[ctr]);
@@ -3256,19 +3407,20 @@ namespace OpenSim.Region.Framework.Scenes
3256 public float GetMass() 3407 public float GetMass()
3257 { 3408 {
3258 float retmass = 0f; 3409 float retmass = 0f;
3259 lock (m_parts) 3410 lockPartsForRead(true);
3260 { 3411 {
3261 foreach (SceneObjectPart part in m_parts.Values) 3412 foreach (SceneObjectPart part in m_parts.Values)
3262 { 3413 {
3263 retmass += part.GetMass(); 3414 retmass += part.GetMass();
3264 } 3415 }
3265 } 3416 }
3417 lockPartsForRead(false);
3266 return retmass; 3418 return retmass;
3267 } 3419 }
3268 3420
3269 public void CheckSculptAndLoad() 3421 public void CheckSculptAndLoad()
3270 { 3422 {
3271 lock (m_parts) 3423 lockPartsForRead(true);
3272 { 3424 {
3273 if (!IsDeleted) 3425 if (!IsDeleted)
3274 { 3426 {
@@ -3293,6 +3445,7 @@ namespace OpenSim.Region.Framework.Scenes
3293 } 3445 }
3294 } 3446 }
3295 } 3447 }
3448 lockPartsForRead(false);
3296 } 3449 }
3297 3450
3298 protected void AssetReceived(string id, Object sender, AssetBase asset) 3451 protected void AssetReceived(string id, Object sender, AssetBase asset)
@@ -3313,7 +3466,7 @@ namespace OpenSim.Region.Framework.Scenes
3313 /// <param name="client"></param> 3466 /// <param name="client"></param>
3314 public void SetGroup(UUID GroupID, IClientAPI client) 3467 public void SetGroup(UUID GroupID, IClientAPI client)
3315 { 3468 {
3316 lock (m_parts) 3469 lockPartsForRead(true);
3317 { 3470 {
3318 foreach (SceneObjectPart part in m_parts.Values) 3471 foreach (SceneObjectPart part in m_parts.Values)
3319 { 3472 {
@@ -3323,7 +3476,7 @@ namespace OpenSim.Region.Framework.Scenes
3323 3476
3324 HasGroupChanged = true; 3477 HasGroupChanged = true;
3325 } 3478 }
3326 3479 lockPartsForRead(false);
3327 ScheduleGroupForFullUpdate(); 3480 ScheduleGroupForFullUpdate();
3328 } 3481 }
3329 3482
@@ -3342,11 +3495,12 @@ namespace OpenSim.Region.Framework.Scenes
3342 3495
3343 public void SetAttachmentPoint(byte point) 3496 public void SetAttachmentPoint(byte point)
3344 { 3497 {
3345 lock (m_parts) 3498 lockPartsForRead(true);
3346 { 3499 {
3347 foreach (SceneObjectPart part in m_parts.Values) 3500 foreach (SceneObjectPart part in m_parts.Values)
3348 part.SetAttachmentPoint(point); 3501 part.SetAttachmentPoint(point);
3349 } 3502 }
3503 lockPartsForRead(false);
3350 } 3504 }
3351 3505
3352 #region ISceneObject 3506 #region ISceneObject