aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--OpenSim/Region/ClientStack/Linden/Caps/EventQueue/Tests/EventQueueTests.cs2
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs6
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/Tests/LLImageManagerTests.cs2
-rw-r--r--OpenSim/Region/ClientStack/Linden/UDP/Tests/PacketHandlerTests.cs2
-rw-r--r--OpenSim/Region/CoreModules/Asset/Tests/FlotsamAssetCacheTests.cs6
-rw-r--r--OpenSim/Region/CoreModules/Avatar/AvatarFactory/Tests/AvatarFactoryModuleTests.cs2
-rw-r--r--OpenSim/Region/CoreModules/Avatar/Friends/Tests/FriendModuleTests.cs2
-rw-r--r--OpenSim/Region/CoreModules/Avatar/Groups/GroupsModule.cs3
-rw-r--r--OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs10
-rw-r--r--OpenSim/Region/CoreModules/Avatar/InstantMessage/OfflineMessageModule.cs32
-rw-r--r--OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs138
-rw-r--r--OpenSim/Region/CoreModules/Framework/InventoryAccess/HGAssetMapper.cs38
-rw-r--r--OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs6
-rw-r--r--OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs10
-rw-r--r--OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/Tests/PresenceConnectorsTests.cs8
-rw-r--r--OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs25
-rw-r--r--OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequest.cs11
-rw-r--r--OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs317
-rw-r--r--OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs107
-rw-r--r--OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs2
-rw-r--r--OpenSim/Region/CoreModules/World/Land/LandObject.cs13
-rw-r--r--OpenSim/Region/CoreModules/World/Land/Tests/PrimCountModuleTests.cs6
-rw-r--r--OpenSim/Region/CoreModules/World/Media/Moap/Tests/MoapTests.cs6
-rw-r--r--OpenSim/Region/CoreModules/World/Objects/Commands/ObjectCommandsModule.cs60
-rw-r--r--OpenSim/Region/CoreModules/World/Serialiser/Tests/SerialiserTests.cs2
-rw-r--r--OpenSim/Region/CoreModules/World/Sound/SoundModule.cs8
-rw-r--r--OpenSim/Region/CoreModules/World/Terrain/Tests/TerrainTest.cs3
-rw-r--r--OpenSim/Region/CoreModules/World/Wind/WindModule.cs2
-rw-r--r--OpenSim/Region/Framework/Interfaces/IXmlRpcRouter.cs1
-rw-r--r--OpenSim/Region/Framework/Scenes/Animation/ScenePresenceAnimator.cs11
-rw-r--r--OpenSim/Region/Framework/Scenes/EntityManager.cs4
-rw-r--r--OpenSim/Region/Framework/Scenes/Scene.Inventory.cs10
-rw-r--r--OpenSim/Region/Framework/Scenes/Scene.cs22
-rw-r--r--OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs18
-rw-r--r--OpenSim/Region/Framework/Scenes/SceneObjectPart.cs9
-rw-r--r--OpenSim/Region/Framework/Scenes/ScenePresence.cs36
-rw-r--r--OpenSim/Region/Framework/Scenes/Tests/BorderTests.cs2
-rw-r--r--OpenSim/Region/Framework/Scenes/Tests/EntityManagerTests.cs2
-rw-r--r--OpenSim/Region/Framework/Scenes/Tests/SceneGraphTests.cs2
-rw-r--r--OpenSim/Region/Framework/Scenes/Tests/SceneManagerTests.cs2
-rw-r--r--OpenSim/Region/Framework/Scenes/Tests/SceneObjectBasicTests.cs101
-rw-r--r--OpenSim/Region/Framework/Scenes/Tests/SceneObjectDeRezTests.cs83
-rw-r--r--OpenSim/Region/Framework/Scenes/Tests/SceneObjectLinkingTests.cs2
-rw-r--r--OpenSim/Region/Framework/Scenes/Tests/SceneObjectResizeTests.cs2
-rw-r--r--OpenSim/Region/Framework/Scenes/Tests/SceneObjectScriptTests.cs2
-rw-r--r--OpenSim/Region/Framework/Scenes/Tests/SceneObjectSpatialTests.cs6
-rw-r--r--OpenSim/Region/Framework/Scenes/Tests/SceneObjectStatusTests.cs43
-rw-r--r--OpenSim/Region/Framework/Scenes/Tests/ScenePresenceAnimationTests.cs2
-rw-r--r--OpenSim/Region/Framework/Scenes/Tests/ScenePresenceAutopilotTests.cs2
-rw-r--r--OpenSim/Region/Framework/Scenes/Tests/ScenePresenceSitTests.cs2
-rw-r--r--OpenSim/Region/Framework/Scenes/Tests/ScenePresenceTeleportTests.cs2
-rw-r--r--OpenSim/Region/Framework/Scenes/Tests/SceneTests.cs2
-rw-r--r--OpenSim/Region/Framework/Scenes/Tests/TaskInventoryTests.cs2
-rw-r--r--OpenSim/Region/Framework/Scenes/Tests/UuidGathererTests.cs2
-rw-r--r--OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/IGroupsServicesConnector.cs15
-rw-r--r--OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/Tests/GroupsModuleTests.cs2
-rw-r--r--OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsServicesConnectorModule.cs127
-rw-r--r--OpenSim/Region/OptionalModules/Scripting/XmlRpcRouterModule/XmlRpcGridRouterModule.cs82
-rw-r--r--OpenSim/Region/OptionalModules/Scripting/XmlRpcRouterModule/XmlRpcRouterModule.cs6
-rw-r--r--OpenSim/Region/OptionalModules/World/NPC/Tests/NPCModuleTests.cs2
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs97
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs4
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSConstraint6Dof.cs4
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs987
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs28
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs283
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs17
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSMaterials.cs200
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSMotors.cs324
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs37
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs272
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSScene.cs110
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs114
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs17
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs156
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs45
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs203
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BulletSimData.cs278
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt204
-rw-r--r--OpenSim/Region/Physics/ChOdePlugin/ODEPrim.cs4
-rw-r--r--OpenSim/Region/Physics/Manager/IMesher.cs2
-rw-r--r--OpenSim/Region/Physics/Manager/PhysicsActor.cs13
-rw-r--r--OpenSim/Region/Physics/Manager/ZeroMesher.cs9
-rw-r--r--OpenSim/Region/Physics/Meshing/Meshmerizer.cs26
-rw-r--r--OpenSim/Region/Physics/OdePlugin/ODEPrim.cs5
-rw-r--r--OpenSim/Region/Physics/OdePlugin/Tests/ODETestClass.cs3
-rw-r--r--OpenSim/Region/Physics/UbitMeshing/Meshmerizer.cs6
-rw-r--r--OpenSim/Region/Physics/UbitOdePlugin/ODEMeshWorker.cs6
-rw-r--r--OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs53
-rw-r--r--OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs7
-rw-r--r--OpenSim/Region/ScriptEngine/Shared/CodeTools/Tests/CSCodeGeneratorTest.cs2
-rw-r--r--OpenSim/Region/ScriptEngine/Shared/CodeTools/Tests/CompilerTest.cs2
-rw-r--r--OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs69
-rw-r--r--OpenSim/Region/ScriptEngine/Shared/Tests/LSL_ApiInventoryTests.cs6
-rw-r--r--OpenSim/Region/ScriptEngine/Shared/Tests/LSL_ApiLinkingTests.cs6
-rw-r--r--OpenSim/Region/ScriptEngine/Shared/Tests/LSL_ApiListTests.cs6
-rw-r--r--OpenSim/Region/ScriptEngine/Shared/Tests/LSL_TypesTestLSLFloat.cs2
-rw-r--r--OpenSim/Region/ScriptEngine/Shared/Tests/LSL_TypesTestLSLInteger.cs2
-rw-r--r--OpenSim/Region/ScriptEngine/Shared/Tests/LSL_TypesTestLSLString.cs2
-rw-r--r--OpenSim/Region/ScriptEngine/Shared/Tests/LSL_TypesTestList.cs2
-rw-r--r--OpenSim/Region/ScriptEngine/Shared/Tests/LSL_TypesTestVector3.cs2
-rw-r--r--OpenSim/Region/ScriptEngine/Shared/Tests/OSSL_ApiAppearanceTest.cs6
-rw-r--r--OpenSim/Region/ScriptEngine/Shared/Tests/OSSL_ApiNpcTests.cs4
-rw-r--r--OpenSim/Region/ScriptEngine/XEngine/Tests/XEngineTest.cs2
-rw-r--r--OpenSim/Region/ScriptEngine/XEngine/XEngine.cs141
-rw-r--r--OpenSim/Region/UserStatistics/WebStatsModule.cs45
106 files changed, 3479 insertions, 1769 deletions
diff --git a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/Tests/EventQueueTests.cs b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/Tests/EventQueueTests.cs
index d604cf6..ed8ec16 100644
--- a/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/Tests/EventQueueTests.cs
+++ b/OpenSim/Region/ClientStack/Linden/Caps/EventQueue/Tests/EventQueueTests.cs
@@ -44,7 +44,7 @@ using OpenSim.Tests.Common.Mock;
44namespace OpenSim.Region.ClientStack.Linden.Tests 44namespace OpenSim.Region.ClientStack.Linden.Tests
45{ 45{
46 [TestFixture] 46 [TestFixture]
47 public class EventQueueTests 47 public class EventQueueTests : OpenSimTestCase
48 { 48 {
49 private TestScene m_scene; 49 private TestScene m_scene;
50 50
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs
index 4860da0..ee66485 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/LLClientView.cs
@@ -12122,11 +12122,13 @@ namespace OpenSim.Region.ClientStack.LindenUDP
12122 if (logPacket) 12122 if (logPacket)
12123 m_log.DebugFormat( 12123 m_log.DebugFormat(
12124 "[CLIENT]: PACKET IN from {0} ({1}) in {2} - {3}", 12124 "[CLIENT]: PACKET IN from {0} ({1}) in {2} - {3}",
12125 Name, SceneAgent.IsChildAgent ? "child" : "root ", m_scene.RegionInfo.RegionName, packet.Type); 12125 Name, SceneAgent.IsChildAgent ? "child" : "root ", Scene.Name, packet.Type);
12126 } 12126 }
12127 12127
12128 if (!ProcessPacketMethod(packet)) 12128 if (!ProcessPacketMethod(packet))
12129 m_log.Warn("[CLIENT]: unhandled packet " + packet.Type); 12129 m_log.WarnFormat(
12130 "[CLIENT]: Unhandled packet {0} from {1} ({2}) in {3}. Ignoring.",
12131 packet.Type, Name, SceneAgent.IsChildAgent ? "child" : "root ", Scene.Name);
12130 } 12132 }
12131 12133
12132 private static PrimitiveBaseShape GetShapeFromAddPacket(ObjectAddPacket addPacket) 12134 private static PrimitiveBaseShape GetShapeFromAddPacket(ObjectAddPacket addPacket)
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/Tests/LLImageManagerTests.cs b/OpenSim/Region/ClientStack/Linden/UDP/Tests/LLImageManagerTests.cs
index 5fcf376..7d9f581 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/Tests/LLImageManagerTests.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/Tests/LLImageManagerTests.cs
@@ -43,7 +43,7 @@ using OpenSim.Tests.Common.Mock;
43namespace OpenSim.Region.ClientStack.LindenUDP.Tests 43namespace OpenSim.Region.ClientStack.LindenUDP.Tests
44{ 44{
45 [TestFixture] 45 [TestFixture]
46 public class LLImageManagerTests 46 public class LLImageManagerTests : OpenSimTestCase
47 { 47 {
48 private AssetBase m_testImageAsset; 48 private AssetBase m_testImageAsset;
49 private Scene scene; 49 private Scene scene;
diff --git a/OpenSim/Region/ClientStack/Linden/UDP/Tests/PacketHandlerTests.cs b/OpenSim/Region/ClientStack/Linden/UDP/Tests/PacketHandlerTests.cs
index 0f88ec6..5f73a94 100644
--- a/OpenSim/Region/ClientStack/Linden/UDP/Tests/PacketHandlerTests.cs
+++ b/OpenSim/Region/ClientStack/Linden/UDP/Tests/PacketHandlerTests.cs
@@ -39,7 +39,7 @@ namespace OpenSim.Region.ClientStack.LindenUDP.Tests
39 /// Tests for the LL packet handler 39 /// Tests for the LL packet handler
40 /// </summary> 40 /// </summary>
41 [TestFixture] 41 [TestFixture]
42 public class PacketHandlerTests 42 public class PacketHandlerTests : OpenSimTestCase
43 { 43 {
44// [Test] 44// [Test]
45// /// <summary> 45// /// <summary>
diff --git a/OpenSim/Region/CoreModules/Asset/Tests/FlotsamAssetCacheTests.cs b/OpenSim/Region/CoreModules/Asset/Tests/FlotsamAssetCacheTests.cs
index 1c2bfd0..fd02b08 100644
--- a/OpenSim/Region/CoreModules/Asset/Tests/FlotsamAssetCacheTests.cs
+++ b/OpenSim/Region/CoreModules/Asset/Tests/FlotsamAssetCacheTests.cs
@@ -47,14 +47,16 @@ namespace OpenSim.Region.CoreModules.Asset.Tests
47 /// At the moment we're only test the in-memory part of the FlotsamAssetCache. This is a considerable weakness. 47 /// At the moment we're only test the in-memory part of the FlotsamAssetCache. This is a considerable weakness.
48 /// </summary> 48 /// </summary>
49 [TestFixture] 49 [TestFixture]
50 public class FlotsamAssetCacheTests 50 public class FlotsamAssetCacheTests : OpenSimTestCase
51 { 51 {
52 protected TestScene m_scene; 52 protected TestScene m_scene;
53 protected FlotsamAssetCache m_cache; 53 protected FlotsamAssetCache m_cache;
54 54
55 [SetUp] 55 [SetUp]
56 public void SetUp() 56 public override void SetUp()
57 { 57 {
58 base.SetUp();
59
58 IConfigSource config = new IniConfigSource(); 60 IConfigSource config = new IniConfigSource();
59 61
60 config.AddConfig("Modules"); 62 config.AddConfig("Modules");
diff --git a/OpenSim/Region/CoreModules/Avatar/AvatarFactory/Tests/AvatarFactoryModuleTests.cs b/OpenSim/Region/CoreModules/Avatar/AvatarFactory/Tests/AvatarFactoryModuleTests.cs
index 848b3bf..1830d41 100644
--- a/OpenSim/Region/CoreModules/Avatar/AvatarFactory/Tests/AvatarFactoryModuleTests.cs
+++ b/OpenSim/Region/CoreModules/Avatar/AvatarFactory/Tests/AvatarFactoryModuleTests.cs
@@ -39,7 +39,7 @@ using OpenSim.Tests.Common.Mock;
39namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory 39namespace OpenSim.Region.CoreModules.Avatar.AvatarFactory
40{ 40{
41 [TestFixture] 41 [TestFixture]
42 public class AvatarFactoryModuleTests 42 public class AvatarFactoryModuleTests : OpenSimTestCase
43 { 43 {
44 /// <summary> 44 /// <summary>
45 /// Only partial right now since we don't yet test that it's ended up in the avatar appearance service. 45 /// Only partial right now since we don't yet test that it's ended up in the avatar appearance service.
diff --git a/OpenSim/Region/CoreModules/Avatar/Friends/Tests/FriendModuleTests.cs b/OpenSim/Region/CoreModules/Avatar/Friends/Tests/FriendModuleTests.cs
index 7a197f7..961117e 100644
--- a/OpenSim/Region/CoreModules/Avatar/Friends/Tests/FriendModuleTests.cs
+++ b/OpenSim/Region/CoreModules/Avatar/Friends/Tests/FriendModuleTests.cs
@@ -40,7 +40,7 @@ using OpenSim.Tests.Common.Mock;
40namespace OpenSim.Region.CoreModules.Avatar.Friends.Tests 40namespace OpenSim.Region.CoreModules.Avatar.Friends.Tests
41{ 41{
42 [TestFixture] 42 [TestFixture]
43 public class FriendsModuleTests 43 public class FriendsModuleTests : OpenSimTestCase
44 { 44 {
45 private FriendsModule m_fm; 45 private FriendsModule m_fm;
46 private TestScene m_scene; 46 private TestScene m_scene;
diff --git a/OpenSim/Region/CoreModules/Avatar/Groups/GroupsModule.cs b/OpenSim/Region/CoreModules/Avatar/Groups/GroupsModule.cs
index af54c1a..b735c61 100644
--- a/OpenSim/Region/CoreModules/Avatar/Groups/GroupsModule.cs
+++ b/OpenSim/Region/CoreModules/Avatar/Groups/GroupsModule.cs
@@ -81,7 +81,10 @@ namespace OpenSim.Region.CoreModules.Avatar.Groups
81 } 81 }
82 82
83 if (groupsConfig.GetString("Module", "Default") != "Default") 83 if (groupsConfig.GetString("Module", "Default") != "Default")
84 {
85 m_Enabled = false;
84 return; 86 return;
87 }
85 } 88 }
86 89
87 } 90 }
diff --git a/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs b/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs
index cc266df..1627f6c 100644
--- a/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs
+++ b/OpenSim/Region/CoreModules/Avatar/InstantMessage/MessageTransferModule.cs
@@ -153,7 +153,7 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage
153 if (sp != null && !sp.IsChildAgent) 153 if (sp != null && !sp.IsChildAgent)
154 { 154 {
155 // Local message 155 // Local message
156 m_log.DebugFormat("[INSTANT MESSAGE]: Delivering IM to root agent {0} {1}", sp.Name, toAgentID); 156// m_log.DebugFormat("[INSTANT MESSAGE]: Delivering IM to root agent {0} {1}", sp.Name, toAgentID);
157 157
158 sp.ControllingClient.SendInstantMessage(im); 158 sp.ControllingClient.SendInstantMessage(im);
159 159
@@ -166,14 +166,14 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage
166 // try child avatar second 166 // try child avatar second
167 foreach (Scene scene in m_Scenes) 167 foreach (Scene scene in m_Scenes)
168 { 168 {
169 //m_log.DebugFormat( 169// m_log.DebugFormat(
170 // "[INSTANT MESSAGE]: Looking for child of {0} in {1}", toAgentID, scene.RegionInfo.RegionName); 170// "[INSTANT MESSAGE]: Looking for child of {0} in {1}", toAgentID, scene.RegionInfo.RegionName);
171 171
172 ScenePresence sp = scene.GetScenePresence(toAgentID); 172 ScenePresence sp = scene.GetScenePresence(toAgentID);
173 if (sp != null) 173 if (sp != null)
174 { 174 {
175 // Local message 175 // Local message
176 m_log.DebugFormat("[INSTANT MESSAGE]: Delivering IM to child agent {0} {1}", sp.Name, toAgentID); 176// m_log.DebugFormat("[INSTANT MESSAGE]: Delivering IM to child agent {0} {1}", sp.Name, toAgentID);
177 177
178 sp.ControllingClient.SendInstantMessage(im); 178 sp.ControllingClient.SendInstantMessage(im);
179 179
@@ -183,7 +183,7 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage
183 } 183 }
184 } 184 }
185 185
186 m_log.DebugFormat("[INSTANT MESSAGE]: Delivering IM to {0} via XMLRPC", im.toAgentID); 186// m_log.DebugFormat("[INSTANT MESSAGE]: Delivering IM to {0} via XMLRPC", im.toAgentID);
187 187
188 SendGridInstantMessageViaXMLRPC(im, result); 188 SendGridInstantMessageViaXMLRPC(im, result);
189 } 189 }
diff --git a/OpenSim/Region/CoreModules/Avatar/InstantMessage/OfflineMessageModule.cs b/OpenSim/Region/CoreModules/Avatar/InstantMessage/OfflineMessageModule.cs
index 3a44cc5..2d46276 100644
--- a/OpenSim/Region/CoreModules/Avatar/InstantMessage/OfflineMessageModule.cs
+++ b/OpenSim/Region/CoreModules/Avatar/InstantMessage/OfflineMessageModule.cs
@@ -189,20 +189,24 @@ namespace OpenSim.Region.CoreModules.Avatar.InstantMessage
189 { 189 {
190 foreach (GridInstantMessage im in msglist) 190 foreach (GridInstantMessage im in msglist)
191 { 191 {
192 // client.SendInstantMessage(im); 192 if (im.dialog == (byte)InstantMessageDialog.InventoryOffered)
193 193 // send it directly or else the item will be given twice
194 // Send through scene event manager so all modules get a chance 194 client.SendInstantMessage(im);
195 // to look at this message before it gets delivered. 195 else
196 // 196 {
197 // Needed for proper state management for stored group 197 // Send through scene event manager so all modules get a chance
198 // invitations 198 // to look at this message before it gets delivered.
199 // 199 //
200 200 // Needed for proper state management for stored group
201 im.offline = 1; 201 // invitations
202 202 //
203 Scene s = FindScene(client.AgentId); 203
204 if (s != null) 204 im.offline = 1;
205 s.EventManager.TriggerIncomingInstantMessage(im); 205
206 Scene s = FindScene(client.AgentId);
207 if (s != null)
208 s.EventManager.TriggerIncomingInstantMessage(im);
209 }
206 } 210 }
207 } 211 }
208 } 212 }
diff --git a/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs b/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs
index 4cfa33d..ae58dfd 100644
--- a/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs
+++ b/OpenSim/Region/CoreModules/Avatar/Inventory/Transfer/InventoryTransferModule.cs
@@ -316,76 +316,74 @@ namespace OpenSim.Region.CoreModules.Avatar.Inventory.Transfer
316 } 316 }
317 } 317 }
318 318
319 // Disabled for now as it looks like http://opensimulator.org/mantis/view.php?id=6311 was fixed by fixes 319 // XXX: This code was placed here to try and accomodate RLV which moves given folders named #RLV/~<name>
320 // to inventory folder versioning allowing the viewer to move the received folder itself as happens on the 320 // to the requested folder, which in this case is #RLV. However, it is the viewer that appears to be
321 // LL grid. Doing it again server-side then wrongly does a second create and move 321 // response from renaming the #RLV/~example folder to ~example. For some reason this is not yet
322// // XXX: This code was placed here to try and accomdate RLV which moves given folders named #RLV/~<name> 322 // happening, possibly because we are not sending the correct inventory update messages with the correct
323// // to a folder called name in #RLV. However, this approach may not be ultimately correct - from analysis 323 // transaction IDs
324// // of Firestorm 4.2.2 on sending an InventoryOffered instead of TaskInventoryOffered (as was previously 324 else if (im.dialog == (byte) InstantMessageDialog.TaskInventoryAccepted)
325// // done), the viewer itself would appear to move and rename the folder, rather than the simulator doing it here. 325 {
326// else if (im.dialog == (byte) InstantMessageDialog.TaskInventoryAccepted) 326 UUID destinationFolderID = UUID.Zero;
327// { 327
328// UUID destinationFolderID = UUID.Zero; 328 if (im.binaryBucket != null && im.binaryBucket.Length >= 16)
329// 329 {
330// if (im.binaryBucket != null && im.binaryBucket.Length >= 16) 330 destinationFolderID = new UUID(im.binaryBucket, 0);
331// { 331 }
332// destinationFolderID = new UUID(im.binaryBucket, 0); 332
333// } 333 if (destinationFolderID != UUID.Zero)
334// 334 {
335// if (destinationFolderID != UUID.Zero) 335 InventoryFolderBase destinationFolder = new InventoryFolderBase(destinationFolderID, client.AgentId);
336// { 336 if (destinationFolder == null)
337// InventoryFolderBase destinationFolder = new InventoryFolderBase(destinationFolderID, client.AgentId); 337 {
338// if (destinationFolder == null) 338 m_log.WarnFormat(
339// { 339 "[INVENTORY TRANSFER]: TaskInventoryAccepted message from {0} in {1} specified folder {2} which does not exist",
340// m_log.WarnFormat( 340 client.Name, scene.Name, destinationFolderID);
341// "[INVENTORY TRANSFER]: TaskInventoryAccepted message from {0} in {1} specified folder {2} which does not exist", 341
342// client.Name, scene.Name, destinationFolderID); 342 return;
343// 343 }
344// return; 344
345// } 345 IInventoryService invService = scene.InventoryService;
346// 346
347// IInventoryService invService = scene.InventoryService; 347 UUID inventoryID = new UUID(im.imSessionID); // The inventory item/folder, back from it's trip
348// 348
349// UUID inventoryID = new UUID(im.imSessionID); // The inventory item/folder, back from it's trip 349 InventoryItemBase item = new InventoryItemBase(inventoryID, client.AgentId);
350// 350 item = invService.GetItem(item);
351// InventoryItemBase item = new InventoryItemBase(inventoryID, client.AgentId); 351 InventoryFolderBase folder = null;
352// item = invService.GetItem(item); 352 UUID? previousParentFolderID = null;
353// InventoryFolderBase folder = null; 353
354// UUID? previousParentFolderID = null; 354 if (item != null) // It's an item
355// 355 {
356// if (item != null) // It's an item 356 previousParentFolderID = item.Folder;
357// { 357 item.Folder = destinationFolderID;
358// previousParentFolderID = item.Folder; 358
359// item.Folder = destinationFolderID; 359 invService.DeleteItems(item.Owner, new List<UUID>() { item.ID });
360// 360 scene.AddInventoryItem(client, item);
361// invService.DeleteItems(item.Owner, new List<UUID>() { item.ID }); 361 }
362// scene.AddInventoryItem(client, item); 362 else
363// } 363 {
364// else 364 folder = new InventoryFolderBase(inventoryID, client.AgentId);
365// { 365 folder = invService.GetFolder(folder);
366// folder = new InventoryFolderBase(inventoryID, client.AgentId); 366
367// folder = invService.GetFolder(folder); 367 if (folder != null) // It's a folder
368// 368 {
369// if (folder != null) // It's a folder 369 previousParentFolderID = folder.ParentID;
370// { 370 folder.ParentID = destinationFolderID;
371// previousParentFolderID = folder.ParentID; 371 invService.MoveFolder(folder);
372// folder.ParentID = destinationFolderID; 372 }
373// invService.MoveFolder(folder); 373 }
374// } 374
375// } 375 // Tell client about updates to original parent and new parent (this should probably be factored with existing move item/folder code).
376// 376 if (previousParentFolderID != null)
377// // Tell client about updates to original parent and new parent (this should probably be factored with existing move item/folder code). 377 {
378// if (previousParentFolderID != null) 378 InventoryFolderBase previousParentFolder
379// { 379 = new InventoryFolderBase((UUID)previousParentFolderID, client.AgentId);
380// InventoryFolderBase previousParentFolder 380 previousParentFolder = invService.GetFolder(previousParentFolder);
381// = new InventoryFolderBase((UUID)previousParentFolderID, client.AgentId); 381 scene.SendInventoryUpdate(client, previousParentFolder, true, true);
382// previousParentFolder = invService.GetFolder(previousParentFolder); 382
383// scene.SendInventoryUpdate(client, previousParentFolder, true, true); 383 scene.SendInventoryUpdate(client, destinationFolder, true, true);
384// 384 }
385// scene.SendInventoryUpdate(client, destinationFolder, true, true); 385 }
386// } 386 }
387// }
388// }
389 else if ( 387 else if (
390 im.dialog == (byte)InstantMessageDialog.InventoryDeclined 388 im.dialog == (byte)InstantMessageDialog.InventoryDeclined
391 || im.dialog == (byte)InstantMessageDialog.TaskInventoryDeclined) 389 || im.dialog == (byte)InstantMessageDialog.TaskInventoryDeclined)
diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGAssetMapper.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGAssetMapper.cs
index f8ec6de..7871eda 100644
--- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGAssetMapper.cs
+++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/HGAssetMapper.cs
@@ -71,7 +71,7 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
71 71
72 #region Internal functions 72 #region Internal functions
73 73
74 public AssetMetadata FetchMetadata(string url, UUID assetID) 74 private AssetMetadata FetchMetadata(string url, UUID assetID)
75 { 75 {
76 if (!url.EndsWith("/") && !url.EndsWith("=")) 76 if (!url.EndsWith("/") && !url.EndsWith("="))
77 url = url + "/"; 77 url = url + "/";
@@ -86,6 +86,27 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
86 return meta; 86 return meta;
87 } 87 }
88 88
89 private AssetBase FetchAsset(string url, UUID assetID)
90 {
91 // Test if it's already here
92 AssetBase asset = m_scene.AssetService.Get(assetID.ToString());
93 if (asset == null)
94 {
95 if (!url.EndsWith("/") && !url.EndsWith("="))
96 url = url + "/";
97
98 asset = m_scene.AssetService.Get(url + assetID.ToString());
99
100 //if (asset != null)
101 // m_log.DebugFormat("[HG ASSET MAPPER]: Fetched asset {0} of type {1} from {2} ", assetID, asset.Metadata.Type, url);
102 //else
103 // m_log.DebugFormat("[HG ASSET MAPPER]: Unable to fetch asset {0} from {1} ", assetID, url);
104
105 }
106
107 return asset;
108 }
109
89 public bool PostAsset(string url, AssetBase asset) 110 public bool PostAsset(string url, AssetBase asset)
90 { 111 {
91 if (asset != null) 112 if (asset != null)
@@ -228,11 +249,22 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess
228 if (meta == null) 249 if (meta == null)
229 return; 250 return;
230 251
231 // The act of gathering UUIDs downloads the assets from the remote server 252 // The act of gathering UUIDs downloads some assets from the remote server
253 // but not all...
232 Dictionary<UUID, AssetType> ids = new Dictionary<UUID, AssetType>(); 254 Dictionary<UUID, AssetType> ids = new Dictionary<UUID, AssetType>();
233 HGUuidGatherer uuidGatherer = new HGUuidGatherer(m_scene.AssetService, userAssetURL); 255 HGUuidGatherer uuidGatherer = new HGUuidGatherer(m_scene.AssetService, userAssetURL);
234 uuidGatherer.GatherAssetUuids(assetID, (AssetType)meta.Type, ids); 256 uuidGatherer.GatherAssetUuids(assetID, (AssetType)meta.Type, ids);
235 257 m_log.DebugFormat("[HG ASSET MAPPER]: Preparing to get {0} assets", ids.Count);
258 bool success = true;
259 foreach (UUID uuid in ids.Keys)
260 if (FetchAsset(userAssetURL, uuid) == null)
261 success = false;
262
263 // maybe all pieces got here...
264 if (!success)
265 m_log.DebugFormat("[HG ASSET MAPPER]: Problems getting item {0} from asset server {1}", assetID, userAssetURL);
266 else
267 m_log.DebugFormat("[HG ASSET MAPPER]: Successfully got item {0} from asset server {1}", assetID, userAssetURL);
236 } 268 }
237 269
238 270
diff --git a/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs b/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs
index 21d8bd7..ac25a93 100644
--- a/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs
+++ b/OpenSim/Region/CoreModules/Framework/InventoryAccess/Tests/InventoryAccessModuleTests.cs
@@ -49,7 +49,7 @@ using OpenSim.Tests.Common.Mock;
49namespace OpenSim.Region.CoreModules.Framework.InventoryAccess.Tests 49namespace OpenSim.Region.CoreModules.Framework.InventoryAccess.Tests
50{ 50{
51 [TestFixture] 51 [TestFixture]
52 public class InventoryAccessModuleTests 52 public class InventoryAccessModuleTests : OpenSimTestCase
53 { 53 {
54 protected TestScene m_scene; 54 protected TestScene m_scene;
55 protected BasicInventoryAccessModule m_iam; 55 protected BasicInventoryAccessModule m_iam;
@@ -57,8 +57,10 @@ namespace OpenSim.Region.CoreModules.Framework.InventoryAccess.Tests
57 protected TestClient m_tc; 57 protected TestClient m_tc;
58 58
59 [SetUp] 59 [SetUp]
60 public void SetUp() 60 public override void SetUp()
61 { 61 {
62 base.SetUp();
63
62 m_iam = new BasicInventoryAccessModule(); 64 m_iam = new BasicInventoryAccessModule();
63 65
64 IConfigSource config = new IniConfigSource(); 66 IConfigSource config = new IniConfigSource();
diff --git a/OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs b/OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs
index 86e7004..77e8b00 100644
--- a/OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs
+++ b/OpenSim/Region/CoreModules/Framework/UserManagement/UserManagementModule.cs
@@ -181,6 +181,7 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement
181 181
182 m_log.DebugFormat("[USER MANAGEMENT MODULE]: HandleAvatarPickerRequest for {0}", query); 182 m_log.DebugFormat("[USER MANAGEMENT MODULE]: HandleAvatarPickerRequest for {0}", query);
183 183
184 // searhc the user accounts service
184 List<UserAccount> accs = m_Scenes[0].UserAccountService.GetUserAccounts(m_Scenes[0].RegionInfo.ScopeID, query); 185 List<UserAccount> accs = m_Scenes[0].UserAccountService.GetUserAccounts(m_Scenes[0].RegionInfo.ScopeID, query);
185 186
186 List<UserData> users = new List<UserData>(); 187 List<UserData> users = new List<UserData>();
@@ -196,6 +197,12 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement
196 } 197 }
197 } 198 }
198 199
200 // search the local cache
201 foreach (UserData data in m_UserCache.Values)
202 if (users.Find(delegate(UserData d) { return d.Id == data.Id; }) == null &&
203 (data.FirstName.StartsWith(query) || data.LastName.StartsWith(query)))
204 users.Add(data);
205
199 AddAdditionalUsers(avatarID, query, users); 206 AddAdditionalUsers(avatarID, query, users);
200 207
201 AvatarPickerReplyPacket replyPacket = (AvatarPickerReplyPacket)PacketPool.Instance.GetPacket(PacketType.AvatarPickerReply); 208 AvatarPickerReplyPacket replyPacket = (AvatarPickerReplyPacket)PacketPool.Instance.GetPacket(PacketType.AvatarPickerReply);
@@ -433,6 +440,9 @@ namespace OpenSim.Region.CoreModules.Framework.UserManagement
433 public void AddUser(UUID uuid, string first, string last, string homeURL) 440 public void AddUser(UUID uuid, string first, string last, string homeURL)
434 { 441 {
435 //m_log.DebugFormat("[USER MANAGEMENT MODULE]: Adding user with id {0}, first {1}, last {2}, url {3}", uuid, first, last, homeURL); 442 //m_log.DebugFormat("[USER MANAGEMENT MODULE]: Adding user with id {0}, first {1}, last {2}, url {3}", uuid, first, last, homeURL);
443 if (homeURL == string.Empty)
444 return;
445
436 AddUser(uuid, homeURL + ";" + first + " " + last); 446 AddUser(uuid, homeURL + ";" + first + " " + last);
437 } 447 }
438 448
diff --git a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/Tests/PresenceConnectorsTests.cs b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/Tests/PresenceConnectorsTests.cs
index 32e47f9..69bac82 100644
--- a/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/Tests/PresenceConnectorsTests.cs
+++ b/OpenSim/Region/CoreModules/ServiceConnectorsOut/Presence/Tests/PresenceConnectorsTests.cs
@@ -35,7 +35,6 @@ using NUnit.Framework;
35using OpenMetaverse; 35using OpenMetaverse;
36using OpenSim.Framework; 36using OpenSim.Framework;
37using Nini.Config; 37using Nini.Config;
38
39using OpenSim.Region.CoreModules.ServiceConnectorsOut.Presence; 38using OpenSim.Region.CoreModules.ServiceConnectorsOut.Presence;
40using OpenSim.Region.Framework.Scenes; 39using OpenSim.Region.Framework.Scenes;
41using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo; 40using PresenceInfo = OpenSim.Services.Interfaces.PresenceInfo;
@@ -44,11 +43,14 @@ using OpenSim.Tests.Common;
44namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Presence.Tests 43namespace OpenSim.Region.CoreModules.ServiceConnectorsOut.Presence.Tests
45{ 44{
46 [TestFixture] 45 [TestFixture]
47 public class PresenceConnectorsTests 46 public class PresenceConnectorsTests : OpenSimTestCase
48 { 47 {
49 LocalPresenceServicesConnector m_LocalConnector; 48 LocalPresenceServicesConnector m_LocalConnector;
50 private void SetUp() 49
50 public override void SetUp()
51 { 51 {
52 base.SetUp();
53
52 IConfigSource config = new IniConfigSource(); 54 IConfigSource config = new IniConfigSource();
53 config.AddConfig("Modules"); 55 config.AddConfig("Modules");
54 config.AddConfig("PresenceService"); 56 config.AddConfig("PresenceService");
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs
index ade5e76..01f1c63 100644
--- a/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs
+++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveReadRequest.cs
@@ -570,13 +570,22 @@ namespace OpenSim.Region.CoreModules.World.Archiver
570 570
571 // Validate User and Group UUID's 571 // Validate User and Group UUID's
572 572
573 if (!ResolveUserUuid(scene, parcel.OwnerID)) 573 if (parcel.IsGroupOwned)
574 parcel.OwnerID = m_rootScene.RegionInfo.EstateSettings.EstateOwner;
575
576 if (!ResolveGroupUuid(parcel.GroupID))
577 { 574 {
578 parcel.GroupID = UUID.Zero; 575 if (!ResolveGroupUuid(parcel.GroupID))
579 parcel.IsGroupOwned = false; 576 {
577 parcel.OwnerID = m_rootScene.RegionInfo.EstateSettings.EstateOwner;
578 parcel.GroupID = UUID.Zero;
579 parcel.IsGroupOwned = false;
580 }
581 }
582 else
583 {
584 if (!ResolveUserUuid(scene, parcel.OwnerID))
585 parcel.OwnerID = m_rootScene.RegionInfo.EstateSettings.EstateOwner;
586
587 if (!ResolveGroupUuid(parcel.GroupID))
588 parcel.GroupID = UUID.Zero;
580 } 589 }
581 590
582 List<LandAccessEntry> accessList = new List<LandAccessEntry>(); 591 List<LandAccessEntry> accessList = new List<LandAccessEntry>();
@@ -589,8 +598,8 @@ namespace OpenSim.Region.CoreModules.World.Archiver
589 parcel.ParcelAccessList = accessList; 598 parcel.ParcelAccessList = accessList;
590 599
591// m_log.DebugFormat( 600// m_log.DebugFormat(
592// "[ARCHIVER]: Adding parcel {0}, local id {1}, area {2}", 601// "[ARCHIVER]: Adding parcel {0}, local id {1}, owner {2}, group {3}, isGroupOwned {4}, area {5}",
593// parcel.Name, parcel.LocalID, parcel.Area); 602// parcel.Name, parcel.LocalID, parcel.OwnerID, parcel.GroupID, parcel.IsGroupOwned, parcel.Area);
594 603
595 landData.Add(parcel); 604 landData.Add(parcel);
596 } 605 }
diff --git a/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequest.cs b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequest.cs
index d751b1c..7bdd65c 100644
--- a/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequest.cs
+++ b/OpenSim/Region/CoreModules/World/Archiver/ArchiveWriteRequest.cs
@@ -167,7 +167,6 @@ namespace OpenSim.Region.CoreModules.World.Archiver
167 } 167 }
168 scenesGroup.CalcSceneLocations(); 168 scenesGroup.CalcSceneLocations();
169 169
170
171 m_archiveWriter = new TarArchiveWriter(m_saveStream); 170 m_archiveWriter = new TarArchiveWriter(m_saveStream);
172 171
173 try 172 try
@@ -216,7 +215,6 @@ namespace OpenSim.Region.CoreModules.World.Archiver
216 } 215 }
217 } 216 }
218 217
219
220 private void ArchiveOneRegion(Scene scene, string regionDir, Dictionary<UUID, AssetType> assetUuids) 218 private void ArchiveOneRegion(Scene scene, string regionDir, Dictionary<UUID, AssetType> assetUuids)
221 { 219 {
222 m_log.InfoFormat("[ARCHIVER]: Writing region {0}", scene.RegionInfo.RegionName); 220 m_log.InfoFormat("[ARCHIVER]: Writing region {0}", scene.RegionInfo.RegionName);
@@ -540,7 +538,6 @@ namespace OpenSim.Region.CoreModules.World.Archiver
540 xtw.WriteElementString("size_in_meters", string.Format("{0},{1}", size.X, size.Y)); 538 xtw.WriteElementString("size_in_meters", string.Format("{0},{1}", size.X, size.Y));
541 } 539 }
542 540
543
544 protected void Save(Scene scene, List<SceneObjectGroup> sceneObjects, string regionDir) 541 protected void Save(Scene scene, List<SceneObjectGroup> sceneObjects, string regionDir)
545 { 542 {
546 if (regionDir != string.Empty) 543 if (regionDir != string.Empty)
@@ -560,8 +557,8 @@ namespace OpenSim.Region.CoreModules.World.Archiver
560 foreach (ILandObject lo in landObjects) 557 foreach (ILandObject lo in landObjects)
561 { 558 {
562 LandData landData = lo.LandData; 559 LandData landData = lo.LandData;
563 string landDataPath = String.Format("{0}{1}{2}.xml", 560 string landDataPath
564 regionDir, ArchiveConstants.LANDDATA_PATH, landData.GlobalID.ToString()); 561 = String.Format("{0}{1}", regionDir, ArchiveConstants.CreateOarLandDataPath(landData));
565 m_archiveWriter.WriteFile(landDataPath, LandDataSerializer.Serialize(landData, m_options)); 562 m_archiveWriter.WriteFile(landDataPath, LandDataSerializer.Serialize(landData, m_options));
566 } 563 }
567 564
@@ -604,7 +601,6 @@ namespace OpenSim.Region.CoreModules.World.Archiver
604 601
605 CloseArchive(String.Empty); 602 CloseArchive(String.Empty);
606 } 603 }
607
608 604
609 /// <summary> 605 /// <summary>
610 /// Closes the archive and notifies that we're done. 606 /// Closes the archive and notifies that we're done.
@@ -629,6 +625,5 @@ namespace OpenSim.Region.CoreModules.World.Archiver
629 625
630 m_rootScene.EventManager.TriggerOarFileSaved(m_requestId, errorMessage); 626 m_rootScene.EventManager.TriggerOarFileSaved(m_requestId, errorMessage);
631 } 627 }
632
633 } 628 }
634} 629} \ No newline at end of file
diff --git a/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs b/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs
index 82f49b0..eec1cec 100644
--- a/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs
+++ b/OpenSim/Region/CoreModules/World/Archiver/Tests/ArchiverTests.cs
@@ -31,16 +31,19 @@ using System.IO;
31using System.Reflection; 31using System.Reflection;
32using System.Threading; 32using System.Threading;
33using log4net.Config; 33using log4net.Config;
34using Nini.Config;
34using NUnit.Framework; 35using NUnit.Framework;
35using OpenMetaverse; 36using OpenMetaverse;
36using OpenMetaverse.Assets; 37using OpenMetaverse.Assets;
37using OpenSim.Framework; 38using OpenSim.Framework;
38using OpenSim.Framework.Serialization; 39using OpenSim.Framework.Serialization;
39using OpenSim.Framework.Serialization.External; 40using OpenSim.Framework.Serialization.External;
41using OpenSim.Region.CoreModules.World.Land;
40using OpenSim.Region.CoreModules.World.Serialiser; 42using OpenSim.Region.CoreModules.World.Serialiser;
41using OpenSim.Region.CoreModules.World.Terrain; 43using OpenSim.Region.CoreModules.World.Terrain;
42using OpenSim.Region.Framework.Scenes; 44using OpenSim.Region.Framework.Scenes;
43using OpenSim.Region.Framework.Scenes.Serialization; 45using OpenSim.Region.Framework.Scenes.Serialization;
46using OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups;
44using OpenSim.Tests.Common; 47using OpenSim.Tests.Common;
45using OpenSim.Tests.Common.Mock; 48using OpenSim.Tests.Common.Mock;
46using ArchiveConstants = OpenSim.Framework.Serialization.ArchiveConstants; 49using ArchiveConstants = OpenSim.Framework.Serialization.ArchiveConstants;
@@ -69,9 +72,6 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
69 { 72 {
70 base.SetUp(); 73 base.SetUp();
71 74
72 // FIXME: Do something about this - relying on statics in unit tests causes trouble sooner or later
73 new SceneManager();
74
75 m_archiverModule = new ArchiverModule(); 75 m_archiverModule = new ArchiverModule();
76 m_serialiserModule = new SerialiserModule(); 76 m_serialiserModule = new SerialiserModule();
77 TerrainModule terrainModule = new TerrainModule(); 77 TerrainModule terrainModule = new TerrainModule();
@@ -127,6 +127,53 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
127 127
128 return new SceneObjectPart(ownerId, shape, groupPosition, rotationOffset, offsetPosition) { Name = partName }; 128 return new SceneObjectPart(ownerId, shape, groupPosition, rotationOffset, offsetPosition) { Name = partName };
129 } 129 }
130
131 private void CreateTestObjects(Scene scene, out SceneObjectGroup sog1, out SceneObjectGroup sog2, out UUID ncAssetUuid)
132 {
133 SceneObjectPart part1 = CreateSceneObjectPart1();
134 sog1 = new SceneObjectGroup(part1);
135 scene.AddNewSceneObject(sog1, false);
136
137 AssetNotecard nc = new AssetNotecard();
138 nc.BodyText = "Hello World!";
139 nc.Encode();
140 ncAssetUuid = UUID.Random();
141 UUID ncItemUuid = UUID.Random();
142 AssetBase ncAsset
143 = AssetHelpers.CreateAsset(ncAssetUuid, AssetType.Notecard, nc.AssetData, UUID.Zero);
144 m_scene.AssetService.Store(ncAsset);
145
146 TaskInventoryItem ncItem
147 = new TaskInventoryItem { Name = "ncItem", AssetID = ncAssetUuid, ItemID = ncItemUuid };
148 SceneObjectPart part2 = CreateSceneObjectPart2();
149 sog2 = new SceneObjectGroup(part2);
150 part2.Inventory.AddInventoryItem(ncItem, true);
151
152 scene.AddNewSceneObject(sog2, false);
153 }
154
155 private static void CreateSoundAsset(TarArchiveWriter tar, Assembly assembly, string soundDataResourceName, out byte[] soundData, out UUID soundUuid)
156 {
157 using (Stream resource = assembly.GetManifestResourceStream(soundDataResourceName))
158 {
159 using (BinaryReader br = new BinaryReader(resource))
160 {
161 // FIXME: Use the inspector instead
162 soundData = br.ReadBytes(99999999);
163 soundUuid = UUID.Parse("00000000-0000-0000-0000-000000000001");
164 string soundAssetFileName
165 = ArchiveConstants.ASSETS_PATH + soundUuid
166 + ArchiveConstants.ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.SoundWAV];
167 tar.WriteFile(soundAssetFileName, soundData);
168
169 /*
170 AssetBase soundAsset = AssetHelpers.CreateAsset(soundUuid, soundData);
171 scene.AssetService.Store(soundAsset);
172 asset1FileName = ArchiveConstants.ASSETS_PATH + soundUuid + ".wav";
173 */
174 }
175 }
176 }
130 177
131 /// <summary> 178 /// <summary>
132 /// Test saving an OpenSim Region Archive. 179 /// Test saving an OpenSim Region Archive.
@@ -204,30 +251,6 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
204 // TODO: Test presence of more files and contents of files. 251 // TODO: Test presence of more files and contents of files.
205 } 252 }
206 253
207 private void CreateTestObjects(Scene scene, out SceneObjectGroup sog1, out SceneObjectGroup sog2, out UUID ncAssetUuid)
208 {
209 SceneObjectPart part1 = CreateSceneObjectPart1();
210 sog1 = new SceneObjectGroup(part1);
211 scene.AddNewSceneObject(sog1, false);
212
213 AssetNotecard nc = new AssetNotecard();
214 nc.BodyText = "Hello World!";
215 nc.Encode();
216 ncAssetUuid = UUID.Random();
217 UUID ncItemUuid = UUID.Random();
218 AssetBase ncAsset
219 = AssetHelpers.CreateAsset(ncAssetUuid, AssetType.Notecard, nc.AssetData, UUID.Zero);
220 m_scene.AssetService.Store(ncAsset);
221
222 TaskInventoryItem ncItem
223 = new TaskInventoryItem { Name = "ncItem", AssetID = ncAssetUuid, ItemID = ncItemUuid };
224 SceneObjectPart part2 = CreateSceneObjectPart2();
225 sog2 = new SceneObjectGroup(part2);
226 part2.Inventory.AddInventoryItem(ncItem, true);
227
228 scene.AddNewSceneObject(sog2, false);
229 }
230
231 /// <summary> 254 /// <summary>
232 /// Test saving an OpenSim Region Archive with the no assets option 255 /// Test saving an OpenSim Region Archive with the no assets option
233 /// </summary> 256 /// </summary>
@@ -309,59 +332,6 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
309 } 332 }
310 333
311 /// <summary> 334 /// <summary>
312 /// Test loading an OpenSim Region Archive where the scene object parts are not ordered by link number (e.g.
313 /// 2 can come after 3).
314 /// </summary>
315 [Test]
316 public void TestLoadOarUnorderedParts()
317 {
318 TestHelpers.InMethod();
319
320 UUID ownerId = TestHelpers.ParseTail(0xaaaa);
321
322 MemoryStream archiveWriteStream = new MemoryStream();
323 TarArchiveWriter tar = new TarArchiveWriter(archiveWriteStream);
324
325 tar.WriteFile(
326 ArchiveConstants.CONTROL_FILE_PATH,
327 new ArchiveWriteRequest(m_scene, (Stream)null, Guid.Empty).CreateControlFile(new ArchiveScenesGroup()));
328
329 SceneObjectGroup sog1 = SceneHelpers.CreateSceneObject(1, ownerId, "obj1-", 0x11);
330 SceneObjectPart sop2
331 = SceneHelpers.CreateSceneObjectPart("obj1-Part2", TestHelpers.ParseTail(0x12), ownerId);
332 SceneObjectPart sop3
333 = SceneHelpers.CreateSceneObjectPart("obj1-Part3", TestHelpers.ParseTail(0x13), ownerId);
334
335 // Add the parts so they will be written out in reverse order to the oar
336 sog1.AddPart(sop3);
337 sop3.LinkNum = 3;
338 sog1.AddPart(sop2);
339 sop2.LinkNum = 2;
340
341 tar.WriteFile(
342 ArchiveConstants.CreateOarObjectPath(sog1.Name, sog1.UUID, sog1.AbsolutePosition),
343 SceneObjectSerializer.ToXml2Format(sog1));
344
345 tar.Close();
346
347 MemoryStream archiveReadStream = new MemoryStream(archiveWriteStream.ToArray());
348
349 lock (this)
350 {
351 m_scene.EventManager.OnOarFileLoaded += LoadCompleted;
352 m_archiverModule.DearchiveRegion(archiveReadStream);
353 }
354
355 Assert.That(m_lastErrorMessage, Is.Null);
356
357 SceneObjectPart part2 = m_scene.GetSceneObjectPart("obj1-Part2");
358 Assert.That(part2.LinkNum, Is.EqualTo(2));
359
360 SceneObjectPart part3 = m_scene.GetSceneObjectPart("obj1-Part3");
361 Assert.That(part3.LinkNum, Is.EqualTo(3));
362 }
363
364 /// <summary>
365 /// Test loading an OpenSim Region Archive. 335 /// Test loading an OpenSim Region Archive.
366 /// </summary> 336 /// </summary>
367 [Test] 337 [Test]
@@ -435,50 +405,57 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
435 TestLoadedRegion(part1, soundItemName, soundData); 405 TestLoadedRegion(part1, soundItemName, soundData);
436 } 406 }
437 407
438 private static void CreateSoundAsset(TarArchiveWriter tar, Assembly assembly, string soundDataResourceName, out byte[] soundData, out UUID soundUuid) 408 /// <summary>
409 /// Test loading an OpenSim Region Archive where the scene object parts are not ordered by link number (e.g.
410 /// 2 can come after 3).
411 /// </summary>
412 [Test]
413 public void TestLoadOarUnorderedParts()
439 { 414 {
440 using (Stream resource = assembly.GetManifestResourceStream(soundDataResourceName)) 415 TestHelpers.InMethod();
441 {
442 using (BinaryReader br = new BinaryReader(resource))
443 {
444 // FIXME: Use the inspector instead
445 soundData = br.ReadBytes(99999999);
446 soundUuid = UUID.Parse("00000000-0000-0000-0000-000000000001");
447 string soundAssetFileName
448 = ArchiveConstants.ASSETS_PATH + soundUuid
449 + ArchiveConstants.ASSET_TYPE_TO_EXTENSION[(sbyte)AssetType.SoundWAV];
450 tar.WriteFile(soundAssetFileName, soundData);
451 416
452 /* 417 UUID ownerId = TestHelpers.ParseTail(0xaaaa);
453 AssetBase soundAsset = AssetHelpers.CreateAsset(soundUuid, soundData);
454 scene.AssetService.Store(soundAsset);
455 asset1FileName = ArchiveConstants.ASSETS_PATH + soundUuid + ".wav";
456 */
457 }
458 }
459 }
460 418
461 private void TestLoadedRegion(SceneObjectPart part1, string soundItemName, byte[] soundData) 419 MemoryStream archiveWriteStream = new MemoryStream();
462 { 420 TarArchiveWriter tar = new TarArchiveWriter(archiveWriteStream);
463 SceneObjectPart object1PartLoaded = m_scene.GetSceneObjectPart(part1.Name);
464 421
465 Assert.That(object1PartLoaded, Is.Not.Null, "object1 was not loaded"); 422 tar.WriteFile(
466 Assert.That(object1PartLoaded.Name, Is.EqualTo(part1.Name), "object1 names not identical"); 423 ArchiveConstants.CONTROL_FILE_PATH,
467 Assert.That(object1PartLoaded.GroupPosition, Is.EqualTo(part1.GroupPosition), "object1 group position not equal"); 424 new ArchiveWriteRequest(m_scene, (Stream)null, Guid.Empty).CreateControlFile(new ArchiveScenesGroup()));
468 Assert.That(
469 object1PartLoaded.RotationOffset, Is.EqualTo(part1.RotationOffset), "object1 rotation offset not equal");
470 Assert.That(
471 object1PartLoaded.OffsetPosition, Is.EqualTo(part1.OffsetPosition), "object1 offset position not equal");
472 Assert.That(object1PartLoaded.SitTargetOrientation, Is.EqualTo(part1.SitTargetOrientation));
473 Assert.That(object1PartLoaded.SitTargetPosition, Is.EqualTo(part1.SitTargetPosition));
474 425
475 TaskInventoryItem loadedSoundItem = object1PartLoaded.Inventory.GetInventoryItems(soundItemName)[0]; 426 SceneObjectGroup sog1 = SceneHelpers.CreateSceneObject(1, ownerId, "obj1-", 0x11);
476 Assert.That(loadedSoundItem, Is.Not.Null, "loaded sound item was null"); 427 SceneObjectPart sop2
477 AssetBase loadedSoundAsset = m_scene.AssetService.Get(loadedSoundItem.AssetID.ToString()); 428 = SceneHelpers.CreateSceneObjectPart("obj1-Part2", TestHelpers.ParseTail(0x12), ownerId);
478 Assert.That(loadedSoundAsset, Is.Not.Null, "loaded sound asset was null"); 429 SceneObjectPart sop3
479 Assert.That(loadedSoundAsset.Data, Is.EqualTo(soundData), "saved and loaded sound data do not match"); 430 = SceneHelpers.CreateSceneObjectPart("obj1-Part3", TestHelpers.ParseTail(0x13), ownerId);
480 431
481 Assert.Greater(m_scene.LandChannel.AllParcels().Count, 0, "incorrect number of parcels"); 432 // Add the parts so they will be written out in reverse order to the oar
433 sog1.AddPart(sop3);
434 sop3.LinkNum = 3;
435 sog1.AddPart(sop2);
436 sop2.LinkNum = 2;
437
438 tar.WriteFile(
439 ArchiveConstants.CreateOarObjectPath(sog1.Name, sog1.UUID, sog1.AbsolutePosition),
440 SceneObjectSerializer.ToXml2Format(sog1));
441
442 tar.Close();
443
444 MemoryStream archiveReadStream = new MemoryStream(archiveWriteStream.ToArray());
445
446 lock (this)
447 {
448 m_scene.EventManager.OnOarFileLoaded += LoadCompleted;
449 m_archiverModule.DearchiveRegion(archiveReadStream);
450 }
451
452 Assert.That(m_lastErrorMessage, Is.Null);
453
454 SceneObjectPart part2 = m_scene.GetSceneObjectPart("obj1-Part2");
455 Assert.That(part2.LinkNum, Is.EqualTo(2));
456
457 SceneObjectPart part3 = m_scene.GetSceneObjectPart("obj1-Part3");
458 Assert.That(part3.LinkNum, Is.EqualTo(3));
482 } 459 }
483 460
484 /// <summary> 461 /// <summary>
@@ -538,8 +515,8 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
538 SerialiserModule serialiserModule = new SerialiserModule(); 515 SerialiserModule serialiserModule = new SerialiserModule();
539 TerrainModule terrainModule = new TerrainModule(); 516 TerrainModule terrainModule = new TerrainModule();
540 517
541 m_sceneHelpers = new SceneHelpers(); 518 SceneHelpers m_sceneHelpers2 = new SceneHelpers();
542 TestScene scene2 = m_sceneHelpers.SetupScene(); 519 TestScene scene2 = m_sceneHelpers2.SetupScene();
543 SceneHelpers.SetupSceneModules(scene2, archiverModule, serialiserModule, terrainModule); 520 SceneHelpers.SetupSceneModules(scene2, archiverModule, serialiserModule, terrainModule);
544 521
545 // Make sure there's a valid owner for the owner we saved (this should have been wiped if the code is 522 // Make sure there's a valid owner for the owner we saved (this should have been wiped if the code is
@@ -563,6 +540,71 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
563 } 540 }
564 541
565 /// <summary> 542 /// <summary>
543 /// Test OAR loading where the land parcel is group deeded.
544 /// </summary>
545 /// <remarks>
546 /// In this situation, the owner ID is set to the group ID.
547 /// </remarks>
548 [Test]
549 public void TestLoadOarDeededLand()
550 {
551 TestHelpers.InMethod();
552// TestHelpers.EnableLogging();
553
554 UUID landID = TestHelpers.ParseTail(0x10);
555
556 MockGroupsServicesConnector groupsService = new MockGroupsServicesConnector();
557
558 IConfigSource configSource = new IniConfigSource();
559 IConfig config = configSource.AddConfig("Groups");
560 config.Set("Enabled", true);
561 config.Set("Module", "GroupsModule");
562 config.Set("DebugEnabled", true);
563 SceneHelpers.SetupSceneModules(
564 m_scene, configSource, new object[] { new GroupsModule(), groupsService, new LandManagementModule() });
565
566 // Create group in scene for loading
567 // FIXME: For now we'll put up with the issue that we'll get a group ID that varies across tests.
568 UUID groupID
569 = groupsService.CreateGroup(UUID.Zero, "group1", "", true, UUID.Zero, 3, true, true, true, UUID.Zero);
570
571 // Construct OAR
572 MemoryStream oarStream = new MemoryStream();
573 TarArchiveWriter tar = new TarArchiveWriter(oarStream);
574
575 tar.WriteDir(ArchiveConstants.LANDDATA_PATH);
576 tar.WriteFile(
577 ArchiveConstants.CONTROL_FILE_PATH,
578 new ArchiveWriteRequest(m_scene, (Stream)null, Guid.Empty).CreateControlFile(new ArchiveScenesGroup()));
579
580 LandObject lo = new LandObject(groupID, true, null);
581 lo.SetLandBitmap(lo.BasicFullRegionLandBitmap());
582 LandData ld = lo.LandData;
583 ld.GlobalID = landID;
584
585 string ldPath = ArchiveConstants.CreateOarLandDataPath(ld);
586 tar.WriteFile(ldPath, LandDataSerializer.Serialize(ld, null));
587 tar.Close();
588
589 oarStream = new MemoryStream(oarStream.ToArray());
590
591 // Load OAR
592 lock (this)
593 {
594 m_scene.EventManager.OnOarFileLoaded += LoadCompleted;
595 m_archiverModule.DearchiveRegion(oarStream);
596 }
597
598 ILandObject rLo = m_scene.LandChannel.GetLandObject(16, 16);
599 LandData rLd = rLo.LandData;
600
601 Assert.That(rLd.GlobalID, Is.EqualTo(landID));
602 Assert.That(rLd.OwnerID, Is.EqualTo(groupID));
603 Assert.That(rLd.GroupID, Is.EqualTo(groupID));
604 Assert.That(rLd.IsGroupOwned, Is.EqualTo(true));
605 }
606
607 /// <summary>
566 /// Test loading the region settings of an OpenSim Region Archive. 608 /// Test loading the region settings of an OpenSim Region Archive.
567 /// </summary> 609 /// </summary>
568 [Test] 610 [Test]
@@ -781,9 +823,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
781 } 823 }
782 } 824 }
783 825
784
785 // Save OAR 826 // Save OAR
786
787 MemoryStream archiveWriteStream = new MemoryStream(); 827 MemoryStream archiveWriteStream = new MemoryStream();
788 m_scene.EventManager.OnOarFileSaved += SaveCompleted; 828 m_scene.EventManager.OnOarFileSaved += SaveCompleted;
789 829
@@ -800,7 +840,6 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
800 840
801 841
802 // Check that the OAR contains the expected data 842 // Check that the OAR contains the expected data
803
804 Assert.That(m_lastRequestId, Is.EqualTo(requestId)); 843 Assert.That(m_lastRequestId, Is.EqualTo(requestId));
805 844
806 byte[] archive = archiveWriteStream.ToArray(); 845 byte[] archive = archiveWriteStream.ToArray();
@@ -892,7 +931,7 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
892 } 931 }
893 932
894 ArchiveScenesGroup scenesGroup = new ArchiveScenesGroup(); 933 ArchiveScenesGroup scenesGroup = new ArchiveScenesGroup();
895 SceneManager.Instance.ForEachScene(delegate(Scene scene) 934 m_sceneHelpers.SceneManager.ForEachScene(delegate(Scene scene)
896 { 935 {
897 scenesGroup.AddScene(scene); 936 scenesGroup.AddScene(scene);
898 }); 937 });
@@ -950,13 +989,13 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
950 989
951 // Delete the current objects, to test that they're loaded from the OAR and didn't 990 // Delete the current objects, to test that they're loaded from the OAR and didn't
952 // just remain in the scene. 991 // just remain in the scene.
953 SceneManager.Instance.ForEachScene(delegate(Scene scene) 992 m_sceneHelpers.SceneManager.ForEachScene(delegate(Scene scene)
954 { 993 {
955 scene.DeleteAllSceneObjects(); 994 scene.DeleteAllSceneObjects();
956 }); 995 });
957 996
958 // Create a "hole", to test that that the corresponding region isn't loaded from the OAR 997 // Create a "hole", to test that that the corresponding region isn't loaded from the OAR
959 SceneManager.Instance.CloseScene(SceneManager.Instance.Scenes[1]); 998 m_sceneHelpers.SceneManager.CloseScene(SceneManager.Instance.Scenes[1]);
960 999
961 1000
962 // Check thay the OAR file contains the expected data 1001 // Check thay the OAR file contains the expected data
@@ -971,10 +1010,32 @@ namespace OpenSim.Region.CoreModules.World.Archiver.Tests
971 1010
972 Assert.That(m_lastErrorMessage, Is.Null); 1011 Assert.That(m_lastErrorMessage, Is.Null);
973 1012
974 Assert.AreEqual(3, SceneManager.Instance.Scenes.Count); 1013 Assert.AreEqual(3, m_sceneHelpers.SceneManager.Scenes.Count);
975 1014
976 TestLoadedRegion(part1, soundItemName, soundData); 1015 TestLoadedRegion(part1, soundItemName, soundData);
977 } 1016 }
978 1017
1018 private void TestLoadedRegion(SceneObjectPart part1, string soundItemName, byte[] soundData)
1019 {
1020 SceneObjectPart object1PartLoaded = m_scene.GetSceneObjectPart(part1.Name);
1021
1022 Assert.That(object1PartLoaded, Is.Not.Null, "object1 was not loaded");
1023 Assert.That(object1PartLoaded.Name, Is.EqualTo(part1.Name), "object1 names not identical");
1024 Assert.That(object1PartLoaded.GroupPosition, Is.EqualTo(part1.GroupPosition), "object1 group position not equal");
1025 Assert.That(
1026 object1PartLoaded.RotationOffset, Is.EqualTo(part1.RotationOffset), "object1 rotation offset not equal");
1027 Assert.That(
1028 object1PartLoaded.OffsetPosition, Is.EqualTo(part1.OffsetPosition), "object1 offset position not equal");
1029 Assert.That(object1PartLoaded.SitTargetOrientation, Is.EqualTo(part1.SitTargetOrientation));
1030 Assert.That(object1PartLoaded.SitTargetPosition, Is.EqualTo(part1.SitTargetPosition));
1031
1032 TaskInventoryItem loadedSoundItem = object1PartLoaded.Inventory.GetInventoryItems(soundItemName)[0];
1033 Assert.That(loadedSoundItem, Is.Not.Null, "loaded sound item was null");
1034 AssetBase loadedSoundAsset = m_scene.AssetService.Get(loadedSoundItem.AssetID.ToString());
1035 Assert.That(loadedSoundAsset, Is.Not.Null, "loaded sound asset was null");
1036 Assert.That(loadedSoundAsset.Data, Is.EqualTo(soundData), "saved and loaded sound data do not match");
1037
1038 Assert.Greater(m_scene.LandChannel.AllParcels().Count, 0, "incorrect number of parcels");
1039 }
979 } 1040 }
980} 1041} \ No newline at end of file
diff --git a/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs
index dc062b6..267332d 100644
--- a/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs
+++ b/OpenSim/Region/CoreModules/World/Estate/EstateManagementModule.cs
@@ -55,6 +55,11 @@ namespace OpenSim.Region.CoreModules.World.Estate
55 55
56 protected EstateManagementCommands m_commands; 56 protected EstateManagementCommands m_commands;
57 57
58 /// <summary>
59 /// If false, region restart requests from the client are blocked even if they are otherwise legitimate.
60 /// </summary>
61 public bool AllowRegionRestartFromClient { get; set; }
62
58 private EstateTerrainXferHandler TerrainUploader; 63 private EstateTerrainXferHandler TerrainUploader;
59 public TelehubManager m_Telehub; 64 public TelehubManager m_Telehub;
60 65
@@ -64,6 +69,53 @@ namespace OpenSim.Region.CoreModules.World.Estate
64 69
65 private int m_delayCount = 0; 70 private int m_delayCount = 0;
66 71
72 #region Region Module interface
73
74 public string Name { get { return "EstateManagementModule"; } }
75
76 public Type ReplaceableInterface { get { return null; } }
77
78 public void Initialise(IConfigSource source)
79 {
80 AllowRegionRestartFromClient = true;
81
82 IConfig config = source.Configs["EstateManagement"];
83
84 if (config != null)
85 AllowRegionRestartFromClient = config.GetBoolean("AllowRegionRestartFromClient", true);
86 }
87
88 public void AddRegion(Scene scene)
89 {
90 Scene = scene;
91 Scene.RegisterModuleInterface<IEstateModule>(this);
92 Scene.EventManager.OnNewClient += EventManager_OnNewClient;
93 Scene.EventManager.OnRequestChangeWaterHeight += changeWaterHeight;
94
95 m_Telehub = new TelehubManager(scene);
96
97 m_commands = new EstateManagementCommands(this);
98 m_commands.Initialise();
99 }
100
101 public void RemoveRegion(Scene scene) {}
102
103 public void RegionLoaded(Scene scene)
104 {
105 // Sets up the sun module based no the saved Estate and Region Settings
106 // DO NOT REMOVE or the sun will stop working
107 scene.TriggerEstateSunUpdate();
108
109 UserManager = scene.RequestModuleInterface<IUserManagement>();
110 }
111
112 public void Close()
113 {
114 m_commands.Close();
115 }
116
117 #endregion
118
67 #region Packet Data Responders 119 #region Packet Data Responders
68 120
69 private void clientSendDetailedEstateData(IClientAPI remote_client, UUID invoice) 121 private void clientSendDetailedEstateData(IClientAPI remote_client, UUID invoice)
@@ -197,6 +249,7 @@ namespace OpenSim.Region.CoreModules.World.Estate
197 Scene.RegionInfo.RegionSettings.TerrainTexture4 = texture; 249 Scene.RegionInfo.RegionSettings.TerrainTexture4 = texture;
198 break; 250 break;
199 } 251 }
252
200 Scene.RegionInfo.RegionSettings.Save(); 253 Scene.RegionInfo.RegionSettings.Save();
201 TriggerRegionInfoChange(); 254 TriggerRegionInfoChange();
202 sendRegionInfoPacketToAll(); 255 sendRegionInfoPacketToAll();
@@ -228,6 +281,7 @@ namespace OpenSim.Region.CoreModules.World.Estate
228 Scene.RegionInfo.RegionSettings.Elevation2NE = highValue; 281 Scene.RegionInfo.RegionSettings.Elevation2NE = highValue;
229 break; 282 break;
230 } 283 }
284
231 Scene.RegionInfo.RegionSettings.Save(); 285 Scene.RegionInfo.RegionSettings.Save();
232 TriggerRegionInfoChange(); 286 TriggerRegionInfoChange();
233 sendRegionHandshakeToAll(); 287 sendRegionHandshakeToAll();
@@ -268,6 +322,12 @@ namespace OpenSim.Region.CoreModules.World.Estate
268 322
269 private void handleEstateRestartSimRequest(IClientAPI remoteClient, int timeInSeconds) 323 private void handleEstateRestartSimRequest(IClientAPI remoteClient, int timeInSeconds)
270 { 324 {
325 if (!AllowRegionRestartFromClient)
326 {
327 remoteClient.SendAlertMessage("Region restart has been disabled on this simulator.");
328 return;
329 }
330
271 IRestartModule restartModule = Scene.RequestModuleInterface<IRestartModule>(); 331 IRestartModule restartModule = Scene.RequestModuleInterface<IRestartModule>();
272 if (restartModule != null) 332 if (restartModule != null)
273 { 333 {
@@ -352,6 +412,7 @@ namespace OpenSim.Region.CoreModules.World.Estate
352 } 412 }
353 413
354 } 414 }
415
355 if ((estateAccessType & 8) != 0) // User remove 416 if ((estateAccessType & 8) != 0) // User remove
356 { 417 {
357 if (Scene.Permissions.CanIssueEstateCommand(remote_client.AgentId, true)) 418 if (Scene.Permissions.CanIssueEstateCommand(remote_client.AgentId, true))
@@ -383,6 +444,7 @@ namespace OpenSim.Region.CoreModules.World.Estate
383 remote_client.SendAlertMessage("Method EstateAccessDelta Failed, you don't have permissions"); 444 remote_client.SendAlertMessage("Method EstateAccessDelta Failed, you don't have permissions");
384 } 445 }
385 } 446 }
447
386 if ((estateAccessType & 16) != 0) // Group add 448 if ((estateAccessType & 16) != 0) // Group add
387 { 449 {
388 if (Scene.Permissions.CanIssueEstateCommand(remote_client.AgentId, true)) 450 if (Scene.Permissions.CanIssueEstateCommand(remote_client.AgentId, true))
@@ -650,7 +712,7 @@ namespace OpenSim.Region.CoreModules.World.Estate
650 } 712 }
651 } 713 }
652 714
653 public void handleOnEstateManageTelehub (IClientAPI client, UUID invoice, UUID senderID, string cmd, uint param1) 715 public void handleOnEstateManageTelehub(IClientAPI client, UUID invoice, UUID senderID, string cmd, uint param1)
654 { 716 {
655 SceneObjectPart part; 717 SceneObjectPart part;
656 718
@@ -1118,49 +1180,6 @@ namespace OpenSim.Region.CoreModules.World.Estate
1118 1180
1119 #endregion 1181 #endregion
1120 1182
1121 #region Region Module interface
1122
1123 public string Name { get { return "EstateManagementModule"; } }
1124
1125 public Type ReplaceableInterface { get { return null; } }
1126
1127 public void Initialise(IConfigSource source) {}
1128
1129 public void AddRegion(Scene scene)
1130 {
1131 m_regionChangeTimer.AutoReset = false;
1132 m_regionChangeTimer.Interval = 2000;
1133 m_regionChangeTimer.Elapsed += RaiseRegionInfoChange;
1134
1135 Scene = scene;
1136 Scene.RegisterModuleInterface<IEstateModule>(this);
1137 Scene.EventManager.OnNewClient += EventManager_OnNewClient;
1138 Scene.EventManager.OnRequestChangeWaterHeight += changeWaterHeight;
1139
1140 m_Telehub = new TelehubManager(scene);
1141
1142 m_commands = new EstateManagementCommands(this);
1143 m_commands.Initialise();
1144 }
1145
1146 public void RemoveRegion(Scene scene) {}
1147
1148 public void RegionLoaded(Scene scene)
1149 {
1150 // Sets up the sun module based no the saved Estate and Region Settings
1151 // DO NOT REMOVE or the sun will stop working
1152 scene.TriggerEstateSunUpdate();
1153
1154 UserManager = scene.RequestModuleInterface<IUserManagement>();
1155 }
1156
1157 public void Close()
1158 {
1159 m_commands.Close();
1160 }
1161
1162 #endregion
1163
1164 #region Other Functions 1183 #region Other Functions
1165 1184
1166 public void changeWaterHeight(float height) 1185 public void changeWaterHeight(float height)
diff --git a/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs b/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs
index 1193057..281c143 100644
--- a/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs
+++ b/OpenSim/Region/CoreModules/World/Land/LandManagementModule.cs
@@ -1384,9 +1384,7 @@ namespace OpenSim.Region.CoreModules.World.Land
1384 } 1384 }
1385 1385
1386 for (int i = 0; i < data.Count; i++) 1386 for (int i = 0; i < data.Count; i++)
1387 {
1388 IncomingLandObjectFromStorage(data[i]); 1387 IncomingLandObjectFromStorage(data[i]);
1389 }
1390 } 1388 }
1391 1389
1392 public void IncomingLandObjectFromStorage(LandData data) 1390 public void IncomingLandObjectFromStorage(LandData data)
diff --git a/OpenSim/Region/CoreModules/World/Land/LandObject.cs b/OpenSim/Region/CoreModules/World/Land/LandObject.cs
index d5b2adb..8ddff99 100644
--- a/OpenSim/Region/CoreModules/World/Land/LandObject.cs
+++ b/OpenSim/Region/CoreModules/World/Land/LandObject.cs
@@ -748,9 +748,10 @@ namespace OpenSim.Region.CoreModules.World.Land
748 int ty = min_y * 4; 748 int ty = min_y * 4;
749 if (ty > ((int)Constants.RegionSize - 1)) 749 if (ty > ((int)Constants.RegionSize - 1))
750 ty = ((int)Constants.RegionSize - 1); 750 ty = ((int)Constants.RegionSize - 1);
751
751 LandData.AABBMin = 752 LandData.AABBMin =
752 new Vector3((float) (min_x * 4), (float) (min_y * 4), 753 new Vector3(
753 (float) m_scene.Heightmap[tx, ty]); 754 (float)(min_x * 4), (float)(min_y * 4), m_scene != null ? (float)m_scene.Heightmap[tx, ty] : 0);
754 755
755 tx = max_x * 4; 756 tx = max_x * 4;
756 if (tx > ((int)Constants.RegionSize - 1)) 757 if (tx > ((int)Constants.RegionSize - 1))
@@ -758,9 +759,11 @@ namespace OpenSim.Region.CoreModules.World.Land
758 ty = max_y * 4; 759 ty = max_y * 4;
759 if (ty > ((int)Constants.RegionSize - 1)) 760 if (ty > ((int)Constants.RegionSize - 1))
760 ty = ((int)Constants.RegionSize - 1); 761 ty = ((int)Constants.RegionSize - 1);
761 LandData.AABBMax = 762
762 new Vector3((float) (max_x * 4), (float) (max_y * 4), 763 LandData.AABBMax
763 (float) m_scene.Heightmap[tx, ty]); 764 = new Vector3(
765 (float)(max_x * 4), (float)(max_y * 4), m_scene != null ? (float)m_scene.Heightmap[tx, ty] : 0);
766
764 LandData.Area = tempArea; 767 LandData.Area = tempArea;
765 } 768 }
766 769
diff --git a/OpenSim/Region/CoreModules/World/Land/Tests/PrimCountModuleTests.cs b/OpenSim/Region/CoreModules/World/Land/Tests/PrimCountModuleTests.cs
index b5ee4d2..0945b43 100644
--- a/OpenSim/Region/CoreModules/World/Land/Tests/PrimCountModuleTests.cs
+++ b/OpenSim/Region/CoreModules/World/Land/Tests/PrimCountModuleTests.cs
@@ -41,7 +41,7 @@ using OpenSim.Tests.Common.Mock;
41namespace OpenSim.Region.CoreModules.World.Land.Tests 41namespace OpenSim.Region.CoreModules.World.Land.Tests
42{ 42{
43 [TestFixture] 43 [TestFixture]
44 public class PrimCountModuleTests 44 public class PrimCountModuleTests : OpenSimTestCase
45 { 45 {
46 protected UUID m_userId = new UUID("00000000-0000-0000-0000-100000000000"); 46 protected UUID m_userId = new UUID("00000000-0000-0000-0000-100000000000");
47 protected UUID m_groupId = new UUID("00000000-0000-0000-8888-000000000000"); 47 protected UUID m_groupId = new UUID("00000000-0000-0000-8888-000000000000");
@@ -60,8 +60,10 @@ namespace OpenSim.Region.CoreModules.World.Land.Tests
60 protected ILandObject m_lo2; 60 protected ILandObject m_lo2;
61 61
62 [SetUp] 62 [SetUp]
63 public void SetUp() 63 public override void SetUp()
64 { 64 {
65 base.SetUp();
66
65 m_pcm = new PrimCountModule(); 67 m_pcm = new PrimCountModule();
66 LandManagementModule lmm = new LandManagementModule(); 68 LandManagementModule lmm = new LandManagementModule();
67 m_scene = new SceneHelpers().SetupScene(); 69 m_scene = new SceneHelpers().SetupScene();
diff --git a/OpenSim/Region/CoreModules/World/Media/Moap/Tests/MoapTests.cs b/OpenSim/Region/CoreModules/World/Media/Moap/Tests/MoapTests.cs
index 396095a..03a96a4 100644
--- a/OpenSim/Region/CoreModules/World/Media/Moap/Tests/MoapTests.cs
+++ b/OpenSim/Region/CoreModules/World/Media/Moap/Tests/MoapTests.cs
@@ -44,14 +44,16 @@ using OpenSim.Tests.Common.Mock;
44namespace OpenSim.Region.CoreModules.World.Media.Moap.Tests 44namespace OpenSim.Region.CoreModules.World.Media.Moap.Tests
45{ 45{
46 [TestFixture] 46 [TestFixture]
47 public class MoapTests 47 public class MoapTests : OpenSimTestCase
48 { 48 {
49 protected TestScene m_scene; 49 protected TestScene m_scene;
50 protected MoapModule m_module; 50 protected MoapModule m_module;
51 51
52 [SetUp] 52 [SetUp]
53 public void SetUp() 53 public override void SetUp()
54 { 54 {
55 base.SetUp();
56
55 m_module = new MoapModule(); 57 m_module = new MoapModule();
56 m_scene = new SceneHelpers().SetupScene(); 58 m_scene = new SceneHelpers().SetupScene();
57 SceneHelpers.SetupSceneModules(m_scene, m_module); 59 SceneHelpers.SetupSceneModules(m_scene, m_module);
diff --git a/OpenSim/Region/CoreModules/World/Objects/Commands/ObjectCommandsModule.cs b/OpenSim/Region/CoreModules/World/Objects/Commands/ObjectCommandsModule.cs
index ab8f143..7b235ae 100644
--- a/OpenSim/Region/CoreModules/World/Objects/Commands/ObjectCommandsModule.cs
+++ b/OpenSim/Region/CoreModules/World/Objects/Commands/ObjectCommandsModule.cs
@@ -365,7 +365,8 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
365 365
366 if (mainParams.Count < 4) 366 if (mainParams.Count < 4)
367 { 367 {
368 m_console.OutputFormat("Usage: show part id [--full] <UUID-or-localID>"); 368 //m_console.OutputFormat("Usage: show part id [--full] <UUID-or-localID>");
369 m_console.OutputFormat("Usage: show part id <UUID-or-localID>");
369 return; 370 return;
370 } 371 }
371 372
@@ -405,6 +406,7 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
405 406
406 if (mainParams.Count < 5) 407 if (mainParams.Count < 5)
407 { 408 {
409 //m_console.OutputFormat("Usage: show part pos <start-coord> to <end-coord>");
408 m_console.OutputFormat("Usage: show part pos [--full] <start-coord> to <end-coord>"); 410 m_console.OutputFormat("Usage: show part pos [--full] <start-coord> to <end-coord>");
409 return; 411 return;
410 } 412 }
@@ -445,7 +447,8 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
445 447
446 if (mainParams.Count < 4) 448 if (mainParams.Count < 4)
447 { 449 {
448 m_console.OutputFormat("Usage: show part name [--full] [--regex] <name>"); 450 m_console.OutputFormat("Usage: show part name [--regex] <name>");
451 //m_console.OutputFormat("Usage: show part name [--full] [--regex] <name>");
449 return; 452 return;
450 } 453 }
451 454
@@ -577,6 +580,58 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
577 cdl.AddRow("Link number", sop.LinkNum); 580 cdl.AddRow("Link number", sop.LinkNum);
578 cdl.AddRow("Flags", sop.Flags); 581 cdl.AddRow("Flags", sop.Flags);
579 582
583 if (showFull)
584 {
585 PrimitiveBaseShape s = sop.Shape;
586 cdl.AddRow("FlexiDrag", s.FlexiDrag);
587 cdl.AddRow("FlexiEntry", s.FlexiEntry);
588 cdl.AddRow("FlexiForce", string.Format("<{0},{1},{2}>", s.FlexiForceX, s.FlexiForceY, s.FlexiForceZ));
589 cdl.AddRow("FlexiGravity", s.FlexiGravity);
590 cdl.AddRow("FlexiSoftness", s.FlexiSoftness);
591 cdl.AddRow("HollowShape", s.HollowShape);
592 cdl.AddRow(
593 "LightColor",
594 string.Format("<{0},{1},{2},{3}>", s.LightColorR, s.LightColorB, s.LightColorG, s.LightColorA));
595 cdl.AddRow("FlexiDrag", s.LightCutoff);
596 cdl.AddRow("FlexiDrag", s.LightEntry);
597 cdl.AddRow("FlexiDrag", s.LightFalloff);
598 cdl.AddRow("FlexiDrag", s.LightIntensity);
599 cdl.AddRow("FlexiDrag", s.LightRadius);
600 cdl.AddRow("Media", string.Format("{0} entries", s.Media != null ? s.Media.Count.ToString() : "n/a"));
601 cdl.AddRow("PathBegin", s.PathBegin);
602 cdl.AddRow("PathEnd", s.PathEnd);
603 cdl.AddRow("PathCurve", s.PathCurve);
604 cdl.AddRow("PathRadiusOffset", s.PathRadiusOffset);
605 cdl.AddRow("PathRevolutions", s.PathRevolutions);
606 cdl.AddRow("PathScale", string.Format("<{0},{1}>", s.PathScaleX, s.PathScaleY));
607 cdl.AddRow("PathSkew", string.Format("<{0},{1}>", s.PathShearX, s.PathShearY));
608 cdl.AddRow("FlexiDrag", s.PathSkew);
609 cdl.AddRow("PathTaper", string.Format("<{0},{1}>", s.PathTaperX, s.PathTaperY));
610 cdl.AddRow("PathTwist", s.PathTwist);
611 cdl.AddRow("PathTwistBegin", s.PathTwistBegin);
612 cdl.AddRow("PCode", s.PCode);
613 cdl.AddRow("ProfileBegin", s.ProfileBegin);
614 cdl.AddRow("ProfileEnd", s.ProfileEnd);
615 cdl.AddRow("ProfileHollow", s.ProfileHollow);
616 cdl.AddRow("ProfileShape", s.ProfileShape);
617 cdl.AddRow("ProjectionAmbiance", s.ProjectionAmbiance);
618 cdl.AddRow("ProjectionEntry", s.ProjectionEntry);
619 cdl.AddRow("ProjectionFocus", s.ProjectionFocus);
620 cdl.AddRow("ProjectionFOV", s.ProjectionFOV);
621 cdl.AddRow("ProjectionTextureUUID", s.ProjectionTextureUUID);
622 cdl.AddRow("Scale", s.Scale);
623 cdl.AddRow(
624 "SculptData",
625 string.Format("{0} bytes", s.SculptData != null ? s.SculptData.Length.ToString() : "n/a"));
626 cdl.AddRow("SculptEntry", s.SculptEntry);
627 cdl.AddRow("SculptTexture", s.SculptTexture);
628 cdl.AddRow("SculptType", s.SculptType);
629 cdl.AddRow("State", s.State);
630
631 // TODO, unpack and display texture entries
632 //cdl.AddRow("Textures", string.Format("{0} entries", s.Textures.
633 }
634
580 object itemsOutput; 635 object itemsOutput;
581 if (showFull) 636 if (showFull)
582 { 637 {
@@ -588,7 +643,6 @@ namespace OpenSim.Region.CoreModules.World.Objects.Commands
588 itemsOutput = sop.Inventory.Count; 643 itemsOutput = sop.Inventory.Count;
589 } 644 }
590 645
591
592 cdl.AddRow("Items", itemsOutput); 646 cdl.AddRow("Items", itemsOutput);
593 647
594 return sb.Append(cdl.ToString()); 648 return sb.Append(cdl.ToString());
diff --git a/OpenSim/Region/CoreModules/World/Serialiser/Tests/SerialiserTests.cs b/OpenSim/Region/CoreModules/World/Serialiser/Tests/SerialiserTests.cs
index 7825e3e..bcb8e2f 100644
--- a/OpenSim/Region/CoreModules/World/Serialiser/Tests/SerialiserTests.cs
+++ b/OpenSim/Region/CoreModules/World/Serialiser/Tests/SerialiserTests.cs
@@ -39,7 +39,7 @@ using OpenSim.Tests.Common;
39namespace OpenSim.Region.CoreModules.World.Serialiser.Tests 39namespace OpenSim.Region.CoreModules.World.Serialiser.Tests
40{ 40{
41 [TestFixture] 41 [TestFixture]
42 public class SerialiserTests 42 public class SerialiserTests : OpenSimTestCase
43 { 43 {
44 private string xml = @" 44 private string xml = @"
45 <SceneObjectGroup> 45 <SceneObjectGroup>
diff --git a/OpenSim/Region/CoreModules/World/Sound/SoundModule.cs b/OpenSim/Region/CoreModules/World/Sound/SoundModule.cs
index 513a8f5..883045a 100644
--- a/OpenSim/Region/CoreModules/World/Sound/SoundModule.cs
+++ b/OpenSim/Region/CoreModules/World/Sound/SoundModule.cs
@@ -43,8 +43,8 @@ namespace OpenSim.Region.CoreModules.World.Sound
43 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "SoundModule")] 43 [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "SoundModule")]
44 public class SoundModule : INonSharedRegionModule, ISoundModule 44 public class SoundModule : INonSharedRegionModule, ISoundModule
45 { 45 {
46 private static readonly ILog m_log = LogManager.GetLogger( 46// private static readonly ILog m_log = LogManager.GetLogger(
47 MethodBase.GetCurrentMethod().DeclaringType); 47// MethodBase.GetCurrentMethod().DeclaringType);
48 48
49 private Scene m_scene; 49 private Scene m_scene;
50 50
@@ -76,7 +76,7 @@ namespace OpenSim.Region.CoreModules.World.Sound
76 76
77 public void RemoveRegion(Scene scene) 77 public void RemoveRegion(Scene scene)
78 { 78 {
79 m_scene.EventManager.OnClientLogin -= OnNewClient; 79 m_scene.EventManager.OnNewClient -= OnNewClient;
80 } 80 }
81 81
82 public void RegionLoaded(Scene scene) 82 public void RegionLoaded(Scene scene)
@@ -85,7 +85,7 @@ namespace OpenSim.Region.CoreModules.World.Sound
85 return; 85 return;
86 86
87 m_scene = scene; 87 m_scene = scene;
88 m_scene.EventManager.OnClientLogin += OnNewClient; 88 m_scene.EventManager.OnNewClient += OnNewClient;
89 89
90 m_scene.RegisterModuleInterface<ISoundModule>(this); 90 m_scene.RegisterModuleInterface<ISoundModule>(this);
91 } 91 }
diff --git a/OpenSim/Region/CoreModules/World/Terrain/Tests/TerrainTest.cs b/OpenSim/Region/CoreModules/World/Terrain/Tests/TerrainTest.cs
index 3d4f762..be719ea 100644
--- a/OpenSim/Region/CoreModules/World/Terrain/Tests/TerrainTest.cs
+++ b/OpenSim/Region/CoreModules/World/Terrain/Tests/TerrainTest.cs
@@ -30,11 +30,12 @@ using NUnit.Framework;
30using OpenSim.Framework; 30using OpenSim.Framework;
31using OpenSim.Region.CoreModules.World.Terrain.PaintBrushes; 31using OpenSim.Region.CoreModules.World.Terrain.PaintBrushes;
32using OpenSim.Region.Framework.Scenes; 32using OpenSim.Region.Framework.Scenes;
33using OpenSim.Tests.Common;
33 34
34namespace OpenSim.Region.CoreModules.World.Terrain.Tests 35namespace OpenSim.Region.CoreModules.World.Terrain.Tests
35{ 36{
36 [TestFixture] 37 [TestFixture]
37 public class TerrainTest 38 public class TerrainTest : OpenSimTestCase
38 { 39 {
39 [Test] 40 [Test]
40 public void BrushTest() 41 public void BrushTest()
diff --git a/OpenSim/Region/CoreModules/World/Wind/WindModule.cs b/OpenSim/Region/CoreModules/World/Wind/WindModule.cs
index fd8e2b4..9de588c 100644
--- a/OpenSim/Region/CoreModules/World/Wind/WindModule.cs
+++ b/OpenSim/Region/CoreModules/World/Wind/WindModule.cs
@@ -66,7 +66,7 @@ namespace OpenSim.Region.CoreModules
66 public void Initialise(IConfigSource config) 66 public void Initialise(IConfigSource config)
67 { 67 {
68 m_windConfig = config.Configs["Wind"]; 68 m_windConfig = config.Configs["Wind"];
69 string desiredWindPlugin = m_dWindPluginName; 69// string desiredWindPlugin = m_dWindPluginName;
70 70
71 if (m_windConfig != null) 71 if (m_windConfig != null)
72 { 72 {
diff --git a/OpenSim/Region/Framework/Interfaces/IXmlRpcRouter.cs b/OpenSim/Region/Framework/Interfaces/IXmlRpcRouter.cs
index 6db6674..093d3f0 100644
--- a/OpenSim/Region/Framework/Interfaces/IXmlRpcRouter.cs
+++ b/OpenSim/Region/Framework/Interfaces/IXmlRpcRouter.cs
@@ -34,5 +34,6 @@ namespace OpenSim.Region.Framework.Interfaces
34 void RegisterNewReceiver(IScriptModule scriptEngine, UUID channelID, UUID objectID, UUID itemID, string url); 34 void RegisterNewReceiver(IScriptModule scriptEngine, UUID channelID, UUID objectID, UUID itemID, string url);
35 void ScriptRemoved(UUID itemID); 35 void ScriptRemoved(UUID itemID);
36 void ObjectRemoved(UUID objectID); 36 void ObjectRemoved(UUID objectID);
37 void UnRegisterReceiver(string channelID, UUID itemID);
37 } 38 }
38} 39}
diff --git a/OpenSim/Region/Framework/Scenes/Animation/ScenePresenceAnimator.cs b/OpenSim/Region/Framework/Scenes/Animation/ScenePresenceAnimator.cs
index 9458079..d18571c 100644
--- a/OpenSim/Region/Framework/Scenes/Animation/ScenePresenceAnimator.cs
+++ b/OpenSim/Region/Framework/Scenes/Animation/ScenePresenceAnimator.cs
@@ -26,9 +26,10 @@
26 */ 26 */
27 27
28using System; 28using System;
29using System.Threading;
30using System.Collections.Generic; 29using System.Collections.Generic;
30using System.Linq;
31using System.Reflection; 31using System.Reflection;
32using System.Threading;
32using log4net; 33using log4net;
33using OpenMetaverse; 34using OpenMetaverse;
34using OpenSim.Framework; 35using OpenSim.Framework;
@@ -113,6 +114,8 @@ namespace OpenSim.Region.Framework.Scenes.Animation
113 if (m_scenePresence.IsChildAgent) 114 if (m_scenePresence.IsChildAgent)
114 return; 115 return;
115 116
117// m_log.DebugFormat("[SCENE PRESENCE ANIMATOR]: Removing animation {0} for {1}", animID, m_scenePresence.Name);
118
116 if (m_animations.Remove(animID)) 119 if (m_animations.Remove(animID))
117 SendAnimPack(); 120 SendAnimPack();
118 } 121 }
@@ -519,6 +522,12 @@ namespace OpenSim.Region.Framework.Scenes.Animation
519 if (m_scenePresence.IsChildAgent) 522 if (m_scenePresence.IsChildAgent)
520 return; 523 return;
521 524
525// m_log.DebugFormat(
526// "[SCENE PRESENCE ANIMATOR]: Sending anim pack with animations '{0}', sequence '{1}', uuids '{2}'",
527// string.Join(",", Array.ConvertAll<UUID, string>(animations, a => a.ToString())),
528// string.Join(",", Array.ConvertAll<int, string>(seqs, s => s.ToString())),
529// string.Join(",", Array.ConvertAll<UUID, string>(objectIDs, o => o.ToString())));
530
522 m_scenePresence.Scene.ForEachClient( 531 m_scenePresence.Scene.ForEachClient(
523 delegate(IClientAPI client) 532 delegate(IClientAPI client)
524 { 533 {
diff --git a/OpenSim/Region/Framework/Scenes/EntityManager.cs b/OpenSim/Region/Framework/Scenes/EntityManager.cs
index b788a3c..7181313 100644
--- a/OpenSim/Region/Framework/Scenes/EntityManager.cs
+++ b/OpenSim/Region/Framework/Scenes/EntityManager.cs
@@ -31,6 +31,7 @@ using System.Collections.Generic;
31using System.Reflection; 31using System.Reflection;
32using log4net; 32using log4net;
33using OpenMetaverse; 33using OpenMetaverse;
34using OpenSim.Framework;
34 35
35namespace OpenSim.Region.Framework.Scenes 36namespace OpenSim.Region.Framework.Scenes
36{ 37{
@@ -38,7 +39,8 @@ namespace OpenSim.Region.Framework.Scenes
38 { 39 {
39// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 40// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
40 41
41 private readonly DoubleDictionary<UUID, uint, EntityBase> m_entities = new DoubleDictionary<UUID, uint, EntityBase>(); 42 private readonly DoubleDictionaryThreadAbortSafe<UUID, uint, EntityBase> m_entities
43 = new DoubleDictionaryThreadAbortSafe<UUID, uint, EntityBase>();
42 44
43 public int Count 45 public int Count
44 { 46 {
diff --git a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs
index c9d1205..65c50bf 100644
--- a/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs
+++ b/OpenSim/Region/Framework/Scenes/Scene.Inventory.cs
@@ -683,12 +683,10 @@ namespace OpenSim.Region.Framework.Scenes
683 itemCopy.SalePrice = item.SalePrice; 683 itemCopy.SalePrice = item.SalePrice;
684 itemCopy.SaleType = item.SaleType; 684 itemCopy.SaleType = item.SaleType;
685 685
686 if (AddInventoryItem(itemCopy)) 686 IInventoryAccessModule invAccess = RequestModuleInterface<IInventoryAccessModule>();
687 { 687 if (invAccess != null)
688 IInventoryAccessModule invAccess = RequestModuleInterface<IInventoryAccessModule>(); 688 invAccess.TransferInventoryAssets(itemCopy, senderId, recipient);
689 if (invAccess != null) 689 AddInventoryItem(itemCopy);
690 Util.FireAndForget(delegate { invAccess.TransferInventoryAssets(itemCopy, senderId, recipient); });
691 }
692 690
693 if (!Permissions.BypassPermissions()) 691 if (!Permissions.BypassPermissions())
694 { 692 {
diff --git a/OpenSim/Region/Framework/Scenes/Scene.cs b/OpenSim/Region/Framework/Scenes/Scene.cs
index c99e37e..2a1949d 100644
--- a/OpenSim/Region/Framework/Scenes/Scene.cs
+++ b/OpenSim/Region/Framework/Scenes/Scene.cs
@@ -762,7 +762,12 @@ namespace OpenSim.Region.Framework.Scenes
762 // 762 //
763 // Out of memory 763 // Out of memory
764 // Operating system has killed the plugin 764 // Operating system has killed the plugin
765 m_sceneGraph.UnRecoverableError += RestartNow; 765 m_sceneGraph.UnRecoverableError
766 += () =>
767 {
768 m_log.ErrorFormat("[SCENE]: Restarting region {0} due to unrecoverable physics crash", Name);
769 RestartNow();
770 };
766 771
767 RegisterDefaultSceneEvents(); 772 RegisterDefaultSceneEvents();
768 773
@@ -6036,10 +6041,17 @@ Environment.Exit(1);
6036 GC.Collect(); 6041 GC.Collect();
6037 } 6042 }
6038 6043
6039 // Wrappers to get physics modules retrieve assets. Has to be done this way 6044 /// <summary>
6040 // because we can't assign the asset service to physics directly - at the 6045 /// Wrappers to get physics modules retrieve assets.
6041 // time physics are instantiated it's not registered but it will be by 6046 /// </summary>
6042 // the time the first prim exists. 6047 /// <remarks>
6048 /// Has to be done this way
6049 /// because we can't assign the asset service to physics directly - at the
6050 /// time physics are instantiated it's not registered but it will be by
6051 /// the time the first prim exists.
6052 /// </remarks>
6053 /// <param name="assetID"></param>
6054 /// <param name="callback"></param>
6043 public void PhysicsRequestAsset(UUID assetID, AssetReceivedDelegate callback) 6055 public void PhysicsRequestAsset(UUID assetID, AssetReceivedDelegate callback)
6044 { 6056 {
6045 AssetService.Get(assetID.ToString(), callback, PhysicsAssetReceived); 6057 AssetService.Get(assetID.ToString(), callback, PhysicsAssetReceived);
diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs
index 4ad8b11..2051a53 100644
--- a/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs
+++ b/OpenSim/Region/Framework/Scenes/SceneObjectGroup.cs
@@ -505,11 +505,19 @@ namespace OpenSim.Region.Framework.Scenes
505 505
506 if (Scene != null) 506 if (Scene != null)
507 { 507 {
508 // if ((Scene.TestBorderCross(val - Vector3.UnitX, Cardinals.E) || Scene.TestBorderCross(val + Vector3.UnitX, Cardinals.W) 508 if (
509 // || Scene.TestBorderCross(val - Vector3.UnitY, Cardinals.N) || Scene.TestBorderCross(val + Vector3.UnitY, Cardinals.S)) 509 // (Scene.TestBorderCross(val - Vector3.UnitX, Cardinals.E)
510 // && !IsAttachmentCheckFull() && (!Scene.LoadingPrims)) 510 // || Scene.TestBorderCross(val + Vector3.UnitX, Cardinals.W)
511 if ((Scene.TestBorderCross(val, Cardinals.E) || Scene.TestBorderCross(val, Cardinals.W) 511 // || Scene.TestBorderCross(val - Vector3.UnitY, Cardinals.N)
512 || Scene.TestBorderCross(val, Cardinals.N) || Scene.TestBorderCross(val, Cardinals.S)) 512 // || Scene.TestBorderCross(val + Vector3.UnitY, Cardinals.S))
513 // Experimental change for better border crossings.
514 // The commented out original lines above would, it seems, trigger
515 // a border crossing a little early or late depending on which
516 // direction the object was moving.
517 (Scene.TestBorderCross(val, Cardinals.E)
518 || Scene.TestBorderCross(val, Cardinals.W)
519 || Scene.TestBorderCross(val, Cardinals.N)
520 || Scene.TestBorderCross(val, Cardinals.S))
513 && !IsAttachmentCheckFull() && (!Scene.LoadingPrims)) 521 && !IsAttachmentCheckFull() && (!Scene.LoadingPrims))
514 { 522 {
515 IEntityTransferModule entityTransfer = m_scene.RequestModuleInterface<IEntityTransferModule>(); 523 IEntityTransferModule entityTransfer = m_scene.RequestModuleInterface<IEntityTransferModule>();
diff --git a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs
index 2191cfa..c746690 100644
--- a/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs
+++ b/OpenSim/Region/Framework/Scenes/SceneObjectPart.cs
@@ -2895,11 +2895,14 @@ namespace OpenSim.Region.Framework.Scenes
2895 2895
2896 public void PhysicsOutOfBounds(Vector3 pos) 2896 public void PhysicsOutOfBounds(Vector3 pos)
2897 { 2897 {
2898 m_log.Error("[PHYSICS]: Physical Object went out of bounds."); 2898 // Note: This is only being called on the root prim at this time.
2899
2900 m_log.ErrorFormat(
2901 "[SCENE OBJECT PART]: Physical object {0}, localID {1} went out of bounds at {2} in {3}. Stopping at {4} and making non-physical.",
2902 Name, LocalId, pos, ParentGroup.Scene.Name, AbsolutePosition);
2899 2903
2900 RemFlag(PrimFlags.Physics); 2904 RemFlag(PrimFlags.Physics);
2901 DoPhysicsPropertyUpdate(false, true); 2905 DoPhysicsPropertyUpdate(false, true);
2902 //ParentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(PhysActor);
2903 } 2906 }
2904 2907
2905 public void PhysicsRequestingTerseUpdate() 2908 public void PhysicsRequestingTerseUpdate()
@@ -4549,7 +4552,7 @@ namespace OpenSim.Region.Framework.Scenes
4549 if (ParentGroup.RootPart == this) 4552 if (ParentGroup.RootPart == this)
4550 AngularVelocity = new Vector3(0, 0, 0); 4553 AngularVelocity = new Vector3(0, 0, 0);
4551 } 4554 }
4552 else 4555 else if (SetVD != wasVD)
4553 { 4556 {
4554 if (ParentGroup.Scene.CollidablePrims) 4557 if (ParentGroup.Scene.CollidablePrims)
4555 { 4558 {
diff --git a/OpenSim/Region/Framework/Scenes/ScenePresence.cs b/OpenSim/Region/Framework/Scenes/ScenePresence.cs
index 7ff163b..041eac2 100644
--- a/OpenSim/Region/Framework/Scenes/ScenePresence.cs
+++ b/OpenSim/Region/Framework/Scenes/ScenePresence.cs
@@ -216,8 +216,6 @@ namespace OpenSim.Region.Framework.Scenes
216 216
217 private Quaternion m_headrotation = Quaternion.Identity; 217 private Quaternion m_headrotation = Quaternion.Identity;
218 218
219 private string m_nextSitAnimation = String.Empty;
220
221 //PauPaw:Proper PID Controler for autopilot************ 219 //PauPaw:Proper PID Controler for autopilot************
222 public bool MovingToTarget { get; private set; } 220 public bool MovingToTarget { get; private set; }
223 public Vector3 MoveToPositionTarget { get; private set; } 221 public Vector3 MoveToPositionTarget { get; private set; }
@@ -2174,25 +2172,10 @@ namespace OpenSim.Region.Framework.Scenes
2174 StandUp(); 2172 StandUp();
2175 } 2173 }
2176 2174
2177// if (!String.IsNullOrEmpty(sitAnimation))
2178// {
2179// m_nextSitAnimation = sitAnimation;
2180// }
2181// else
2182// {
2183 m_nextSitAnimation = "SIT";
2184// }
2185
2186 //SceneObjectPart part = m_scene.GetSceneObjectPart(targetID);
2187 SceneObjectPart part = FindNextAvailableSitTarget(targetID); 2175 SceneObjectPart part = FindNextAvailableSitTarget(targetID);
2188 2176
2189 if (part != null) 2177 if (part != null)
2190 { 2178 {
2191 if (!String.IsNullOrEmpty(part.SitAnimation))
2192 {
2193 m_nextSitAnimation = part.SitAnimation;
2194 }
2195
2196 m_requestedSitTargetID = part.LocalId; 2179 m_requestedSitTargetID = part.LocalId;
2197 m_requestedSitTargetUUID = targetID; 2180 m_requestedSitTargetUUID = targetID;
2198 2181
@@ -2264,18 +2247,6 @@ namespace OpenSim.Region.Framework.Scenes
2264 2247
2265 public void HandleAgentSit(IClientAPI remoteClient, UUID agentID) 2248 public void HandleAgentSit(IClientAPI remoteClient, UUID agentID)
2266 { 2249 {
2267 if (!String.IsNullOrEmpty(m_nextSitAnimation))
2268 {
2269 HandleAgentSit(remoteClient, agentID, m_nextSitAnimation);
2270 }
2271 else
2272 {
2273 HandleAgentSit(remoteClient, agentID, "SIT");
2274 }
2275 }
2276
2277 public void HandleAgentSit(IClientAPI remoteClient, UUID agentID, string sitAnimation)
2278 {
2279 SceneObjectPart part = m_scene.GetSceneObjectPart(m_requestedSitTargetID); 2250 SceneObjectPart part = m_scene.GetSceneObjectPart(m_requestedSitTargetID);
2280 2251
2281 if (part != null) 2252 if (part != null)
@@ -2347,7 +2318,12 @@ namespace OpenSim.Region.Framework.Scenes
2347 2318
2348 Velocity = Vector3.Zero; 2319 Velocity = Vector3.Zero;
2349 RemoveFromPhysicalScene(); 2320 RemoveFromPhysicalScene();
2350 2321
2322 String sitAnimation = "SIT";
2323 if (!String.IsNullOrEmpty(part.SitAnimation))
2324 {
2325 sitAnimation = part.SitAnimation;
2326 }
2351 Animator.TrySetMovementAnimation(sitAnimation); 2327 Animator.TrySetMovementAnimation(sitAnimation);
2352 SendAvatarDataToAllAgents(); 2328 SendAvatarDataToAllAgents();
2353 } 2329 }
diff --git a/OpenSim/Region/Framework/Scenes/Tests/BorderTests.cs b/OpenSim/Region/Framework/Scenes/Tests/BorderTests.cs
index 4a21dc9..e209221 100644
--- a/OpenSim/Region/Framework/Scenes/Tests/BorderTests.cs
+++ b/OpenSim/Region/Framework/Scenes/Tests/BorderTests.cs
@@ -37,7 +37,7 @@ using OpenSim.Tests.Common;
37namespace OpenSim.Region.Framework.Scenes.Tests 37namespace OpenSim.Region.Framework.Scenes.Tests
38{ 38{
39 [TestFixture] 39 [TestFixture]
40 public class BorderTests 40 public class BorderTests : OpenSimTestCase
41 { 41 {
42 [Test] 42 [Test]
43 public void TestCross() 43 public void TestCross()
diff --git a/OpenSim/Region/Framework/Scenes/Tests/EntityManagerTests.cs b/OpenSim/Region/Framework/Scenes/Tests/EntityManagerTests.cs
index ea9fc93..766ce83 100644
--- a/OpenSim/Region/Framework/Scenes/Tests/EntityManagerTests.cs
+++ b/OpenSim/Region/Framework/Scenes/Tests/EntityManagerTests.cs
@@ -41,7 +41,7 @@ using OpenSim.Tests.Common;
41namespace OpenSim.Region.Framework.Scenes.Tests 41namespace OpenSim.Region.Framework.Scenes.Tests
42{ 42{
43 [TestFixture, LongRunning] 43 [TestFixture, LongRunning]
44 public class EntityManagerTests 44 public class EntityManagerTests : OpenSimTestCase
45 { 45 {
46 static public Random random; 46 static public Random random;
47 SceneObjectGroup found; 47 SceneObjectGroup found;
diff --git a/OpenSim/Region/Framework/Scenes/Tests/SceneGraphTests.cs b/OpenSim/Region/Framework/Scenes/Tests/SceneGraphTests.cs
index d23c965..575a081 100644
--- a/OpenSim/Region/Framework/Scenes/Tests/SceneGraphTests.cs
+++ b/OpenSim/Region/Framework/Scenes/Tests/SceneGraphTests.cs
@@ -40,7 +40,7 @@ using OpenSim.Tests.Common.Mock;
40namespace OpenSim.Region.Framework.Scenes.Tests 40namespace OpenSim.Region.Framework.Scenes.Tests
41{ 41{
42 [TestFixture] 42 [TestFixture]
43 public class SceneGraphTests 43 public class SceneGraphTests : OpenSimTestCase
44 { 44 {
45 [Test] 45 [Test]
46 public void TestDuplicateObject() 46 public void TestDuplicateObject()
diff --git a/OpenSim/Region/Framework/Scenes/Tests/SceneManagerTests.cs b/OpenSim/Region/Framework/Scenes/Tests/SceneManagerTests.cs
index ab56f4e..2d831fa 100644
--- a/OpenSim/Region/Framework/Scenes/Tests/SceneManagerTests.cs
+++ b/OpenSim/Region/Framework/Scenes/Tests/SceneManagerTests.cs
@@ -41,7 +41,7 @@ using OpenSim.Tests.Common.Mock;
41namespace OpenSim.Region.Framework.Scenes.Tests 41namespace OpenSim.Region.Framework.Scenes.Tests
42{ 42{
43 [TestFixture] 43 [TestFixture]
44 public class SceneManagerTests 44 public class SceneManagerTests : OpenSimTestCase
45 { 45 {
46 [Test] 46 [Test]
47 public void TestClose() 47 public void TestClose()
diff --git a/OpenSim/Region/Framework/Scenes/Tests/SceneObjectBasicTests.cs b/OpenSim/Region/Framework/Scenes/Tests/SceneObjectBasicTests.cs
index 5b334c6..a07d64c 100644
--- a/OpenSim/Region/Framework/Scenes/Tests/SceneObjectBasicTests.cs
+++ b/OpenSim/Region/Framework/Scenes/Tests/SceneObjectBasicTests.cs
@@ -29,6 +29,7 @@ using System;
29using System.Collections.Generic; 29using System.Collections.Generic;
30using System.Reflection; 30using System.Reflection;
31using System.Threading; 31using System.Threading;
32using Nini.Config;
32using NUnit.Framework; 33using NUnit.Framework;
33using OpenMetaverse; 34using OpenMetaverse;
34using OpenSim.Framework; 35using OpenSim.Framework;
@@ -182,6 +183,10 @@ namespace OpenSim.Region.Framework.Scenes.Tests
182 /// <summary> 183 /// <summary>
183 /// Test deleting an object from a scene. 184 /// Test deleting an object from a scene.
184 /// </summary> 185 /// </summary>
186 /// <remarks>
187 /// This is the most basic form of delete. For all more sophisticated forms of derez (done asynchrnously
188 /// and where object can be taken to user inventory, etc.), see SceneObjectDeRezTests.
189 /// </remarks>
185 [Test] 190 [Test]
186 public void TestDeleteSceneObject() 191 public void TestDeleteSceneObject()
187 { 192 {
@@ -201,100 +206,6 @@ namespace OpenSim.Region.Framework.Scenes.Tests
201 } 206 }
202 207
203 /// <summary> 208 /// <summary>
204 /// Test deleting an object asynchronously
205 /// </summary>
206 [Test]
207 public void TestDeleteSceneObjectAsync()
208 {
209 TestHelpers.InMethod();
210 //log4net.Config.XmlConfigurator.Configure();
211
212 UUID agentId = UUID.Parse("00000000-0000-0000-0000-000000000001");
213
214 TestScene scene = new SceneHelpers().SetupScene();
215
216 // Turn off the timer on the async sog deleter - we'll crank it by hand for this test.
217 AsyncSceneObjectGroupDeleter sogd = scene.SceneObjectGroupDeleter;
218 sogd.Enabled = false;
219
220 SceneObjectGroup so = SceneHelpers.AddSceneObject(scene);
221
222 IClientAPI client = SceneHelpers.AddScenePresence(scene, agentId).ControllingClient;
223 scene.DeRezObjects(client, new System.Collections.Generic.List<uint>() { so.LocalId }, UUID.Zero, DeRezAction.Delete, UUID.Zero);
224
225 SceneObjectPart retrievedPart = scene.GetSceneObjectPart(so.LocalId);
226
227 Assert.That(retrievedPart, Is.Not.Null);
228
229 Assert.That(so.IsDeleted, Is.False);
230
231 sogd.InventoryDeQueueAndDelete();
232
233 Assert.That(so.IsDeleted, Is.True);
234
235 SceneObjectPart retrievedPart2 = scene.GetSceneObjectPart(so.LocalId);
236 Assert.That(retrievedPart2, Is.Null);
237 }
238
239 /// <summary>
240 /// Test deleting an object asynchronously to user inventory.
241 /// </summary>
242// [Test]
243 public void TestDeleteSceneObjectAsyncToUserInventory()
244 {
245 TestHelpers.InMethod();
246 TestHelpers.EnableLogging();
247
248 UUID agentId = UUID.Parse("00000000-0000-0000-0000-000000000001");
249 string myObjectName = "Fred";
250
251 TestScene scene = new SceneHelpers().SetupScene();
252
253 // Turn off the timer on the async sog deleter - we'll crank it by hand for this test.
254 AsyncSceneObjectGroupDeleter sogd = scene.SceneObjectGroupDeleter;
255 sogd.Enabled = false;
256
257 SceneObjectGroup so = SceneHelpers.AddSceneObject(scene, myObjectName, agentId);
258
259// Assert.That(
260// scene.CommsManager.UserAdminService.AddUser(
261// "Bob", "Hoskins", "test", "test@test.com", 1000, 1000, agentId),
262// Is.EqualTo(agentId));
263
264 UserAccount ua = UserAccountHelpers.CreateUserWithInventory(scene, agentId);
265 InventoryFolderBase folder1
266 = UserInventoryHelpers.CreateInventoryFolder(scene.InventoryService, ua.PrincipalID, "folder1");
267
268 IClientAPI client = SceneHelpers.AddScenePresence(scene, agentId).ControllingClient;
269 scene.DeRezObjects(client, new List<uint>() { so.LocalId }, UUID.Zero, DeRezAction.Take, folder1.ID);
270
271 SceneObjectPart retrievedPart = scene.GetSceneObjectPart(so.LocalId);
272
273 Assert.That(retrievedPart, Is.Not.Null);
274 Assert.That(so.IsDeleted, Is.False);
275
276 sogd.InventoryDeQueueAndDelete();
277
278 Assert.That(so.IsDeleted, Is.True);
279
280 SceneObjectPart retrievedPart2 = scene.GetSceneObjectPart(so.LocalId);
281 Assert.That(retrievedPart2, Is.Null);
282
283// SceneSetupHelpers.DeleteSceneObjectAsync(scene, part, DeRezAction.Take, userInfo.RootFolder.ID, client);
284
285 InventoryItemBase retrievedItem
286 = UserInventoryHelpers.GetInventoryItem(
287 scene.InventoryService, ua.PrincipalID, "folder1/" + myObjectName);
288
289 // Check that we now have the taken part in our inventory
290 Assert.That(retrievedItem, Is.Not.Null);
291
292 // Check that the taken part has actually disappeared
293// SceneObjectPart retrievedPart = scene.GetSceneObjectPart(part.LocalId);
294// Assert.That(retrievedPart, Is.Null);
295 }
296
297 /// <summary>
298 /// Changing a scene object uuid changes the root part uuid. This is a valid operation if the object is not 209 /// Changing a scene object uuid changes the root part uuid. This is a valid operation if the object is not
299 /// in a scene and is useful if one wants to supply a UUID directly rather than use the one generated by 210 /// in a scene and is useful if one wants to supply a UUID directly rather than use the one generated by
300 /// OpenSim. 211 /// OpenSim.
@@ -329,4 +240,4 @@ namespace OpenSim.Region.Framework.Scenes.Tests
329 Assert.That(sog.Parts.Length, Is.EqualTo(2)); 240 Assert.That(sog.Parts.Length, Is.EqualTo(2));
330 } 241 }
331 } 242 }
332} 243} \ No newline at end of file
diff --git a/OpenSim/Region/Framework/Scenes/Tests/SceneObjectDeRezTests.cs b/OpenSim/Region/Framework/Scenes/Tests/SceneObjectDeRezTests.cs
index 0076f41..c1522e7 100644
--- a/OpenSim/Region/Framework/Scenes/Tests/SceneObjectDeRezTests.cs
+++ b/OpenSim/Region/Framework/Scenes/Tests/SceneObjectDeRezTests.cs
@@ -33,22 +33,24 @@ using NUnit.Framework;
33using OpenMetaverse; 33using OpenMetaverse;
34using OpenSim.Framework; 34using OpenSim.Framework;
35using OpenSim.Framework.Communications; 35using OpenSim.Framework.Communications;
36using OpenSim.Region.CoreModules.Framework.InventoryAccess;
36using OpenSim.Region.CoreModules.World.Permissions; 37using OpenSim.Region.CoreModules.World.Permissions;
37using OpenSim.Region.Framework.Scenes; 38using OpenSim.Region.Framework.Scenes;
39using OpenSim.Services.Interfaces;
38using OpenSim.Tests.Common; 40using OpenSim.Tests.Common;
39using OpenSim.Tests.Common.Mock; 41using OpenSim.Tests.Common.Mock;
40 42
41namespace OpenSim.Region.Framework.Scenes.Tests 43namespace OpenSim.Region.Framework.Scenes.Tests
42{ 44{
43 /// <summary> 45 /// <summary>
44 /// Tests derez of scene objects by users. 46 /// Tests derez of scene objects.
45 /// </summary> 47 /// </summary>
46 /// <remarks> 48 /// <remarks>
47 /// This is at a level above the SceneObjectBasicTests, which act on the scene directly. 49 /// This is at a level above the SceneObjectBasicTests, which act on the scene directly.
48 /// TODO: These tests are very incomplete - they only test for a few conditions. 50 /// TODO: These tests are incomplete - need to test more kinds of derez (e.g. return object).
49 /// </remarks> 51 /// </remarks>
50 [TestFixture] 52 [TestFixture]
51 public class SceneObjectDeRezTests 53 public class SceneObjectDeRezTests : OpenSimTestCase
52 { 54 {
53 /// <summary> 55 /// <summary>
54 /// Test deleting an object from a scene. 56 /// Test deleting an object from a scene.
@@ -76,14 +78,20 @@ namespace OpenSim.Region.Framework.Scenes.Tests
76 = new SceneObjectPart(userId, PrimitiveBaseShape.Default, Vector3.Zero, Quaternion.Identity, Vector3.Zero); 78 = new SceneObjectPart(userId, PrimitiveBaseShape.Default, Vector3.Zero, Quaternion.Identity, Vector3.Zero);
77 part.Name = "obj1"; 79 part.Name = "obj1";
78 scene.AddNewSceneObject(new SceneObjectGroup(part), false); 80 scene.AddNewSceneObject(new SceneObjectGroup(part), false);
81
79 List<uint> localIds = new List<uint>(); 82 List<uint> localIds = new List<uint>();
80 localIds.Add(part.LocalId); 83 localIds.Add(part.LocalId);
81
82 scene.DeRezObjects(client, localIds, UUID.Zero, DeRezAction.Delete, UUID.Zero); 84 scene.DeRezObjects(client, localIds, UUID.Zero, DeRezAction.Delete, UUID.Zero);
85
86 // Check that object isn't deleted until we crank the sogd handle.
87 SceneObjectPart retrievedPart = scene.GetSceneObjectPart(part.LocalId);
88 Assert.That(retrievedPart, Is.Not.Null);
89 Assert.That(retrievedPart.ParentGroup.IsDeleted, Is.False);
90
83 sogd.InventoryDeQueueAndDelete(); 91 sogd.InventoryDeQueueAndDelete();
84 92
85 SceneObjectPart retrievedPart = scene.GetSceneObjectPart(part.LocalId); 93 SceneObjectPart retrievedPart2 = scene.GetSceneObjectPart(part.LocalId);
86 Assert.That(retrievedPart, Is.Null); 94 Assert.That(retrievedPart2, Is.Null);
87 } 95 }
88 96
89 /// <summary> 97 /// <summary>
@@ -124,6 +132,67 @@ namespace OpenSim.Region.Framework.Scenes.Tests
124 // Object should still be in the scene. 132 // Object should still be in the scene.
125 SceneObjectPart retrievedPart = scene.GetSceneObjectPart(part.LocalId); 133 SceneObjectPart retrievedPart = scene.GetSceneObjectPart(part.LocalId);
126 Assert.That(retrievedPart.UUID, Is.EqualTo(part.UUID)); 134 Assert.That(retrievedPart.UUID, Is.EqualTo(part.UUID));
127 } 135 }
136
137 /// <summary>
138 /// Test deleting an object asynchronously to user inventory.
139 /// </summary>
140 [Test]
141 public void TestDeleteSceneObjectAsyncToUserInventory()
142 {
143 TestHelpers.InMethod();
144// TestHelpers.EnableLogging();
145
146 UUID agentId = UUID.Parse("00000000-0000-0000-0000-000000000001");
147 string myObjectName = "Fred";
148
149 TestScene scene = new SceneHelpers().SetupScene();
150
151 IConfigSource configSource = new IniConfigSource();
152 IConfig config = configSource.AddConfig("Modules");
153 config.Set("InventoryAccessModule", "BasicInventoryAccessModule");
154 SceneHelpers.SetupSceneModules(
155 scene, configSource, new object[] { new BasicInventoryAccessModule() });
156
157 SceneHelpers.SetupSceneModules(scene, new object[] { });
158
159 // Turn off the timer on the async sog deleter - we'll crank it by hand for this test.
160 AsyncSceneObjectGroupDeleter sogd = scene.SceneObjectGroupDeleter;
161 sogd.Enabled = false;
162
163 SceneObjectGroup so = SceneHelpers.AddSceneObject(scene, myObjectName, agentId);
164
165 UserAccount ua = UserAccountHelpers.CreateUserWithInventory(scene, agentId);
166 InventoryFolderBase folder1
167 = UserInventoryHelpers.CreateInventoryFolder(scene.InventoryService, ua.PrincipalID, "folder1");
168
169 IClientAPI client = SceneHelpers.AddScenePresence(scene, agentId).ControllingClient;
170 scene.DeRezObjects(client, new List<uint>() { so.LocalId }, UUID.Zero, DeRezAction.Take, folder1.ID);
171
172 SceneObjectPart retrievedPart = scene.GetSceneObjectPart(so.LocalId);
173
174 Assert.That(retrievedPart, Is.Not.Null);
175 Assert.That(so.IsDeleted, Is.False);
176
177 sogd.InventoryDeQueueAndDelete();
178
179 Assert.That(so.IsDeleted, Is.True);
180
181 SceneObjectPart retrievedPart2 = scene.GetSceneObjectPart(so.LocalId);
182 Assert.That(retrievedPart2, Is.Null);
183
184// SceneSetupHelpers.DeleteSceneObjectAsync(scene, part, DeRezAction.Take, userInfo.RootFolder.ID, client);
185
186 InventoryItemBase retrievedItem
187 = UserInventoryHelpers.GetInventoryItem(
188 scene.InventoryService, ua.PrincipalID, "folder1/" + myObjectName);
189
190 // Check that we now have the taken part in our inventory
191 Assert.That(retrievedItem, Is.Not.Null);
192
193 // Check that the taken part has actually disappeared
194// SceneObjectPart retrievedPart = scene.GetSceneObjectPart(part.LocalId);
195// Assert.That(retrievedPart, Is.Null);
196 }
128 } 197 }
129} \ No newline at end of file 198} \ No newline at end of file
diff --git a/OpenSim/Region/Framework/Scenes/Tests/SceneObjectLinkingTests.cs b/OpenSim/Region/Framework/Scenes/Tests/SceneObjectLinkingTests.cs
index 0e525c9..9378e20 100644
--- a/OpenSim/Region/Framework/Scenes/Tests/SceneObjectLinkingTests.cs
+++ b/OpenSim/Region/Framework/Scenes/Tests/SceneObjectLinkingTests.cs
@@ -40,7 +40,7 @@ using log4net;
40namespace OpenSim.Region.Framework.Scenes.Tests 40namespace OpenSim.Region.Framework.Scenes.Tests
41{ 41{
42 [TestFixture] 42 [TestFixture]
43 public class SceneObjectLinkingTests 43 public class SceneObjectLinkingTests : OpenSimTestCase
44 { 44 {
45 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 45 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
46 46
diff --git a/OpenSim/Region/Framework/Scenes/Tests/SceneObjectResizeTests.cs b/OpenSim/Region/Framework/Scenes/Tests/SceneObjectResizeTests.cs
index e931859..1182c96 100644
--- a/OpenSim/Region/Framework/Scenes/Tests/SceneObjectResizeTests.cs
+++ b/OpenSim/Region/Framework/Scenes/Tests/SceneObjectResizeTests.cs
@@ -41,7 +41,7 @@ namespace OpenSim.Region.Framework.Scenes.Tests
41 /// Basic scene object resize tests 41 /// Basic scene object resize tests
42 /// </summary> 42 /// </summary>
43 [TestFixture] 43 [TestFixture]
44 public class SceneObjectResizeTests 44 public class SceneObjectResizeTests : OpenSimTestCase
45 { 45 {
46 /// <summary> 46 /// <summary>
47 /// Test resizing an object 47 /// Test resizing an object
diff --git a/OpenSim/Region/Framework/Scenes/Tests/SceneObjectScriptTests.cs b/OpenSim/Region/Framework/Scenes/Tests/SceneObjectScriptTests.cs
index d2361f8..a58e735 100644
--- a/OpenSim/Region/Framework/Scenes/Tests/SceneObjectScriptTests.cs
+++ b/OpenSim/Region/Framework/Scenes/Tests/SceneObjectScriptTests.cs
@@ -40,7 +40,7 @@ using OpenSim.Tests.Common.Mock;
40namespace OpenSim.Region.Framework.Scenes.Tests 40namespace OpenSim.Region.Framework.Scenes.Tests
41{ 41{
42 [TestFixture] 42 [TestFixture]
43 public class SceneObjectScriptTests 43 public class SceneObjectScriptTests : OpenSimTestCase
44 { 44 {
45 [Test] 45 [Test]
46 public void TestAddScript() 46 public void TestAddScript()
diff --git a/OpenSim/Region/Framework/Scenes/Tests/SceneObjectSpatialTests.cs b/OpenSim/Region/Framework/Scenes/Tests/SceneObjectSpatialTests.cs
index 6d255aa..abaa1d1 100644
--- a/OpenSim/Region/Framework/Scenes/Tests/SceneObjectSpatialTests.cs
+++ b/OpenSim/Region/Framework/Scenes/Tests/SceneObjectSpatialTests.cs
@@ -42,14 +42,16 @@ namespace OpenSim.Region.Framework.Scenes.Tests
42 /// Spatial scene object tests (will eventually cover root and child part position, rotation properties, etc.) 42 /// Spatial scene object tests (will eventually cover root and child part position, rotation properties, etc.)
43 /// </summary> 43 /// </summary>
44 [TestFixture] 44 [TestFixture]
45 public class SceneObjectSpatialTests 45 public class SceneObjectSpatialTests : OpenSimTestCase
46 { 46 {
47 TestScene m_scene; 47 TestScene m_scene;
48 UUID m_ownerId = TestHelpers.ParseTail(0x1); 48 UUID m_ownerId = TestHelpers.ParseTail(0x1);
49 49
50 [SetUp] 50 [SetUp]
51 public void SetUp() 51 public override void SetUp()
52 { 52 {
53 base.SetUp();
54
53 m_scene = new SceneHelpers().SetupScene(); 55 m_scene = new SceneHelpers().SetupScene();
54 } 56 }
55 57
diff --git a/OpenSim/Region/Framework/Scenes/Tests/SceneObjectStatusTests.cs b/OpenSim/Region/Framework/Scenes/Tests/SceneObjectStatusTests.cs
index 742c769..8eb3191 100644
--- a/OpenSim/Region/Framework/Scenes/Tests/SceneObjectStatusTests.cs
+++ b/OpenSim/Region/Framework/Scenes/Tests/SceneObjectStatusTests.cs
@@ -42,7 +42,7 @@ namespace OpenSim.Region.Framework.Scenes.Tests
42 /// Basic scene object status tests 42 /// Basic scene object status tests
43 /// </summary> 43 /// </summary>
44 [TestFixture] 44 [TestFixture]
45 public class SceneObjectStatusTests 45 public class SceneObjectStatusTests : OpenSimTestCase
46 { 46 {
47 private TestScene m_scene; 47 private TestScene m_scene;
48 private UUID m_ownerId = TestHelpers.ParseTail(0x1); 48 private UUID m_ownerId = TestHelpers.ParseTail(0x1);
@@ -78,6 +78,26 @@ namespace OpenSim.Region.Framework.Scenes.Tests
78 } 78 }
79 79
80 [Test] 80 [Test]
81 public void TestSetNonPhysicsVolumeDetectSinglePrim()
82 {
83 TestHelpers.InMethod();
84
85 m_scene.AddSceneObject(m_so1);
86
87 SceneObjectPart rootPart = m_so1.RootPart;
88 Assert.That(rootPart.Flags, Is.EqualTo(PrimFlags.None));
89
90 m_so1.ScriptSetVolumeDetect(true);
91
92// Console.WriteLine("so.RootPart.Flags [{0}]", so.RootPart.Flags);
93 Assert.That(rootPart.Flags, Is.EqualTo(PrimFlags.Phantom));
94
95 m_so1.ScriptSetVolumeDetect(false);
96
97 Assert.That(rootPart.Flags, Is.EqualTo(PrimFlags.None));
98 }
99
100 [Test]
81 public void TestSetPhysicsSinglePrim() 101 public void TestSetPhysicsSinglePrim()
82 { 102 {
83 TestHelpers.InMethod(); 103 TestHelpers.InMethod();
@@ -89,13 +109,32 @@ namespace OpenSim.Region.Framework.Scenes.Tests
89 109
90 m_so1.ScriptSetPhysicsStatus(true); 110 m_so1.ScriptSetPhysicsStatus(true);
91 111
92// Console.WriteLine("so.RootPart.Flags [{0}]", so.RootPart.Flags);
93 Assert.That(rootPart.Flags, Is.EqualTo(PrimFlags.Physics)); 112 Assert.That(rootPart.Flags, Is.EqualTo(PrimFlags.Physics));
94 113
95 m_so1.ScriptSetPhysicsStatus(false); 114 m_so1.ScriptSetPhysicsStatus(false);
96 115
97 Assert.That(rootPart.Flags, Is.EqualTo(PrimFlags.None)); 116 Assert.That(rootPart.Flags, Is.EqualTo(PrimFlags.None));
98 } 117 }
118
119 [Test]
120 public void TestSetPhysicsVolumeDetectSinglePrim()
121 {
122 TestHelpers.InMethod();
123
124 m_scene.AddSceneObject(m_so1);
125
126 SceneObjectPart rootPart = m_so1.RootPart;
127 Assert.That(rootPart.Flags, Is.EqualTo(PrimFlags.None));
128
129 m_so1.ScriptSetPhysicsStatus(true);
130 m_so1.ScriptSetVolumeDetect(true);
131
132 Assert.That(rootPart.Flags, Is.EqualTo(PrimFlags.Phantom | PrimFlags.Physics));
133
134 m_so1.ScriptSetVolumeDetect(false);
135
136 Assert.That(rootPart.Flags, Is.EqualTo(PrimFlags.Physics));
137 }
99 138
100 [Test] 139 [Test]
101 public void TestSetPhysicsLinkset() 140 public void TestSetPhysicsLinkset()
diff --git a/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceAnimationTests.cs b/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceAnimationTests.cs
index 646e5fa..1cd8ae9 100644
--- a/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceAnimationTests.cs
+++ b/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceAnimationTests.cs
@@ -51,7 +51,7 @@ namespace OpenSim.Region.Framework.Scenes.Tests
51 /// Scene presence animation tests 51 /// Scene presence animation tests
52 /// </summary> 52 /// </summary>
53 [TestFixture] 53 [TestFixture]
54 public class ScenePresenceAnimationTests 54 public class ScenePresenceAnimationTests : OpenSimTestCase
55 { 55 {
56 [Test] 56 [Test]
57 public void TestFlyingAnimation() 57 public void TestFlyingAnimation()
diff --git a/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceAutopilotTests.cs b/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceAutopilotTests.cs
index 1d1ff88..d80afd3 100644
--- a/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceAutopilotTests.cs
+++ b/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceAutopilotTests.cs
@@ -42,7 +42,7 @@ using OpenSim.Tests.Common.Mock;
42namespace OpenSim.Region.Framework.Scenes.Tests 42namespace OpenSim.Region.Framework.Scenes.Tests
43{ 43{
44 [TestFixture] 44 [TestFixture]
45 public class ScenePresenceAutopilotTests 45 public class ScenePresenceAutopilotTests : OpenSimTestCase
46 { 46 {
47 private TestScene m_scene; 47 private TestScene m_scene;
48 48
diff --git a/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceSitTests.cs b/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceSitTests.cs
index 493ab70..acaeb90 100644
--- a/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceSitTests.cs
+++ b/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceSitTests.cs
@@ -43,7 +43,7 @@ using System.Threading;
43namespace OpenSim.Region.Framework.Scenes.Tests 43namespace OpenSim.Region.Framework.Scenes.Tests
44{ 44{
45 [TestFixture] 45 [TestFixture]
46 public class ScenePresenceSitTests 46 public class ScenePresenceSitTests : OpenSimTestCase
47 { 47 {
48 private TestScene m_scene; 48 private TestScene m_scene;
49 private ScenePresence m_sp; 49 private ScenePresence m_sp;
diff --git a/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceTeleportTests.cs b/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceTeleportTests.cs
index 37b5184..8dd1f3d 100644
--- a/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceTeleportTests.cs
+++ b/OpenSim/Region/Framework/Scenes/Tests/ScenePresenceTeleportTests.cs
@@ -49,7 +49,7 @@ namespace OpenSim.Region.Framework.Scenes.Tests
49 /// Teleport tests in a standalone OpenSim 49 /// Teleport tests in a standalone OpenSim
50 /// </summary> 50 /// </summary>
51 [TestFixture] 51 [TestFixture]
52 public class ScenePresenceTeleportTests 52 public class ScenePresenceTeleportTests : OpenSimTestCase
53 { 53 {
54 [TestFixtureSetUp] 54 [TestFixtureSetUp]
55 public void FixtureInit() 55 public void FixtureInit()
diff --git a/OpenSim/Region/Framework/Scenes/Tests/SceneTests.cs b/OpenSim/Region/Framework/Scenes/Tests/SceneTests.cs
index ac3da1e..9d8eb0b 100644
--- a/OpenSim/Region/Framework/Scenes/Tests/SceneTests.cs
+++ b/OpenSim/Region/Framework/Scenes/Tests/SceneTests.cs
@@ -50,7 +50,7 @@ namespace OpenSim.Region.Framework.Scenes.Tests
50 /// Scene presence tests 50 /// Scene presence tests
51 /// </summary> 51 /// </summary>
52 [TestFixture] 52 [TestFixture]
53 public class SceneTests 53 public class SceneTests : OpenSimTestCase
54 { 54 {
55 /// <summary> 55 /// <summary>
56 /// Very basic scene update test. Should become more elaborate with time. 56 /// Very basic scene update test. Should become more elaborate with time.
diff --git a/OpenSim/Region/Framework/Scenes/Tests/TaskInventoryTests.cs b/OpenSim/Region/Framework/Scenes/Tests/TaskInventoryTests.cs
index a51e4e3..0b461f5 100644
--- a/OpenSim/Region/Framework/Scenes/Tests/TaskInventoryTests.cs
+++ b/OpenSim/Region/Framework/Scenes/Tests/TaskInventoryTests.cs
@@ -50,7 +50,7 @@ using OpenSim.Tests.Common.Mock;
50namespace OpenSim.Region.Framework.Tests 50namespace OpenSim.Region.Framework.Tests
51{ 51{
52 [TestFixture] 52 [TestFixture]
53 public class TaskInventoryTests 53 public class TaskInventoryTests : OpenSimTestCase
54 { 54 {
55 [Test] 55 [Test]
56 public void TestAddTaskInventoryItem() 56 public void TestAddTaskInventoryItem()
diff --git a/OpenSim/Region/Framework/Scenes/Tests/UuidGathererTests.cs b/OpenSim/Region/Framework/Scenes/Tests/UuidGathererTests.cs
index 198e487..dd27294 100644
--- a/OpenSim/Region/Framework/Scenes/Tests/UuidGathererTests.cs
+++ b/OpenSim/Region/Framework/Scenes/Tests/UuidGathererTests.cs
@@ -38,7 +38,7 @@ using OpenSim.Tests.Common.Mock;
38namespace OpenSim.Region.Framework.Scenes.Tests 38namespace OpenSim.Region.Framework.Scenes.Tests
39{ 39{
40 [TestFixture] 40 [TestFixture]
41 public class UuidGathererTests 41 public class UuidGathererTests : OpenSimTestCase
42 { 42 {
43 protected IAssetService m_assetService; 43 protected IAssetService m_assetService;
44 protected UuidGatherer m_uuidGatherer; 44 protected UuidGatherer m_uuidGatherer;
diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/IGroupsServicesConnector.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/IGroupsServicesConnector.cs
index 6d26075..6b5b40a 100644
--- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/IGroupsServicesConnector.cs
+++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/IGroupsServicesConnector.cs
@@ -36,7 +36,22 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
36 { 36 {
37 UUID CreateGroup(UUID RequestingAgentID, string name, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish, UUID founderID); 37 UUID CreateGroup(UUID RequestingAgentID, string name, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish, UUID founderID);
38 void UpdateGroup(UUID RequestingAgentID, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish); 38 void UpdateGroup(UUID RequestingAgentID, UUID groupID, string charter, bool showInList, UUID insigniaID, int membershipFee, bool openEnrollment, bool allowPublish, bool maturePublish);
39
40 /// <summary>
41 /// Get the group record.
42 /// </summary>
43 /// <returns></returns>
44 /// <param name='RequestingAgentID'>The UUID of the user making the request.</param>
45 /// <param name='GroupID'>
46 /// The ID of the record to retrieve.
47 /// GroupName may be specified instead, in which case this parameter will be UUID.Zero
48 /// </param>
49 /// <param name='GroupName'>
50 /// The name of the group to retrieve.
51 /// GroupID may be specified instead, in which case this parmeter will be null.
52 /// </param>
39 GroupRecord GetGroupRecord(UUID RequestingAgentID, UUID GroupID, string GroupName); 53 GroupRecord GetGroupRecord(UUID RequestingAgentID, UUID GroupID, string GroupName);
54
40 List<DirGroupsReplyData> FindGroups(UUID RequestingAgentID, string search); 55 List<DirGroupsReplyData> FindGroups(UUID RequestingAgentID, string search);
41 List<GroupMembersData> GetGroupMembers(UUID RequestingAgentID, UUID GroupID); 56 List<GroupMembersData> GetGroupMembers(UUID RequestingAgentID, UUID GroupID);
42 57
diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/Tests/GroupsModuleTests.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/Tests/GroupsModuleTests.cs
index ac638f1..c1bdacb 100644
--- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/Tests/GroupsModuleTests.cs
+++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/Tests/GroupsModuleTests.cs
@@ -42,7 +42,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups.Tests
42 /// Basic groups module tests 42 /// Basic groups module tests
43 /// </summary> 43 /// </summary>
44 [TestFixture] 44 [TestFixture]
45 public class GroupsModuleTests 45 public class GroupsModuleTests : OpenSimTestCase
46 { 46 {
47 [Test] 47 [Test]
48 public void TestBasic() 48 public void TestBasic()
diff --git a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsServicesConnectorModule.cs b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsServicesConnectorModule.cs
index d0c3ea5..1101851 100644
--- a/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsServicesConnectorModule.cs
+++ b/OpenSim/Region/OptionalModules/Avatar/XmlRpcGroups/XmlRpcGroupsServicesConnectorModule.cs
@@ -54,13 +54,62 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
54 54
55 private bool m_debugEnabled = false; 55 private bool m_debugEnabled = false;
56 56
57 public const GroupPowers m_DefaultEveryonePowers = GroupPowers.AllowSetHome | 57 public const GroupPowers DefaultEveryonePowers
58 GroupPowers.Accountable | 58 = GroupPowers.AllowSetHome
59 GroupPowers.JoinChat | 59 | GroupPowers.Accountable
60 GroupPowers.AllowVoiceChat | 60 | GroupPowers.JoinChat
61 GroupPowers.ReceiveNotices | 61 | GroupPowers.AllowVoiceChat
62 GroupPowers.StartProposal | 62 | GroupPowers.ReceiveNotices
63 GroupPowers.VoteOnProposal; 63 | GroupPowers.StartProposal
64 | GroupPowers.VoteOnProposal;
65
66 // Would this be cleaner as (GroupPowers)ulong.MaxValue?
67 public const GroupPowers DefaultOwnerPowers
68 = GroupPowers.Accountable
69 | GroupPowers.AllowEditLand
70 | GroupPowers.AllowFly
71 | GroupPowers.AllowLandmark
72 | GroupPowers.AllowRez
73 | GroupPowers.AllowSetHome
74 | GroupPowers.AllowVoiceChat
75 | GroupPowers.AssignMember
76 | GroupPowers.AssignMemberLimited
77 | GroupPowers.ChangeActions
78 | GroupPowers.ChangeIdentity
79 | GroupPowers.ChangeMedia
80 | GroupPowers.ChangeOptions
81 | GroupPowers.CreateRole
82 | GroupPowers.DeedObject
83 | GroupPowers.DeleteRole
84 | GroupPowers.Eject
85 | GroupPowers.FindPlaces
86 | GroupPowers.Invite
87 | GroupPowers.JoinChat
88 | GroupPowers.LandChangeIdentity
89 | GroupPowers.LandDeed
90 | GroupPowers.LandDivideJoin
91 | GroupPowers.LandEdit
92 | GroupPowers.LandEjectAndFreeze
93 | GroupPowers.LandGardening
94 | GroupPowers.LandManageAllowed
95 | GroupPowers.LandManageBanned
96 | GroupPowers.LandManagePasses
97 | GroupPowers.LandOptions
98 | GroupPowers.LandRelease
99 | GroupPowers.LandSetSale
100 | GroupPowers.ModerateChat
101 | GroupPowers.ObjectManipulate
102 | GroupPowers.ObjectSetForSale
103 | GroupPowers.ReceiveNotices
104 | GroupPowers.RemoveMember
105 | GroupPowers.ReturnGroupOwned
106 | GroupPowers.ReturnGroupSet
107 | GroupPowers.ReturnNonGroup
108 | GroupPowers.RoleProperties
109 | GroupPowers.SendNotices
110 | GroupPowers.SetLandingPoint
111 | GroupPowers.StartProposal
112 | GroupPowers.VoteOnProposal;
64 113
65 private bool m_connectorEnabled = false; 114 private bool m_connectorEnabled = false;
66 115
@@ -219,59 +268,9 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
219 param["AllowPublish"] = allowPublish == true ? 1 : 0; 268 param["AllowPublish"] = allowPublish == true ? 1 : 0;
220 param["MaturePublish"] = maturePublish == true ? 1 : 0; 269 param["MaturePublish"] = maturePublish == true ? 1 : 0;
221 param["FounderID"] = founderID.ToString(); 270 param["FounderID"] = founderID.ToString();
222 param["EveryonePowers"] = ((ulong)m_DefaultEveryonePowers).ToString(); 271 param["EveryonePowers"] = ((ulong)DefaultEveryonePowers).ToString();
223 param["OwnerRoleID"] = OwnerRoleID.ToString(); 272 param["OwnerRoleID"] = OwnerRoleID.ToString();
224 273 param["OwnersPowers"] = ((ulong)DefaultOwnerPowers).ToString();
225 // Would this be cleaner as (GroupPowers)ulong.MaxValue;
226 GroupPowers OwnerPowers = GroupPowers.Accountable
227 | GroupPowers.AllowEditLand
228 | GroupPowers.AllowFly
229 | GroupPowers.AllowLandmark
230 | GroupPowers.AllowRez
231 | GroupPowers.AllowSetHome
232 | GroupPowers.AllowVoiceChat
233 | GroupPowers.AssignMember
234 | GroupPowers.AssignMemberLimited
235 | GroupPowers.ChangeActions
236 | GroupPowers.ChangeIdentity
237 | GroupPowers.ChangeMedia
238 | GroupPowers.ChangeOptions
239 | GroupPowers.CreateRole
240 | GroupPowers.DeedObject
241 | GroupPowers.DeleteRole
242 | GroupPowers.Eject
243 | GroupPowers.FindPlaces
244 | GroupPowers.Invite
245 | GroupPowers.JoinChat
246 | GroupPowers.LandChangeIdentity
247 | GroupPowers.LandDeed
248 | GroupPowers.LandDivideJoin
249 | GroupPowers.LandEdit
250 | GroupPowers.LandEjectAndFreeze
251 | GroupPowers.LandGardening
252 | GroupPowers.LandManageAllowed
253 | GroupPowers.LandManageBanned
254 | GroupPowers.LandManagePasses
255 | GroupPowers.LandOptions
256 | GroupPowers.LandRelease
257 | GroupPowers.LandSetSale
258 | GroupPowers.ModerateChat
259 | GroupPowers.ObjectManipulate
260 | GroupPowers.ObjectSetForSale
261 | GroupPowers.ReceiveNotices
262 | GroupPowers.RemoveMember
263 | GroupPowers.ReturnGroupOwned
264 | GroupPowers.ReturnGroupSet
265 | GroupPowers.ReturnNonGroup
266 | GroupPowers.RoleProperties
267 | GroupPowers.SendNotices
268 | GroupPowers.SetLandingPoint
269 | GroupPowers.StartProposal
270 | GroupPowers.VoteOnProposal;
271 param["OwnersPowers"] = ((ulong)OwnerPowers).ToString();
272
273
274
275 274
276 Hashtable respData = XmlRpcCall(requestingAgentID, "groups.createGroup", param); 275 Hashtable respData = XmlRpcCall(requestingAgentID, "groups.createGroup", param);
277 276
@@ -612,8 +611,6 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
612 } 611 }
613 612
614 return Roles; 613 return Roles;
615
616
617 } 614 }
618 615
619 public List<GroupRolesData> GetGroupRoles(UUID requestingAgentID, UUID GroupID) 616 public List<GroupRolesData> GetGroupRoles(UUID requestingAgentID, UUID GroupID)
@@ -676,7 +673,6 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
676 } 673 }
677 674
678 return members; 675 return members;
679
680 } 676 }
681 677
682 public List<GroupRoleMembersData> GetGroupRoleMembers(UUID requestingAgentID, UUID GroupID) 678 public List<GroupRoleMembersData> GetGroupRoleMembers(UUID requestingAgentID, UUID GroupID)
@@ -727,9 +723,10 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
727 values.Add(data); 723 values.Add(data);
728 } 724 }
729 } 725 }
730 return values;
731 726
727 return values;
732 } 728 }
729
733 public GroupNoticeInfo GetGroupNotice(UUID requestingAgentID, UUID noticeID) 730 public GroupNoticeInfo GetGroupNotice(UUID requestingAgentID, UUID noticeID)
734 { 731 {
735 Hashtable param = new Hashtable(); 732 Hashtable param = new Hashtable();
@@ -737,7 +734,6 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
737 734
738 Hashtable respData = XmlRpcCall(requestingAgentID, "groups.getGroupNotice", param); 735 Hashtable respData = XmlRpcCall(requestingAgentID, "groups.getGroupNotice", param);
739 736
740
741 if (respData.Contains("error")) 737 if (respData.Contains("error"))
742 { 738 {
743 return null; 739 return null;
@@ -761,6 +757,7 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
761 757
762 return data; 758 return data;
763 } 759 }
760
764 public void AddGroupNotice(UUID requestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message, byte[] binaryBucket) 761 public void AddGroupNotice(UUID requestingAgentID, UUID groupID, UUID noticeID, string fromName, string subject, string message, byte[] binaryBucket)
765 { 762 {
766 string binBucket = OpenMetaverse.Utils.BytesToHexString(binaryBucket, ""); 763 string binBucket = OpenMetaverse.Utils.BytesToHexString(binaryBucket, "");
@@ -777,8 +774,6 @@ namespace OpenSim.Region.OptionalModules.Avatar.XmlRpcGroups
777 XmlRpcCall(requestingAgentID, "groups.addGroupNotice", param); 774 XmlRpcCall(requestingAgentID, "groups.addGroupNotice", param);
778 } 775 }
779 776
780
781
782 #endregion 777 #endregion
783 778
784 #region GroupSessionTracking 779 #region GroupSessionTracking
diff --git a/OpenSim/Region/OptionalModules/Scripting/XmlRpcRouterModule/XmlRpcGridRouterModule.cs b/OpenSim/Region/OptionalModules/Scripting/XmlRpcRouterModule/XmlRpcGridRouterModule.cs
index 6120a81..709d389 100644
--- a/OpenSim/Region/OptionalModules/Scripting/XmlRpcRouterModule/XmlRpcGridRouterModule.cs
+++ b/OpenSim/Region/OptionalModules/Scripting/XmlRpcRouterModule/XmlRpcGridRouterModule.cs
@@ -46,6 +46,7 @@ namespace OpenSim.Region.OptionalModules.Scripting.XmlRpcGridRouterModule
46{ 46{
47 public class XmlRpcInfo 47 public class XmlRpcInfo
48 { 48 {
49 public UUID item;
49 public UUID channel; 50 public UUID channel;
50 public string uri; 51 public string uri;
51 } 52 }
@@ -88,6 +89,14 @@ namespace OpenSim.Region.OptionalModules.Scripting.XmlRpcGridRouterModule
88 return; 89 return;
89 90
90 scene.RegisterModuleInterface<IXmlRpcRouter>(this); 91 scene.RegisterModuleInterface<IXmlRpcRouter>(this);
92
93 IScriptModule scriptEngine = scene.RequestModuleInterface<IScriptModule>();
94 if ( scriptEngine != null )
95 {
96 scriptEngine.OnScriptRemoved += this.ScriptRemoved;
97 scriptEngine.OnObjectRemoved += this.ObjectRemoved;
98
99 }
91 } 100 }
92 101
93 public void RegionLoaded(Scene scene) 102 public void RegionLoaded(Scene scene)
@@ -120,22 +129,36 @@ namespace OpenSim.Region.OptionalModules.Scripting.XmlRpcGridRouterModule
120 129
121 public void RegisterNewReceiver(IScriptModule scriptEngine, UUID channel, UUID objectID, UUID itemID, string uri) 130 public void RegisterNewReceiver(IScriptModule scriptEngine, UUID channel, UUID objectID, UUID itemID, string uri)
122 { 131 {
123 if (!m_Channels.ContainsKey(itemID)) 132 if (!m_Enabled)
124 { 133 return;
125 XmlRpcInfo info = new XmlRpcInfo();
126 info.channel = channel;
127 info.uri = uri;
128 134
129 bool success = SynchronousRestObjectRequester.MakeRequest<XmlRpcInfo, bool>( 135 m_log.InfoFormat("[XMLRPC GRID ROUTER]: New receiver Obj: {0} Ch: {1} ID: {2} URI: {3}",
130 "POST", m_ServerURI+"/RegisterChannel/", info); 136 objectID.ToString(), channel.ToString(), itemID.ToString(), uri);
131 137
132 if (!success) 138 XmlRpcInfo info = new XmlRpcInfo();
133 { 139 info.channel = channel;
134 m_log.Error("[XMLRPC GRID ROUTER] Error contacting server"); 140 info.uri = uri;
135 } 141 info.item = itemID;
142
143 bool success = SynchronousRestObjectRequester.MakeRequest<XmlRpcInfo, bool>(
144 "POST", m_ServerURI+"/RegisterChannel/", info);
136 145
137 m_Channels[itemID] = channel; 146 if (!success)
147 {
148 m_log.Error("[XMLRPC GRID ROUTER] Error contacting server");
138 } 149 }
150
151 m_Channels[itemID] = channel;
152
153 }
154
155 public void UnRegisterReceiver(string channelID, UUID itemID)
156 {
157 if (!m_Enabled)
158 return;
159
160 RemoveChannel(itemID);
161
139 } 162 }
140 163
141 public void ScriptRemoved(UUID itemID) 164 public void ScriptRemoved(UUID itemID)
@@ -143,10 +166,33 @@ namespace OpenSim.Region.OptionalModules.Scripting.XmlRpcGridRouterModule
143 if (!m_Enabled) 166 if (!m_Enabled)
144 return; 167 return;
145 168
146 if (m_Channels.ContainsKey(itemID)) 169 RemoveChannel(itemID);
170
171 }
172
173 public void ObjectRemoved(UUID objectID)
174 {
175 // m_log.InfoFormat("[XMLRPC GRID ROUTER]: Object Removed {0}",objectID.ToString());
176 }
177
178 private bool RemoveChannel(UUID itemID)
179 {
180 if(!m_Channels.ContainsKey(itemID))
181 {
182 m_log.InfoFormat("[XMLRPC GRID ROUTER]: Attempted to unregister non-existing Item: {0}", itemID.ToString());
183 return false;
184 }
185
186 XmlRpcInfo info = new XmlRpcInfo();
187
188 info.channel = m_Channels[itemID];
189 info.item = itemID;
190 info.uri = "http://0.0.0.0:00";
191
192 if (info != null)
147 { 193 {
148 bool success = SynchronousRestObjectRequester.MakeRequest<UUID, bool>( 194 bool success = SynchronousRestObjectRequester.MakeRequest<XmlRpcInfo, bool>(
149 "POST", m_ServerURI+"/RemoveChannel/", m_Channels[itemID]); 195 "POST", m_ServerURI+"/RemoveChannel/", info);
150 196
151 if (!success) 197 if (!success)
152 { 198 {
@@ -154,11 +200,9 @@ namespace OpenSim.Region.OptionalModules.Scripting.XmlRpcGridRouterModule
154 } 200 }
155 201
156 m_Channels.Remove(itemID); 202 m_Channels.Remove(itemID);
203 return true;
157 } 204 }
158 } 205 return false;
159
160 public void ObjectRemoved(UUID objectID)
161 {
162 } 206 }
163 } 207 }
164} 208}
diff --git a/OpenSim/Region/OptionalModules/Scripting/XmlRpcRouterModule/XmlRpcRouterModule.cs b/OpenSim/Region/OptionalModules/Scripting/XmlRpcRouterModule/XmlRpcRouterModule.cs
index 4bde52a..9d373ef 100644
--- a/OpenSim/Region/OptionalModules/Scripting/XmlRpcRouterModule/XmlRpcRouterModule.cs
+++ b/OpenSim/Region/OptionalModules/Scripting/XmlRpcRouterModule/XmlRpcRouterModule.cs
@@ -104,12 +104,18 @@ namespace OpenSim.Region.OptionalModules.Scripting.XmlRpcRouterModule
104 } 104 }
105 } 105 }
106 106
107 public void UnRegisterReceiver(string channelID, UUID itemID)
108 {
109 }
110
107 public void ScriptRemoved(UUID itemID) 111 public void ScriptRemoved(UUID itemID)
108 { 112 {
113 System.Console.WriteLine("TEST Script Removed!");
109 } 114 }
110 115
111 public void ObjectRemoved(UUID objectID) 116 public void ObjectRemoved(UUID objectID)
112 { 117 {
118 System.Console.WriteLine("TEST Obj Removed!");
113 } 119 }
114 } 120 }
115} 121}
diff --git a/OpenSim/Region/OptionalModules/World/NPC/Tests/NPCModuleTests.cs b/OpenSim/Region/OptionalModules/World/NPC/Tests/NPCModuleTests.cs
index 52ed846..a522277 100644
--- a/OpenSim/Region/OptionalModules/World/NPC/Tests/NPCModuleTests.cs
+++ b/OpenSim/Region/OptionalModules/World/NPC/Tests/NPCModuleTests.cs
@@ -48,7 +48,7 @@ using OpenSim.Tests.Common.Mock;
48namespace OpenSim.Region.OptionalModules.World.NPC.Tests 48namespace OpenSim.Region.OptionalModules.World.NPC.Tests
49{ 49{
50 [TestFixture] 50 [TestFixture]
51 public class NPCModuleTests 51 public class NPCModuleTests : OpenSimTestCase
52 { 52 {
53 private TestScene m_scene; 53 private TestScene m_scene;
54 private AvatarFactoryModule m_afMod; 54 private AvatarFactoryModule m_afMod;
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs
index 4c195e1..4dd6264 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs
@@ -105,12 +105,12 @@ public sealed class BSCharacter : BSPhysObject
105 DetailLog("{0},BSCharacter.create,call,size={1},scale={2},density={3},volume={4},mass={5}", 105 DetailLog("{0},BSCharacter.create,call,size={1},scale={2},density={3},volume={4},mass={5}",
106 LocalID, _size, Scale, _avatarDensity, _avatarVolume, RawMass); 106 LocalID, _size, Scale, _avatarDensity, _avatarVolume, RawMass);
107 107
108 // do actual create at taint time 108 // do actual creation in taint time
109 PhysicsScene.TaintedObject("BSCharacter.create", delegate() 109 PhysicsScene.TaintedObject("BSCharacter.create", delegate()
110 { 110 {
111 DetailLog("{0},BSCharacter.create,taint", LocalID); 111 DetailLog("{0},BSCharacter.create,taint", LocalID);
112 // New body and shape into PhysBody and PhysShape 112 // New body and shape into PhysBody and PhysShape
113 PhysicsScene.Shapes.GetBodyAndShape(true, PhysicsScene.World, this, null, null); 113 PhysicsScene.Shapes.GetBodyAndShape(true, PhysicsScene.World, this);
114 114
115 SetPhysicalProperties(); 115 SetPhysicalProperties();
116 }); 116 });
@@ -124,7 +124,9 @@ public sealed class BSCharacter : BSPhysObject
124 PhysicsScene.TaintedObject("BSCharacter.destroy", delegate() 124 PhysicsScene.TaintedObject("BSCharacter.destroy", delegate()
125 { 125 {
126 PhysicsScene.Shapes.DereferenceBody(PhysBody, true, null); 126 PhysicsScene.Shapes.DereferenceBody(PhysBody, true, null);
127 PhysBody.Clear();
127 PhysicsScene.Shapes.DereferenceShape(PhysShape, true, null); 128 PhysicsScene.Shapes.DereferenceShape(PhysShape, true, null);
129 PhysShape.Clear();
128 }); 130 });
129 } 131 }
130 132
@@ -165,9 +167,8 @@ public sealed class BSCharacter : BSPhysObject
165 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, PhysBody.ptr); 167 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, PhysBody.ptr);
166 168
167 // Do this after the object has been added to the world 169 // Do this after the object has been added to the world
168 BulletSimAPI.SetCollisionFilterMask2(PhysBody.ptr, 170 PhysBody.collisionType = CollisionType.Avatar;
169 (uint)CollisionFilterGroups.AvatarFilter, 171 PhysBody.ApplyCollisionMask();
170 (uint)CollisionFilterGroups.AvatarMask);
171 } 172 }
172 173
173 public override void RequestPhysicsterseUpdate() 174 public override void RequestPhysicsterseUpdate()
@@ -187,6 +188,11 @@ public sealed class BSCharacter : BSPhysObject
187 set { 188 set {
188 // When an avatar's size is set, only the height is changed. 189 // When an avatar's size is set, only the height is changed.
189 _size = value; 190 _size = value;
191 // Old versions of ScenePresence passed only the height. If width and/or depth are zero,
192 // replace with the default values.
193 if (_size.X == 0f) _size.X = PhysicsScene.Params.avatarCapsuleDepth;
194 if (_size.Y == 0f) _size.Y = PhysicsScene.Params.avatarCapsuleWidth;
195
190 ComputeAvatarScale(_size); 196 ComputeAvatarScale(_size);
191 ComputeAvatarVolumeAndMass(); 197 ComputeAvatarVolumeAndMass();
192 DetailLog("{0},BSCharacter.setSize,call,size={1},scale={2},density={3},volume={4},mass={5}", 198 DetailLog("{0},BSCharacter.setSize,call,size={1},scale={2},density={3},volume={4},mass={5}",
@@ -194,15 +200,18 @@ public sealed class BSCharacter : BSPhysObject
194 200
195 PhysicsScene.TaintedObject("BSCharacter.setSize", delegate() 201 PhysicsScene.TaintedObject("BSCharacter.setSize", delegate()
196 { 202 {
197 BulletSimAPI.SetLocalScaling2(PhysShape.ptr, Scale); 203 if (PhysBody.HasPhysicalBody && PhysShape.HasPhysicalShape)
198 UpdatePhysicalMassProperties(RawMass); 204 {
205 BulletSimAPI.SetLocalScaling2(PhysShape.ptr, Scale);
206 UpdatePhysicalMassProperties(RawMass);
207 // Make sure this change appears as a property update event
208 BulletSimAPI.PushUpdate2(PhysBody.ptr);
209 }
199 }); 210 });
200 211
201 } 212 }
202 } 213 }
203 214
204 public override OMV.Vector3 Scale { get; set; }
205
206 public override PrimitiveBaseShape Shape 215 public override PrimitiveBaseShape Shape
207 { 216 {
208 set { BaseShape = value; } 217 set { BaseShape = value; }
@@ -236,7 +245,8 @@ public sealed class BSCharacter : BSPhysObject
236 // Zero some other properties directly into the physics engine 245 // Zero some other properties directly into the physics engine
237 PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate() 246 PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate()
238 { 247 {
239 BulletSimAPI.ClearAllForces2(PhysBody.ptr); 248 if (PhysBody.HasPhysicalBody)
249 BulletSimAPI.ClearAllForces2(PhysBody.ptr);
240 }); 250 });
241 } 251 }
242 public override void ZeroAngularMotion(bool inTaintTime) 252 public override void ZeroAngularMotion(bool inTaintTime)
@@ -245,10 +255,13 @@ public sealed class BSCharacter : BSPhysObject
245 255
246 PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate() 256 PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate()
247 { 257 {
248 BulletSimAPI.SetInterpolationAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero); 258 if (PhysBody.HasPhysicalBody)
249 BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero); 259 {
250 // The next also get rid of applied linear force but the linear velocity is untouched. 260 BulletSimAPI.SetInterpolationAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero);
251 BulletSimAPI.ClearForces2(PhysBody.ptr); 261 BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero);
262 // The next also get rid of applied linear force but the linear velocity is untouched.
263 BulletSimAPI.ClearForces2(PhysBody.ptr);
264 }
252 }); 265 });
253 } 266 }
254 267
@@ -273,7 +286,8 @@ public sealed class BSCharacter : BSPhysObject
273 PhysicsScene.TaintedObject("BSCharacter.setPosition", delegate() 286 PhysicsScene.TaintedObject("BSCharacter.setPosition", delegate()
274 { 287 {
275 DetailLog("{0},BSCharacter.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation); 288 DetailLog("{0},BSCharacter.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation);
276 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); 289 if (PhysBody.HasPhysicalBody)
290 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
277 }); 291 });
278 } 292 }
279 } 293 }
@@ -297,6 +311,15 @@ public sealed class BSCharacter : BSPhysObject
297 { 311 {
298 bool ret = false; 312 bool ret = false;
299 313
314 // TODO: check for out of bounds
315 if (!PhysicsScene.TerrainManager.IsWithinKnownTerrain(_position))
316 {
317 // The character is out of the known/simulated area.
318 // Upper levels of code will handle the transition to other areas so, for
319 // the time, we just ignore the position.
320 return ret;
321 }
322
300 // If below the ground, move the avatar up 323 // If below the ground, move the avatar up
301 float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position); 324 float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position);
302 if (Position.Z < terrainHeight) 325 if (Position.Z < terrainHeight)
@@ -307,7 +330,7 @@ public sealed class BSCharacter : BSPhysObject
307 } 330 }
308 if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0) 331 if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0)
309 { 332 {
310 float waterHeight = PhysicsScene.GetWaterLevelAtXYZ(_position); 333 float waterHeight = PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(_position);
311 if (Position.Z < waterHeight) 334 if (Position.Z < waterHeight)
312 { 335 {
313 _position.Z = waterHeight; 336 _position.Z = waterHeight;
@@ -315,7 +338,6 @@ public sealed class BSCharacter : BSPhysObject
315 } 338 }
316 } 339 }
317 340
318 // TODO: check for out of bounds
319 return ret; 341 return ret;
320 } 342 }
321 343
@@ -332,7 +354,8 @@ public sealed class BSCharacter : BSPhysObject
332 PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.PositionSanityCheck", delegate() 354 PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.PositionSanityCheck", delegate()
333 { 355 {
334 DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation); 356 DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation);
335 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); 357 if (PhysBody.HasPhysicalBody)
358 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
336 }); 359 });
337 ret = true; 360 ret = true;
338 } 361 }
@@ -359,7 +382,8 @@ public sealed class BSCharacter : BSPhysObject
359 PhysicsScene.TaintedObject("BSCharacter.SetForce", delegate() 382 PhysicsScene.TaintedObject("BSCharacter.SetForce", delegate()
360 { 383 {
361 DetailLog("{0},BSCharacter.setForce,taint,force={1}", LocalID, _force); 384 DetailLog("{0},BSCharacter.setForce,taint,force={1}", LocalID, _force);
362 BulletSimAPI.SetObjectForce2(PhysBody.ptr, _force); 385 if (PhysBody.HasPhysicalBody)
386 BulletSimAPI.SetObjectForce2(PhysBody.ptr, _force);
363 }); 387 });
364 } 388 }
365 } 389 }
@@ -398,7 +422,8 @@ public sealed class BSCharacter : BSPhysObject
398 if (_currentFriction != PhysicsScene.Params.avatarStandingFriction) 422 if (_currentFriction != PhysicsScene.Params.avatarStandingFriction)
399 { 423 {
400 _currentFriction = PhysicsScene.Params.avatarStandingFriction; 424 _currentFriction = PhysicsScene.Params.avatarStandingFriction;
401 BulletSimAPI.SetFriction2(PhysBody.ptr, _currentFriction); 425 if (PhysBody.HasPhysicalBody)
426 BulletSimAPI.SetFriction2(PhysBody.ptr, _currentFriction);
402 } 427 }
403 } 428 }
404 else 429 else
@@ -406,7 +431,8 @@ public sealed class BSCharacter : BSPhysObject
406 if (_currentFriction != PhysicsScene.Params.avatarFriction) 431 if (_currentFriction != PhysicsScene.Params.avatarFriction)
407 { 432 {
408 _currentFriction = PhysicsScene.Params.avatarFriction; 433 _currentFriction = PhysicsScene.Params.avatarFriction;
409 BulletSimAPI.SetFriction2(PhysBody.ptr, _currentFriction); 434 if (PhysBody.HasPhysicalBody)
435 BulletSimAPI.SetFriction2(PhysBody.ptr, _currentFriction);
410 } 436 }
411 } 437 }
412 _velocity = value; 438 _velocity = value;
@@ -443,8 +469,11 @@ public sealed class BSCharacter : BSPhysObject
443 // m_log.DebugFormat("{0}: set orientation to {1}", LogHeader, _orientation); 469 // m_log.DebugFormat("{0}: set orientation to {1}", LogHeader, _orientation);
444 PhysicsScene.TaintedObject("BSCharacter.setOrientation", delegate() 470 PhysicsScene.TaintedObject("BSCharacter.setOrientation", delegate()
445 { 471 {
446 // _position = BulletSimAPI.GetPosition2(BSBody.ptr); 472 if (PhysBody.HasPhysicalBody)
447 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); 473 {
474 // _position = BulletSimAPI.GetPosition2(BSBody.ptr);
475 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
476 }
448 }); 477 });
449 } 478 }
450 } 479 }
@@ -517,10 +546,13 @@ public sealed class BSCharacter : BSPhysObject
517 _floatOnWater = value; 546 _floatOnWater = value;
518 PhysicsScene.TaintedObject("BSCharacter.setFloatOnWater", delegate() 547 PhysicsScene.TaintedObject("BSCharacter.setFloatOnWater", delegate()
519 { 548 {
520 if (_floatOnWater) 549 if (PhysBody.HasPhysicalBody)
521 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER); 550 {
522 else 551 if (_floatOnWater)
523 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER); 552 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER);
553 else
554 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER);
555 }
524 }); 556 });
525 } 557 }
526 } 558 }
@@ -553,7 +585,8 @@ public sealed class BSCharacter : BSPhysObject
553 DetailLog("{0},BSCharacter.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy); 585 DetailLog("{0},BSCharacter.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
554 // Buoyancy is faked by changing the gravity applied to the object 586 // Buoyancy is faked by changing the gravity applied to the object
555 float grav = PhysicsScene.Params.gravity * (1f - _buoyancy); 587 float grav = PhysicsScene.Params.gravity * (1f - _buoyancy);
556 BulletSimAPI.SetGravity2(PhysBody.ptr, new OMV.Vector3(0f, 0f, grav)); 588 if (PhysBody.HasPhysicalBody)
589 BulletSimAPI.SetGravity2(PhysBody.ptr, new OMV.Vector3(0f, 0f, grav));
557 } 590 }
558 } 591 }
559 592
@@ -599,7 +632,8 @@ public sealed class BSCharacter : BSPhysObject
599 PhysicsScene.TaintedObject("BSCharacter.AddForce", delegate() 632 PhysicsScene.TaintedObject("BSCharacter.AddForce", delegate()
600 { 633 {
601 DetailLog("{0},BSCharacter.setAddForce,taint,addedForce={1}", LocalID, _force); 634 DetailLog("{0},BSCharacter.setAddForce,taint,addedForce={1}", LocalID, _force);
602 BulletSimAPI.SetObjectForce2(PhysBody.ptr, _force); 635 if (PhysBody.HasPhysicalBody)
636 BulletSimAPI.SetObjectForce2(PhysBody.ptr, _force);
603 }); 637 });
604 } 638 }
605 else 639 else
@@ -616,9 +650,6 @@ public sealed class BSCharacter : BSPhysObject
616 650
617 private void ComputeAvatarScale(OMV.Vector3 size) 651 private void ComputeAvatarScale(OMV.Vector3 size)
618 { 652 {
619 // The 'size' given by the simulator is the mid-point of the avatar
620 // and X and Y are unspecified.
621
622 OMV.Vector3 newScale = size; 653 OMV.Vector3 newScale = size;
623 // newScale.X = PhysicsScene.Params.avatarCapsuleWidth; 654 // newScale.X = PhysicsScene.Params.avatarCapsuleWidth;
624 // newScale.Y = PhysicsScene.Params.avatarCapsuleDepth; 655 // newScale.Y = PhysicsScene.Params.avatarCapsuleDepth;
@@ -677,7 +708,7 @@ public sealed class BSCharacter : BSPhysObject
677 } 708 }
678 709
679 // Tell the linkset about value changes 710 // Tell the linkset about value changes
680 Linkset.UpdateProperties(this); 711 Linkset.UpdateProperties(this, true);
681 712
682 // Avatars don't report their changes the usual way. Changes are checked for in the heartbeat loop. 713 // Avatars don't report their changes the usual way. Changes are checked for in the heartbeat loop.
683 // base.RequestPhysicsterseUpdate(); 714 // base.RequestPhysicsterseUpdate();
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs b/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs
index 65fac00..6b1e304 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSConstraint.cs
@@ -57,7 +57,7 @@ public abstract class BSConstraint : IDisposable
57 if (m_enabled) 57 if (m_enabled)
58 { 58 {
59 m_enabled = false; 59 m_enabled = false;
60 if (m_constraint.ptr != IntPtr.Zero) 60 if (m_constraint.HasPhysicalConstraint)
61 { 61 {
62 bool success = BulletSimAPI.DestroyConstraint2(m_world.ptr, m_constraint.ptr); 62 bool success = BulletSimAPI.DestroyConstraint2(m_world.ptr, m_constraint.ptr);
63 m_world.physicsScene.DetailLog("{0},BSConstraint.Dispose,taint,id1={1},body1={2},id2={3},body2={4},success={5}", 63 m_world.physicsScene.DetailLog("{0},BSConstraint.Dispose,taint,id1={1},body1={2},id2={3},body2={4},success={5}",
@@ -65,7 +65,7 @@ public abstract class BSConstraint : IDisposable
65 m_body1.ID, m_body1.ptr.ToString("X"), 65 m_body1.ID, m_body1.ptr.ToString("X"),
66 m_body2.ID, m_body2.ptr.ToString("X"), 66 m_body2.ID, m_body2.ptr.ToString("X"),
67 success); 67 success);
68 m_constraint.ptr = System.IntPtr.Zero; 68 m_constraint.Clear();
69 } 69 }
70 } 70 }
71 } 71 }
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSConstraint6Dof.cs b/OpenSim/Region/Physics/BulletSPlugin/BSConstraint6Dof.cs
index 23ef052..b073555 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSConstraint6Dof.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSConstraint6Dof.cs
@@ -65,7 +65,7 @@ public sealed class BSConstraint6Dof : BSConstraint
65 m_world = world; 65 m_world = world;
66 m_body1 = obj1; 66 m_body1 = obj1;
67 m_body2 = obj2; 67 m_body2 = obj2;
68 if (obj1.ptr == IntPtr.Zero || obj2.ptr == IntPtr.Zero) 68 if (!obj1.HasPhysicalBody || !obj2.HasPhysicalBody)
69 { 69 {
70 world.physicsScene.DetailLog("{0},BS6DOFConstraint,badBodyPtr,wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}", 70 world.physicsScene.DetailLog("{0},BS6DOFConstraint,badBodyPtr,wID={1}, rID={2}, rBody={3}, cID={4}, cBody={5}",
71 BSScene.DetailLogZero, world.worldID, 71 BSScene.DetailLogZero, world.worldID,
@@ -83,7 +83,7 @@ public sealed class BSConstraint6Dof : BSConstraint
83 world.physicsScene.DetailLog("{0},BS6DofConstraint,createMidPoint,wID={1}, csrt={2}, rID={3}, rBody={4}, cID={5}, cBody={6}", 83 world.physicsScene.DetailLog("{0},BS6DofConstraint,createMidPoint,wID={1}, csrt={2}, rID={3}, rBody={4}, cID={5}, cBody={6}",
84 BSScene.DetailLogZero, world.worldID, m_constraint.ptr.ToString("X"), 84 BSScene.DetailLogZero, world.worldID, m_constraint.ptr.ToString("X"),
85 obj1.ID, obj1.ptr.ToString("X"), obj2.ID, obj2.ptr.ToString("X")); 85 obj1.ID, obj1.ptr.ToString("X"), obj2.ID, obj2.ptr.ToString("X"));
86 if (m_constraint.ptr == IntPtr.Zero) 86 if (!m_constraint.HasPhysicalConstraint)
87 { 87 {
88 world.physicsScene.Logger.ErrorFormat("{0} Failed creation of 6Dof constraint. rootID={1}, childID={2}", 88 world.physicsScene.Logger.ErrorFormat("{0} Failed creation of 6Dof constraint. rootID={1}, childID={2}",
89 LogHeader, obj1.ID, obj2.ID); 89 LogHeader, obj1.ID, obj2.ID);
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs
index dbc9039..5887249 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs
@@ -24,30 +24,17 @@
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 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. 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 * 26 *
27 27 * The quotations from http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial
28/* RA: June 14, 2011. Copied from ODEDynamics.cs and converted to 28 * are Copyright (c) 2009 Linden Research, Inc and are used under their license
29 * call the BulletSim system. 29 * of Creative Commons Attribution-Share Alike 3.0
30 */ 30 * (http://creativecommons.org/licenses/by-sa/3.0/).
31/* Revised Aug, Sept 2009 by Kitto Flora. ODEDynamics.cs replaces
32 * ODEVehicleSettings.cs. It and ODEPrim.cs are re-organised:
33 * ODEPrim.cs contains methods dealing with Prim editing, Prim
34 * characteristics and Kinetic motion.
35 * ODEDynamics.cs contains methods dealing with Prim Physical motion
36 * (dynamics) and the associated settings. Old Linear and angular
37 * motors for dynamic motion have been replace with MoveLinear()
38 * and MoveAngular(); 'Physical' is used only to switch ODE dynamic
39 * simualtion on/off; VEHICAL_TYPE_NONE/VEHICAL_TYPE_<other> is to
40 * switch between 'VEHICLE' parameter use and general dynamics
41 * settings use.
42 */ 31 */
43 32
44using System; 33using System;
45using System.Collections.Generic; 34using System.Collections.Generic;
46using System.Reflection; 35using System.Reflection;
47using System.Runtime.InteropServices; 36using System.Runtime.InteropServices;
48using log4net;
49using OpenMetaverse; 37using OpenMetaverse;
50using OpenSim.Framework;
51using OpenSim.Region.Physics.Manager; 38using OpenSim.Region.Physics.Manager;
52 39
53namespace OpenSim.Region.Physics.BulletSPlugin 40namespace OpenSim.Region.Physics.BulletSPlugin
@@ -80,10 +67,10 @@ namespace OpenSim.Region.Physics.BulletSPlugin
80 private Quaternion m_referenceFrame = Quaternion.Identity; 67 private Quaternion m_referenceFrame = Quaternion.Identity;
81 68
82 // Linear properties 69 // Linear properties
70 private BSVMotor m_linearMotor = new BSVMotor("LinearMotor");
83 private Vector3 m_linearMotorDirection = Vector3.Zero; // velocity requested by LSL, decayed by time 71 private Vector3 m_linearMotorDirection = Vector3.Zero; // velocity requested by LSL, decayed by time
84 private Vector3 m_linearMotorOffset = Vector3.Zero; // the point of force can be offset from the center 72 private Vector3 m_linearMotorOffset = Vector3.Zero; // the point of force can be offset from the center
85 private Vector3 m_linearMotorDirectionLASTSET = Vector3.Zero; // velocity requested by LSL 73 private Vector3 m_linearMotorDirectionLASTSET = Vector3.Zero; // velocity requested by LSL
86 private Vector3 m_newVelocity = Vector3.Zero; // velocity computed to be applied to body
87 private Vector3 m_linearFrictionTimescale = Vector3.Zero; 74 private Vector3 m_linearFrictionTimescale = Vector3.Zero;
88 private float m_linearMotorDecayTimescale = 0; 75 private float m_linearMotorDecayTimescale = 0;
89 private float m_linearMotorTimescale = 0; 76 private float m_linearMotorTimescale = 0;
@@ -93,13 +80,14 @@ namespace OpenSim.Region.Physics.BulletSPlugin
93 // private Vector3 m_linearMotorOffset = Vector3.Zero; 80 // private Vector3 m_linearMotorOffset = Vector3.Zero;
94 81
95 //Angular properties 82 //Angular properties
83 private BSVMotor m_angularMotor = new BSVMotor("AngularMotor");
96 private Vector3 m_angularMotorDirection = Vector3.Zero; // angular velocity requested by LSL motor 84 private Vector3 m_angularMotorDirection = Vector3.Zero; // angular velocity requested by LSL motor
97 // private int m_angularMotorApply = 0; // application frame counter 85 // private int m_angularMotorApply = 0; // application frame counter
98 private Vector3 m_angularMotorVelocity = Vector3.Zero; // current angular motor velocity 86 private Vector3 m_angularMotorVelocity = Vector3.Zero; // current angular motor velocity
99 private float m_angularMotorTimescale = 0; // motor angular velocity ramp up rate 87 private float m_angularMotorTimescale = 0; // motor angular velocity ramp up rate
100 private float m_angularMotorDecayTimescale = 0; // motor angular velocity decay rate 88 private float m_angularMotorDecayTimescale = 0; // motor angular velocity decay rate
101 private Vector3 m_angularFrictionTimescale = Vector3.Zero; // body angular velocity decay rate 89 private Vector3 m_angularFrictionTimescale = Vector3.Zero; // body angular velocity decay rate
102 private Vector3 m_lastAngularVelocity = Vector3.Zero; // what was last applied to body 90 private Vector3 m_lastAngularVelocity = Vector3.Zero;
103 private Vector3 m_lastVertAttractor = Vector3.Zero; // what VA was last applied to body 91 private Vector3 m_lastVertAttractor = Vector3.Zero; // what VA was last applied to body
104 92
105 //Deflection properties 93 //Deflection properties
@@ -124,8 +112,11 @@ namespace OpenSim.Region.Physics.BulletSPlugin
124 // Therefore only m_VehicleBuoyancy=1 (0g) will use the script-requested .Z velocity. 112 // Therefore only m_VehicleBuoyancy=1 (0g) will use the script-requested .Z velocity.
125 113
126 //Attractor properties 114 //Attractor properties
127 private float m_verticalAttractionEfficiency = 1.0f; // damped 115 private BSVMotor m_verticalAttractionMotor = new BSVMotor("VerticalAttraction");
128 private float m_verticalAttractionTimescale = 500f; // Timescale > 300 means no vert attractor. 116 private float m_verticalAttractionEfficiency = 1.0f; // damped
117 private float m_verticalAttractionCutoff = 500f; // per the documentation
118 // Timescale > cutoff means no vert attractor.
119 private float m_verticalAttractionTimescale = 510f;
129 120
130 public BSDynamics(BSScene myScene, BSPrim myPrim) 121 public BSDynamics(BSScene myScene, BSPrim myPrim)
131 { 122 {
@@ -137,7 +128,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
137 // Return 'true' if this vehicle is doing vehicle things 128 // Return 'true' if this vehicle is doing vehicle things
138 public bool IsActive 129 public bool IsActive
139 { 130 {
140 get { return Type != Vehicle.TYPE_NONE; } 131 get { return Type != Vehicle.TYPE_NONE && Prim.IsPhysical; }
141 } 132 }
142 133
143 internal void ProcessFloatVehicleParam(Vehicle pParam, float pValue) 134 internal void ProcessFloatVehicleParam(Vehicle pParam, float pValue)
@@ -152,13 +143,15 @@ namespace OpenSim.Region.Physics.BulletSPlugin
152 m_angularDeflectionTimescale = Math.Max(pValue, 0.01f); 143 m_angularDeflectionTimescale = Math.Max(pValue, 0.01f);
153 break; 144 break;
154 case Vehicle.ANGULAR_MOTOR_DECAY_TIMESCALE: 145 case Vehicle.ANGULAR_MOTOR_DECAY_TIMESCALE:
155 m_angularMotorDecayTimescale = Math.Max(pValue, 0.01f); 146 m_angularMotorDecayTimescale = ClampInRange(0.01f, pValue, 120);
147 m_angularMotor.TargetValueDecayTimeScale = m_angularMotorDecayTimescale;
156 break; 148 break;
157 case Vehicle.ANGULAR_MOTOR_TIMESCALE: 149 case Vehicle.ANGULAR_MOTOR_TIMESCALE:
158 m_angularMotorTimescale = Math.Max(pValue, 0.01f); 150 m_angularMotorTimescale = Math.Max(pValue, 0.01f);
151 m_angularMotor.TimeScale = m_angularMotorTimescale;
159 break; 152 break;
160 case Vehicle.BANKING_EFFICIENCY: 153 case Vehicle.BANKING_EFFICIENCY:
161 m_bankingEfficiency = Math.Max(-1f, Math.Min(pValue, 1f)); 154 m_bankingEfficiency = ClampInRange(-1f, pValue, 1f);
162 break; 155 break;
163 case Vehicle.BANKING_MIX: 156 case Vehicle.BANKING_MIX:
164 m_bankingMix = Math.Max(pValue, 0.01f); 157 m_bankingMix = Math.Max(pValue, 0.01f);
@@ -167,10 +160,10 @@ namespace OpenSim.Region.Physics.BulletSPlugin
167 m_bankingTimescale = Math.Max(pValue, 0.01f); 160 m_bankingTimescale = Math.Max(pValue, 0.01f);
168 break; 161 break;
169 case Vehicle.BUOYANCY: 162 case Vehicle.BUOYANCY:
170 m_VehicleBuoyancy = Math.Max(-1f, Math.Min(pValue, 1f)); 163 m_VehicleBuoyancy = ClampInRange(-1f, pValue, 1f);
171 break; 164 break;
172 case Vehicle.HOVER_EFFICIENCY: 165 case Vehicle.HOVER_EFFICIENCY:
173 m_VhoverEfficiency = Math.Max(0f, Math.Min(pValue, 1f)); 166 m_VhoverEfficiency = ClampInRange(0f, pValue, 1f);
174 break; 167 break;
175 case Vehicle.HOVER_HEIGHT: 168 case Vehicle.HOVER_HEIGHT:
176 m_VhoverHeight = pValue; 169 m_VhoverHeight = pValue;
@@ -185,33 +178,40 @@ namespace OpenSim.Region.Physics.BulletSPlugin
185 m_linearDeflectionTimescale = Math.Max(pValue, 0.01f); 178 m_linearDeflectionTimescale = Math.Max(pValue, 0.01f);
186 break; 179 break;
187 case Vehicle.LINEAR_MOTOR_DECAY_TIMESCALE: 180 case Vehicle.LINEAR_MOTOR_DECAY_TIMESCALE:
188 m_linearMotorDecayTimescale = Math.Max(pValue, 0.01f); 181 m_linearMotorDecayTimescale = ClampInRange(0.01f, pValue, 120);
182 m_linearMotor.TargetValueDecayTimeScale = m_linearMotorDecayTimescale;
189 break; 183 break;
190 case Vehicle.LINEAR_MOTOR_TIMESCALE: 184 case Vehicle.LINEAR_MOTOR_TIMESCALE:
191 m_linearMotorTimescale = Math.Max(pValue, 0.01f); 185 m_linearMotorTimescale = Math.Max(pValue, 0.01f);
186 m_linearMotor.TimeScale = m_linearMotorTimescale;
192 break; 187 break;
193 case Vehicle.VERTICAL_ATTRACTION_EFFICIENCY: 188 case Vehicle.VERTICAL_ATTRACTION_EFFICIENCY:
194 m_verticalAttractionEfficiency = Math.Max(0.1f, Math.Min(pValue, 1f)); 189 m_verticalAttractionEfficiency = ClampInRange(0.1f, pValue, 1f);
190 m_verticalAttractionMotor.Efficiency = m_verticalAttractionEfficiency;
195 break; 191 break;
196 case Vehicle.VERTICAL_ATTRACTION_TIMESCALE: 192 case Vehicle.VERTICAL_ATTRACTION_TIMESCALE:
197 m_verticalAttractionTimescale = Math.Max(pValue, 0.01f); 193 m_verticalAttractionTimescale = Math.Max(pValue, 0.01f);
194 m_verticalAttractionMotor.TimeScale = m_verticalAttractionTimescale;
198 break; 195 break;
199 196
200 // These are vector properties but the engine lets you use a single float value to 197 // These are vector properties but the engine lets you use a single float value to
201 // set all of the components to the same value 198 // set all of the components to the same value
202 case Vehicle.ANGULAR_FRICTION_TIMESCALE: 199 case Vehicle.ANGULAR_FRICTION_TIMESCALE:
203 m_angularFrictionTimescale = new Vector3(pValue, pValue, pValue); 200 m_angularFrictionTimescale = new Vector3(pValue, pValue, pValue);
201 m_angularMotor.FrictionTimescale = m_angularFrictionTimescale;
204 break; 202 break;
205 case Vehicle.ANGULAR_MOTOR_DIRECTION: 203 case Vehicle.ANGULAR_MOTOR_DIRECTION:
206 m_angularMotorDirection = new Vector3(pValue, pValue, pValue); 204 m_angularMotorDirection = new Vector3(pValue, pValue, pValue);
207 // m_angularMotorApply = 100; 205 m_angularMotor.SetTarget(m_angularMotorDirection);
208 break; 206 break;
209 case Vehicle.LINEAR_FRICTION_TIMESCALE: 207 case Vehicle.LINEAR_FRICTION_TIMESCALE:
210 m_linearFrictionTimescale = new Vector3(pValue, pValue, pValue); 208 m_linearFrictionTimescale = new Vector3(pValue, pValue, pValue);
209 m_linearMotor.FrictionTimescale = m_linearFrictionTimescale;
211 break; 210 break;
212 case Vehicle.LINEAR_MOTOR_DIRECTION: 211 case Vehicle.LINEAR_MOTOR_DIRECTION:
213 m_linearMotorDirection = new Vector3(pValue, pValue, pValue); 212 m_linearMotorDirection = new Vector3(pValue, pValue, pValue);
214 m_linearMotorDirectionLASTSET = new Vector3(pValue, pValue, pValue); 213 m_linearMotorDirectionLASTSET = new Vector3(pValue, pValue, pValue);
214 m_linearMotor.SetTarget(m_linearMotorDirection);
215 break; 215 break;
216 case Vehicle.LINEAR_MOTOR_OFFSET: 216 case Vehicle.LINEAR_MOTOR_OFFSET:
217 m_linearMotorOffset = new Vector3(pValue, pValue, pValue); 217 m_linearMotorOffset = new Vector3(pValue, pValue, pValue);
@@ -227,21 +227,24 @@ namespace OpenSim.Region.Physics.BulletSPlugin
227 { 227 {
228 case Vehicle.ANGULAR_FRICTION_TIMESCALE: 228 case Vehicle.ANGULAR_FRICTION_TIMESCALE:
229 m_angularFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z); 229 m_angularFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z);
230 m_angularMotor.FrictionTimescale = m_angularFrictionTimescale;
230 break; 231 break;
231 case Vehicle.ANGULAR_MOTOR_DIRECTION: 232 case Vehicle.ANGULAR_MOTOR_DIRECTION:
232 // Limit requested angular speed to 2 rps= 4 pi rads/sec 233 // Limit requested angular speed to 2 rps= 4 pi rads/sec
233 pValue.X = Math.Max(-12.56f, Math.Min(pValue.X, 12.56f)); 234 pValue.X = ClampInRange(-12.56f, pValue.X, 12.56f);
234 pValue.Y = Math.Max(-12.56f, Math.Min(pValue.Y, 12.56f)); 235 pValue.Y = ClampInRange(-12.56f, pValue.Y, 12.56f);
235 pValue.Z = Math.Max(-12.56f, Math.Min(pValue.Z, 12.56f)); 236 pValue.Z = ClampInRange(-12.56f, pValue.Z, 12.56f);
236 m_angularMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z); 237 m_angularMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z);
237 // m_angularMotorApply = 100; 238 m_angularMotor.SetTarget(m_angularMotorDirection);
238 break; 239 break;
239 case Vehicle.LINEAR_FRICTION_TIMESCALE: 240 case Vehicle.LINEAR_FRICTION_TIMESCALE:
240 m_linearFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z); 241 m_linearFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z);
242 m_linearMotor.FrictionTimescale = m_linearFrictionTimescale;
241 break; 243 break;
242 case Vehicle.LINEAR_MOTOR_DIRECTION: 244 case Vehicle.LINEAR_MOTOR_DIRECTION:
243 m_linearMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z); 245 m_linearMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z);
244 m_linearMotorDirectionLASTSET = new Vector3(pValue.X, pValue.Y, pValue.Z); 246 m_linearMotorDirectionLASTSET = new Vector3(pValue.X, pValue.Y, pValue.Z);
247 m_linearMotor.SetTarget(m_linearMotorDirection);
245 break; 248 break;
246 case Vehicle.LINEAR_MOTOR_OFFSET: 249 case Vehicle.LINEAR_MOTOR_OFFSET:
247 m_linearMotorOffset = new Vector3(pValue.X, pValue.Y, pValue.Z); 250 m_linearMotorOffset = new Vector3(pValue.X, pValue.Y, pValue.Z);
@@ -303,7 +306,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
303 m_VhoverEfficiency = 0; 306 m_VhoverEfficiency = 0;
304 m_VhoverTimescale = 0; 307 m_VhoverTimescale = 0;
305 m_VehicleBuoyancy = 0; 308 m_VehicleBuoyancy = 0;
306 309
307 m_linearDeflectionEfficiency = 1; 310 m_linearDeflectionEfficiency = 1;
308 m_linearDeflectionTimescale = 1; 311 m_linearDeflectionTimescale = 1;
309 312
@@ -319,6 +322,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
319 322
320 m_referenceFrame = Quaternion.Identity; 323 m_referenceFrame = Quaternion.Identity;
321 m_flags = (VehicleFlag)0; 324 m_flags = (VehicleFlag)0;
325
322 break; 326 break;
323 327
324 case Vehicle.TYPE_SLED: 328 case Vehicle.TYPE_SLED:
@@ -351,10 +355,14 @@ namespace OpenSim.Region.Physics.BulletSPlugin
351 m_bankingMix = 1; 355 m_bankingMix = 1;
352 356
353 m_referenceFrame = Quaternion.Identity; 357 m_referenceFrame = Quaternion.Identity;
354 m_flags |= (VehicleFlag.NO_DEFLECTION_UP | VehicleFlag.LIMIT_ROLL_ONLY | VehicleFlag.LIMIT_MOTOR_UP); 358 m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY
355 m_flags &= 359 | VehicleFlag.HOVER_TERRAIN_ONLY
356 ~(VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | 360 | VehicleFlag.HOVER_GLOBAL_HEIGHT
357 VehicleFlag.HOVER_GLOBAL_HEIGHT | VehicleFlag.HOVER_UP_ONLY); 361 | VehicleFlag.HOVER_UP_ONLY);
362 m_flags |= (VehicleFlag.NO_DEFLECTION_UP
363 | VehicleFlag.LIMIT_ROLL_ONLY
364 | VehicleFlag.LIMIT_MOTOR_UP);
365
358 break; 366 break;
359 case Vehicle.TYPE_CAR: 367 case Vehicle.TYPE_CAR:
360 m_linearMotorDirection = Vector3.Zero; 368 m_linearMotorDirection = Vector3.Zero;
@@ -498,6 +506,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
498 m_bankingEfficiency = 0; 506 m_bankingEfficiency = 0;
499 m_bankingMix = 0.7f; 507 m_bankingMix = 0.7f;
500 m_bankingTimescale = 5; 508 m_bankingTimescale = 5;
509
501 m_referenceFrame = Quaternion.Identity; 510 m_referenceFrame = Quaternion.Identity;
502 511
503 m_referenceFrame = Quaternion.Identity; 512 m_referenceFrame = Quaternion.Identity;
@@ -510,6 +519,26 @@ namespace OpenSim.Region.Physics.BulletSPlugin
510 | VehicleFlag.HOVER_GLOBAL_HEIGHT); 519 | VehicleFlag.HOVER_GLOBAL_HEIGHT);
511 break; 520 break;
512 } 521 }
522
523 // Update any physical parameters based on this type.
524 Refresh();
525
526 m_linearMotor = new BSVMotor("LinearMotor", m_linearMotorTimescale,
527 m_linearMotorDecayTimescale, m_linearFrictionTimescale,
528 1f);
529 m_linearMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG DEBUG (enables detail logging)
530
531 m_angularMotor = new BSVMotor("AngularMotor", m_angularMotorTimescale,
532 m_angularMotorDecayTimescale, m_angularFrictionTimescale,
533 1f);
534 m_angularMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG DEBUG (enables detail logging)
535
536 m_verticalAttractionMotor = new BSVMotor("VerticalAttraction", m_verticalAttractionTimescale,
537 BSMotor.Infinite, BSMotor.InfiniteVector,
538 m_verticalAttractionEfficiency);
539 // Z goes away and we keep X and Y
540 m_verticalAttractionMotor.FrictionTimescale = new Vector3(BSMotor.Infinite, BSMotor.Infinite, 0.1f);
541 m_verticalAttractionMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG DEBUG (enables detail logging)
513 } 542 }
514 543
515 // Some of the properties of this prim may have changed. 544 // Some of the properties of this prim may have changed.
@@ -518,13 +547,35 @@ namespace OpenSim.Region.Physics.BulletSPlugin
518 { 547 {
519 if (IsActive) 548 if (IsActive)
520 { 549 {
521 // Friction effects are handled by this vehicle code 550 // Remember the mass so we don't have to fetch it every step
522 BulletSimAPI.SetFriction2(Prim.PhysBody.ptr, 0f); 551 m_vehicleMass = Prim.Linkset.LinksetMass;
523 BulletSimAPI.SetHitFraction2(Prim.PhysBody.ptr, 0f); 552
524 553 // Friction affects are handled by this vehicle code
525 // BulletSimAPI.SetAngularDamping2(Prim.PhysBody.ptr, 0.8f); 554 float friction = 0f;
526 555 BulletSimAPI.SetFriction2(Prim.PhysBody.ptr, friction);
527 VDetailLog("{0},BSDynamics.Refresh,zeroingFriction and adding damping", Prim.LocalID); 556
557 // Moderate angular movement introduced by Bullet.
558 // TODO: possibly set AngularFactor and LinearFactor for the type of vehicle.
559 // Maybe compute linear and angular factor and damping from params.
560 float angularDamping = PhysicsScene.Params.vehicleAngularDamping;
561 BulletSimAPI.SetAngularDamping2(Prim.PhysBody.ptr, angularDamping);
562
563 // Vehicles report collision events so we know when it's on the ground
564 BulletSimAPI.AddToCollisionFlags2(Prim.PhysBody.ptr, CollisionFlags.BS_VEHICLE_COLLISIONS);
565
566 // DEBUG DEBUG DEBUG: use uniform inertia to smooth movement added by Bullet
567 // Vector3 localInertia = new Vector3(1f, 1f, 1f);
568 // Vector3 localInertia = new Vector3(m_vehicleMass, m_vehicleMass, m_vehicleMass);
569 Vector3 localInertia = BulletSimAPI.CalculateLocalInertia2(Prim.PhysShape.ptr, m_vehicleMass);
570 BulletSimAPI.SetMassProps2(Prim.PhysBody.ptr, m_vehicleMass, localInertia);
571 BulletSimAPI.UpdateInertiaTensor2(Prim.PhysBody.ptr);
572
573 VDetailLog("{0},BSDynamics.Refresh,mass={1},frict={2},inert={3},aDamp={4}",
574 Prim.LocalID, m_vehicleMass, friction, localInertia, angularDamping);
575 }
576 else
577 {
578 BulletSimAPI.RemoveFromCollisionFlags2(Prim.PhysBody.ptr, CollisionFlags.BS_VEHICLE_COLLISIONS);
528 } 579 }
529 } 580 }
530 581
@@ -546,116 +597,294 @@ namespace OpenSim.Region.Physics.BulletSPlugin
546 Refresh(); 597 Refresh();
547 } 598 }
548 599
600 #region Known vehicle value functions
601 // Vehicle physical parameters that we buffer from constant getting and setting.
602 // The "m_known*" variables are initialized to 'null', fetched only if referenced
603 // and stored back into the physics engine only if updated.
604 // This does two things: 1) saves continuious calls into unmanaged code, and
605 // 2) signals when a physics property update must happen back to the simulator
606 // to update values modified for the vehicle.
607 private int m_knownChanged;
608 private float? m_knownTerrainHeight;
609 private float? m_knownWaterLevel;
610 private Vector3? m_knownPosition;
611 private Vector3? m_knownVelocity;
612 private Vector3 m_knownForce;
613 private Quaternion? m_knownOrientation;
614 private Vector3? m_knownRotationalVelocity;
615 private Vector3 m_knownRotationalForce;
616 private float? m_knownForwardSpeed;
617
618 private const int m_knownChangedPosition = 1 << 0;
619 private const int m_knownChangedVelocity = 1 << 1;
620 private const int m_knownChangedForce = 1 << 2;
621 private const int m_knownChangedOrientation = 1 << 3;
622 private const int m_knownChangedRotationalVelocity = 1 << 4;
623 private const int m_knownChangedRotationalForce = 1 << 5;
624
625 private void ForgetKnownVehicleProperties()
626 {
627 m_knownTerrainHeight = null;
628 m_knownWaterLevel = null;
629 m_knownPosition = null;
630 m_knownVelocity = null;
631 m_knownForce = Vector3.Zero;
632 m_knownOrientation = null;
633 m_knownRotationalVelocity = null;
634 m_knownRotationalForce = Vector3.Zero;
635 m_knownForwardSpeed = null;
636 m_knownChanged = 0;
637 }
638 private void PushKnownChanged()
639 {
640 if (m_knownChanged != 0)
641 {
642 if ((m_knownChanged & m_knownChangedPosition) != 0)
643 Prim.ForcePosition = VehiclePosition;
644 if ((m_knownChanged & m_knownChangedOrientation) != 0)
645 Prim.ForceOrientation = VehicleOrientation;
646 if ((m_knownChanged & m_knownChangedVelocity) != 0)
647 {
648 Prim.ForceVelocity = VehicleVelocity;
649 BulletSimAPI.SetInterpolationLinearVelocity2(Prim.PhysBody.ptr, VehicleVelocity);
650 }
651 if ((m_knownChanged & m_knownChangedForce) != 0)
652 Prim.AddForce((Vector3)m_knownForce, false, true);
653
654 if ((m_knownChanged & m_knownChangedRotationalVelocity) != 0)
655 {
656 Prim.ForceRotationalVelocity = VehicleRotationalVelocity;
657 // Fake out Bullet by making it think the velocity is the same as last time.
658 BulletSimAPI.SetInterpolationAngularVelocity2(Prim.PhysBody.ptr, VehicleRotationalVelocity);
659 }
660 if ((m_knownChanged & m_knownChangedRotationalForce) != 0)
661 Prim.AddAngularForce((Vector3)m_knownRotationalForce, false, true);
662
663 // If we set one of the values (ie, the physics engine didn't do it) we must force
664 // an UpdateProperties event to send the changes up to the simulator.
665 BulletSimAPI.PushUpdate2(Prim.PhysBody.ptr);
666 }
667 m_knownChanged = 0;
668 }
669
670 // Since the computation of terrain height can be a little involved, this routine
671 // is used ot fetch the height only once for each vehicle simulation step.
672 private float GetTerrainHeight(Vector3 pos)
673 {
674 if (m_knownTerrainHeight == null)
675 m_knownTerrainHeight = Prim.PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(pos);
676 return (float)m_knownTerrainHeight;
677 }
678
679 // Since the computation of water level can be a little involved, this routine
680 // is used ot fetch the level only once for each vehicle simulation step.
681 private float GetWaterLevel(Vector3 pos)
682 {
683 if (m_knownWaterLevel == null)
684 m_knownWaterLevel = Prim.PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(pos);
685 return (float)m_knownWaterLevel;
686 }
687
688 private Vector3 VehiclePosition
689 {
690 get
691 {
692 if (m_knownPosition == null)
693 m_knownPosition = Prim.ForcePosition;
694 return (Vector3)m_knownPosition;
695 }
696 set
697 {
698 m_knownPosition = value;
699 m_knownChanged |= m_knownChangedPosition;
700 }
701 }
702
703 private Quaternion VehicleOrientation
704 {
705 get
706 {
707 if (m_knownOrientation == null)
708 m_knownOrientation = Prim.ForceOrientation;
709 return (Quaternion)m_knownOrientation;
710 }
711 set
712 {
713 m_knownOrientation = value;
714 m_knownChanged |= m_knownChangedOrientation;
715 }
716 }
717
718 private Vector3 VehicleVelocity
719 {
720 get
721 {
722 if (m_knownVelocity == null)
723 m_knownVelocity = Prim.ForceVelocity;
724 return (Vector3)m_knownVelocity;
725 }
726 set
727 {
728 m_knownVelocity = value;
729 m_knownChanged |= m_knownChangedVelocity;
730 }
731 }
732
733 private void VehicleAddForce(Vector3 aForce)
734 {
735 m_knownForce += aForce;
736 m_knownChanged |= m_knownChangedForce;
737 }
738
739 private Vector3 VehicleRotationalVelocity
740 {
741 get
742 {
743 if (m_knownRotationalVelocity == null)
744 m_knownRotationalVelocity = Prim.ForceRotationalVelocity;
745 return (Vector3)m_knownRotationalVelocity;
746 }
747 set
748 {
749 m_knownRotationalVelocity = value;
750 m_knownChanged |= m_knownChangedRotationalVelocity;
751 }
752 }
753 private void VehicleAddAngularForce(Vector3 aForce)
754 {
755 m_knownRotationalForce += aForce;
756 m_knownChanged |= m_knownChangedRotationalForce;
757 }
758 private float VehicleForwardSpeed
759 {
760 get
761 {
762 if (m_knownForwardSpeed == null)
763 m_knownForwardSpeed = (VehicleVelocity * Quaternion.Inverse(VehicleOrientation)).X;
764 return (float)m_knownForwardSpeed;
765 }
766 }
767
768 #endregion // Known vehicle value functions
769
549 // One step of the vehicle properties for the next 'pTimestep' seconds. 770 // One step of the vehicle properties for the next 'pTimestep' seconds.
550 internal void Step(float pTimestep) 771 internal void Step(float pTimestep)
551 { 772 {
552 if (!IsActive) return; 773 if (!IsActive) return;
553 774
554 // DEBUG 775 ForgetKnownVehicleProperties();
555 // Because Bullet does apply forces to the vehicle, our last computed
556 // linear and angular velocities are not what is happening now.
557 // Vector3 externalAngularVelocity = Prim.ForceRotationalVelocity - m_lastAngularVelocity;
558 // m_lastAngularVelocity += (externalAngularVelocity * 0.5f) * pTimestep;
559 // m_lastAngularVelocity = Prim.ForceRotationalVelocity; // DEBUG: account for what Bullet did last time
560 // m_lastLinearVelocityVector = Prim.ForceVelocity * Quaternion.Inverse(Prim.ForceOrientation); // DEBUG:
561 // END DEBUG
562
563 m_vehicleMass = Prim.Linkset.LinksetMass;
564 776
565 MoveLinear(pTimestep); 777 MoveLinear(pTimestep);
566 // Commented out for debug
567 MoveAngular(pTimestep); 778 MoveAngular(pTimestep);
568 // Prim.ApplyTorqueImpulse(-Prim.RotationalVelocity * m_vehicleMass, false); // DEBUG DEBUG
569 // Prim.ForceRotationalVelocity = -Prim.RotationalVelocity; // DEBUG DEBUG
570 779
571 LimitRotation(pTimestep); 780 LimitRotation(pTimestep);
572 781
573 // remember the position so next step we can limit absolute movement effects 782 // remember the position so next step we can limit absolute movement effects
574 m_lastPositionVector = Prim.ForcePosition; 783 m_lastPositionVector = VehiclePosition;
575 784
576 VDetailLog("{0},BSDynamics.Step,frict={1},grav={2},inertia={3},mass={4}", // DEBUG DEBUG 785 // If we forced the changing of some vehicle parameters, update the values and
577 Prim.LocalID, 786 // for the physics engine to note the changes so an UpdateProperties event will happen.
578 BulletSimAPI.GetFriction2(Prim.PhysBody.ptr), 787 PushKnownChanged();
579 BulletSimAPI.GetGravity2(Prim.PhysBody.ptr), 788
580 Prim.Inertia,
581 m_vehicleMass
582 );
583 VDetailLog("{0},BSDynamics.Step,done,pos={1},force={2},velocity={3},angvel={4}", 789 VDetailLog("{0},BSDynamics.Step,done,pos={1},force={2},velocity={3},angvel={4}",
584 Prim.LocalID, Prim.ForcePosition, Prim.Force, Prim.ForceVelocity, Prim.RotationalVelocity); 790 Prim.LocalID, VehiclePosition, Prim.Force, VehicleVelocity, VehicleRotationalVelocity);
585 }// end Step 791 }
586 792
587 // Apply the effect of the linear motor. 793 // Apply the effect of the linear motor and other linear motions (like hover and float).
588 // Also does hover and float.
589 private void MoveLinear(float pTimestep) 794 private void MoveLinear(float pTimestep)
590 { 795 {
591 // m_linearMotorDirection is the target direction we are moving relative to the vehicle coordinates 796 Vector3 linearMotorContribution = m_linearMotor.Step(pTimestep);
592 // m_lastLinearVelocityVector is the current speed we are moving in that direction
593 if (m_linearMotorDirection.LengthSquared() > 0.001f)
594 {
595 Vector3 origDir = m_linearMotorDirection; // DEBUG
596 Vector3 origVel = m_lastLinearVelocityVector; // DEBUG
597 // DEBUG: the vehicle velocity rotated to be relative to vehicle coordinates for comparison
598 Vector3 vehicleVelocity = Prim.ForceVelocity * Quaternion.Inverse(Prim.ForceOrientation); // DEBUG
599 797
600 // Add (desiredVelocity - lastAppliedVelocity) / howLongItShouldTakeToComplete 798 // The movement computed in the linear motor is relative to the vehicle
601 Vector3 addAmount = (m_linearMotorDirection - m_lastLinearVelocityVector)/(m_linearMotorTimescale) * pTimestep; 799 // coordinates. Rotate the movement to world coordinates.
602 m_lastLinearVelocityVector += addAmount; 800 linearMotorContribution *= VehicleOrientation;
603 801
604 float decayFactor = (1.0f / m_linearMotorDecayTimescale) * pTimestep; 802 // ==================================================================
605 m_linearMotorDirection *= (1f - decayFactor); 803 // Buoyancy: force to overcome gravity.
804 // m_VehicleBuoyancy: -1=2g; 0=1g; 1=0g;
805 // So, if zero, don't change anything (let gravity happen). If one, negate the effect of gravity.
806 Vector3 buoyancyContribution = Prim.PhysicsScene.DefaultGravity * m_VehicleBuoyancy;
606 807
607 // Rotate new object velocity from vehicle relative to world coordinates 808 Vector3 terrainHeightContribution = ComputeLinearTerrainHeightCorrection(pTimestep);
608 m_newVelocity = m_lastLinearVelocityVector * Prim.ForceOrientation;
609 809
610 // Apply friction for next time 810 Vector3 hoverContribution = ComputeLinearHover(pTimestep);
611 Vector3 frictionFactor = (Vector3.One / m_linearFrictionTimescale) * pTimestep;
612 m_lastLinearVelocityVector *= (Vector3.One - frictionFactor);
613 811
614 VDetailLog("{0},MoveLinear,nonZero,origlmDir={1},origlvVel={2},vehVel={3},add={4},decay={5},frict={6},lmDir={7},lvVec={8},newVel={9}", 812 ComputeLinearBlockingEndPoint(pTimestep);
615 Prim.LocalID, origDir, origVel, vehicleVelocity, addAmount, decayFactor, frictionFactor,
616 m_linearMotorDirection, m_lastLinearVelocityVector, m_newVelocity);
617 }
618 else
619 {
620 // if what remains of direction is very small, zero it.
621 m_linearMotorDirection = Vector3.Zero;
622 m_lastLinearVelocityVector = Vector3.Zero;
623 m_newVelocity = Vector3.Zero;
624 813
625 VDetailLog("{0},MoveLinear,zeroed", Prim.LocalID); 814 Vector3 limitMotorUpContribution = ComputeLinearMotorUp(pTimestep);
626 }
627 815
628 // m_newVelocity is velocity computed from linear motor in world coordinates 816 // ==================================================================
817 Vector3 newVelocity = linearMotorContribution
818 + terrainHeightContribution
819 + hoverContribution
820 + limitMotorUpContribution;
629 821
630 // Gravity and Buoyancy 822 Vector3 newForce = buoyancyContribution;
631 // There is some gravity, make a gravity force vector that is applied after object velocity. 823
632 // m_VehicleBuoyancy: -1=2g; 0=1g; 1=0g; 824 // If not changing some axis, reduce out velocity
633 Vector3 grav = Prim.PhysicsScene.DefaultGravity * (1f - m_VehicleBuoyancy); 825 if ((m_flags & (VehicleFlag.NO_X)) != 0)
826 newVelocity.X = 0;
827 if ((m_flags & (VehicleFlag.NO_Y)) != 0)
828 newVelocity.Y = 0;
829 if ((m_flags & (VehicleFlag.NO_Z)) != 0)
830 newVelocity.Z = 0;
634 831
635 /* 832 // ==================================================================
636 * RA: Not sure why one would do this unless we are hoping external forces are doing gravity, ... 833 // Clamp high or low velocities
637 // Preserve the current Z velocity 834 float newVelocityLengthSq = newVelocity.LengthSquared();
638 Vector3 vel_now = m_prim.Velocity; 835 // if (newVelocityLengthSq > 1e6f)
639 m_dir.Z = vel_now.Z; // Preserve the accumulated falling velocity 836 if (newVelocityLengthSq > 1000f)
640 */ 837 {
838 newVelocity /= newVelocity.Length();
839 newVelocity *= 1000f;
840 }
841 // else if (newVelocityLengthSq < 1e-6f)
842 else if (newVelocityLengthSq < 0.001f)
843 newVelocity = Vector3.Zero;
844
845 // ==================================================================
846 // Stuff new linear velocity into the vehicle.
847 // Since the velocity is just being set, it is not scaled by pTimeStep. Bullet will do that for us.
848 VehicleVelocity = newVelocity;
849
850 // Other linear forces are applied as forces.
851 Vector3 totalDownForce = newForce * m_vehicleMass;
852 if (!totalDownForce.ApproxEquals(Vector3.Zero, 0.01f))
853 {
854 VehicleAddForce(totalDownForce);
855 }
856
857 VDetailLog("{0}, MoveLinear,done,newVel={1},totDown={2},IsColliding={3}",
858 Prim.LocalID, newVelocity, totalDownForce, Prim.IsColliding);
859 VDetailLog("{0}, MoveLinear,done,linContrib={1},terrContrib={2},hoverContrib={3},limitContrib={4},buoyContrib={5}",
860 Prim.LocalID,
861 linearMotorContribution, terrainHeightContribution, hoverContribution,
862 limitMotorUpContribution, buoyancyContribution
863 );
641 864
642 Vector3 pos = Prim.ForcePosition; 865 } // end MoveLinear()
643// Vector3 accel = new Vector3(-(m_dir.X - m_lastLinearVelocityVector.X / 0.1f), -(m_dir.Y - m_lastLinearVelocityVector.Y / 0.1f), m_dir.Z - m_lastLinearVelocityVector.Z / 0.1f);
644 866
867 public Vector3 ComputeLinearTerrainHeightCorrection(float pTimestep)
868 {
869 Vector3 ret = Vector3.Zero;
645 // If below the terrain, move us above the ground a little. 870 // If below the terrain, move us above the ground a little.
646 float terrainHeight = Prim.PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(pos); 871 // TODO: Consider taking the rotated size of the object or possibly casting a ray.
647 // Taking the rotated size doesn't work here because m_prim.Size is the size of the root prim and not the linkset. 872 if (VehiclePosition.Z < GetTerrainHeight(VehiclePosition))
648 // TODO: Add a m_prim.LinkSet.Size similar to m_prim.LinkSet.Mass.
649 // Vector3 rotatedSize = m_prim.Size * m_prim.ForceOrientation;
650 // if (rotatedSize.Z < terrainHeight)
651 if (pos.Z < terrainHeight)
652 { 873 {
653 pos.Z = terrainHeight + 2; 874 // TODO: correct position by applying force rather than forcing position.
654 Prim.ForcePosition = pos; 875 Vector3 newPosition = VehiclePosition;
655 VDetailLog("{0},MoveLinear,terrainHeight,terrainHeight={1},pos={2}", Prim.LocalID, terrainHeight, pos); 876 newPosition.Z = GetTerrainHeight(VehiclePosition) + 1f;
877 VehiclePosition = newPosition;
878 VDetailLog("{0}, MoveLinear,terrainHeight,terrainHeight={1},pos={2}",
879 Prim.LocalID, GetTerrainHeight(VehiclePosition), VehiclePosition);
656 } 880 }
881 return ret;
882 }
883
884 public Vector3 ComputeLinearHover(float pTimestep)
885 {
886 Vector3 ret = Vector3.Zero;
657 887
658 // Check if hovering
659 // m_VhoverEfficiency: 0=bouncy, 1=totally damped 888 // m_VhoverEfficiency: 0=bouncy, 1=totally damped
660 // m_VhoverTimescale: time to achieve height 889 // m_VhoverTimescale: time to achieve height
661 if ((m_flags & (VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.HOVER_GLOBAL_HEIGHT)) != 0) 890 if ((m_flags & (VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.HOVER_GLOBAL_HEIGHT)) != 0)
@@ -663,11 +892,11 @@ namespace OpenSim.Region.Physics.BulletSPlugin
663 // We should hover, get the target height 892 // We should hover, get the target height
664 if ((m_flags & VehicleFlag.HOVER_WATER_ONLY) != 0) 893 if ((m_flags & VehicleFlag.HOVER_WATER_ONLY) != 0)
665 { 894 {
666 m_VhoverTargetHeight = Prim.PhysicsScene.GetWaterLevelAtXYZ(pos) + m_VhoverHeight; 895 m_VhoverTargetHeight = GetWaterLevel(VehiclePosition) + m_VhoverHeight;
667 } 896 }
668 if ((m_flags & VehicleFlag.HOVER_TERRAIN_ONLY) != 0) 897 if ((m_flags & VehicleFlag.HOVER_TERRAIN_ONLY) != 0)
669 { 898 {
670 m_VhoverTargetHeight = terrainHeight + m_VhoverHeight; 899 m_VhoverTargetHeight = GetTerrainHeight(VehiclePosition) + m_VhoverHeight;
671 } 900 }
672 if ((m_flags & VehicleFlag.HOVER_GLOBAL_HEIGHT) != 0) 901 if ((m_flags & VehicleFlag.HOVER_GLOBAL_HEIGHT) != 0)
673 { 902 {
@@ -677,45 +906,47 @@ namespace OpenSim.Region.Physics.BulletSPlugin
677 if ((m_flags & VehicleFlag.HOVER_UP_ONLY) != 0) 906 if ((m_flags & VehicleFlag.HOVER_UP_ONLY) != 0)
678 { 907 {
679 // If body is already heigher, use its height as target height 908 // If body is already heigher, use its height as target height
680 if (pos.Z > m_VhoverTargetHeight) 909 if (VehiclePosition.Z > m_VhoverTargetHeight)
681 m_VhoverTargetHeight = pos.Z; 910 m_VhoverTargetHeight = VehiclePosition.Z;
682 } 911 }
912
683 if ((m_flags & VehicleFlag.LOCK_HOVER_HEIGHT) != 0) 913 if ((m_flags & VehicleFlag.LOCK_HOVER_HEIGHT) != 0)
684 { 914 {
685 if (Math.Abs(pos.Z - m_VhoverTargetHeight) > 0.2f) 915 if (Math.Abs(VehiclePosition.Z - m_VhoverTargetHeight) > 0.2f)
686 { 916 {
917 Vector3 pos = VehiclePosition;
687 pos.Z = m_VhoverTargetHeight; 918 pos.Z = m_VhoverTargetHeight;
688 Prim.ForcePosition = pos; 919 VehiclePosition = pos;
689 } 920 }
690 } 921 }
691 else 922 else
692 { 923 {
693 float verticalError = pos.Z - m_VhoverTargetHeight; 924 // Error is positive if below the target and negative if above.
694 // RA: where does the 50 come from? 925 float verticalError = m_VhoverTargetHeight - VehiclePosition.Z;
695 float verticalCorrectionVelocity = pTimestep * ((verticalError * 50.0f) / m_VhoverTimescale); 926 float verticalCorrectionVelocity = verticalError / m_VhoverTimescale;
696 // Replace Vertical speed with correction figure if significant 927
697 if (Math.Abs(verticalError) > 0.01f) 928 // TODO: implement m_VhoverEfficiency correctly
698 { 929 if (Math.Abs(verticalError) > m_VhoverEfficiency)
699 m_newVelocity.Z += verticalCorrectionVelocity;
700 //KF: m_VhoverEfficiency is not yet implemented
701 }
702 else if (verticalError < -0.01)
703 {
704 m_newVelocity.Z -= verticalCorrectionVelocity;
705 }
706 else
707 { 930 {
708 m_newVelocity.Z = 0f; 931 ret = new Vector3(0f, 0f, verticalCorrectionVelocity);
709 } 932 }
710 } 933 }
711 934
712 VDetailLog("{0},MoveLinear,hover,pos={1},dir={2},height={3},target={4}", Prim.LocalID, pos, m_newVelocity, m_VhoverHeight, m_VhoverTargetHeight); 935 VDetailLog("{0}, MoveLinear,hover,pos={1},ret={2},hoverTS={3},height={4},target={5}",
936 Prim.LocalID, VehiclePosition, ret, m_VhoverTimescale, m_VhoverHeight, m_VhoverTargetHeight);
713 } 937 }
714 938
939 return ret;
940 }
941
942 public bool ComputeLinearBlockingEndPoint(float pTimestep)
943 {
944 bool changed = false;
945
946 Vector3 pos = VehiclePosition;
715 Vector3 posChange = pos - m_lastPositionVector; 947 Vector3 posChange = pos - m_lastPositionVector;
716 if (m_BlockingEndPoint != Vector3.Zero) 948 if (m_BlockingEndPoint != Vector3.Zero)
717 { 949 {
718 bool changed = false;
719 if (pos.X >= (m_BlockingEndPoint.X - (float)1)) 950 if (pos.X >= (m_BlockingEndPoint.X - (float)1))
720 { 951 {
721 pos.X -= posChange.X + 1; 952 pos.X -= posChange.X + 1;
@@ -743,233 +974,110 @@ namespace OpenSim.Region.Physics.BulletSPlugin
743 } 974 }
744 if (changed) 975 if (changed)
745 { 976 {
746 Prim.ForcePosition = pos; 977 VehiclePosition = pos;
747 VDetailLog("{0},MoveLinear,blockingEndPoint,block={1},origPos={2},pos={3}", 978 VDetailLog("{0}, MoveLinear,blockingEndPoint,block={1},origPos={2},pos={3}",
748 Prim.LocalID, m_BlockingEndPoint, posChange, pos); 979 Prim.LocalID, m_BlockingEndPoint, posChange, pos);
749 } 980 }
750 } 981 }
982 return changed;
983 }
751 984
752 #region downForce 985 // From http://wiki.secondlife.com/wiki/LlSetVehicleFlags :
753 Vector3 downForce = Vector3.Zero; 986 // Prevent ground vehicles from motoring into the sky. This flag has a subtle effect when
987 // used with conjunction with banking: the strength of the banking will decay when the
988 // vehicle no longer experiences collisions. The decay timescale is the same as
989 // VEHICLE_BANKING_TIMESCALE. This is to help prevent ground vehicles from steering
990 // when they are in mid jump.
991 // TODO: this code is wrong. Also, what should it do for boats (height from water)?
992 // This is just using the ground and a general collision check. Should really be using
993 // a downward raycast to find what is below.
994 public Vector3 ComputeLinearMotorUp(float pTimestep)
995 {
996 Vector3 ret = Vector3.Zero;
997 float distanceAboveGround = 0f;
754 998
755 if ((m_flags & (VehicleFlag.LIMIT_MOTOR_UP)) != 0) 999 if ((m_flags & (VehicleFlag.LIMIT_MOTOR_UP)) != 0)
756 { 1000 {
757 // If the vehicle is motoring into the sky, get it going back down. 1001 float targetHeight = Type == Vehicle.TYPE_BOAT ? GetWaterLevel(VehiclePosition) : GetTerrainHeight(VehiclePosition);
758 // Is this an angular force or both linear and angular?? 1002 distanceAboveGround = VehiclePosition.Z - targetHeight;
759 float distanceAboveGround = pos.Z - terrainHeight; 1003 // Not colliding if the vehicle is off the ground
760 if (distanceAboveGround > 2f) 1004 if (!Prim.IsColliding)
761 { 1005 {
762 // downForce = new Vector3(0, 0, (-distanceAboveGround / m_bankingTimescale) * pTimestep); 1006 // downForce = new Vector3(0, 0, (-distanceAboveGround / m_bankingTimescale) * pTimestep);
763 // downForce = new Vector3(0, 0, -distanceAboveGround / m_bankingTimescale); 1007 // downForce = new Vector3(0, 0, -distanceAboveGround / m_bankingTimescale);
764 downForce = new Vector3(0, 0, -distanceAboveGround); 1008 ret = new Vector3(0, 0, -distanceAboveGround);
765 } 1009 }
766 // TODO: this calculation is all wrong. From the description at 1010 // TODO: this calculation is wrong. From the description at
767 // (http://wiki.secondlife.com/wiki/Category:LSL_Vehicle), the downForce 1011 // (http://wiki.secondlife.com/wiki/Category:LSL_Vehicle), the downForce
768 // has a decay factor. This says this force should 1012 // has a decay factor. This says this force should
769 // be computed with a motor. 1013 // be computed with a motor.
770 VDetailLog("{0},MoveLinear,limitMotorUp,distAbove={1},downForce={2}", 1014 // TODO: add interaction with banking.
771 Prim.LocalID, distanceAboveGround, downForce);
772 }
773 #endregion // downForce
774
775 // If not changing some axis, reduce out velocity
776 if ((m_flags & (VehicleFlag.NO_X)) != 0)
777 m_newVelocity.X = 0;
778 if ((m_flags & (VehicleFlag.NO_Y)) != 0)
779 m_newVelocity.Y = 0;
780 if ((m_flags & (VehicleFlag.NO_Z)) != 0)
781 m_newVelocity.Z = 0;
782
783 // Clamp REALLY high or low velocities
784 if (m_newVelocity.LengthSquared() > 1e6f)
785 {
786 m_newVelocity /= m_newVelocity.Length();
787 m_newVelocity *= 1000f;
788 } 1015 }
789 else if (m_newVelocity.LengthSquared() < 1e-6f) 1016 VDetailLog("{0}, MoveLinear,limitMotorUp,distAbove={1},colliding={2},ret={3}",
790 m_newVelocity = Vector3.Zero; 1017 Prim.LocalID, distanceAboveGround, Prim.IsColliding, ret);
791 1018 return ret;
792 // Stuff new linear velocity into the vehicle 1019 }
793 Prim.ForceVelocity = m_newVelocity;
794 // Prim.ApplyForceImpulse((m_newVelocity - Prim.Velocity) * m_vehicleMass, false); // DEBUG DEBUG
795
796 Vector3 totalDownForce = downForce + grav;
797 if (totalDownForce != Vector3.Zero)
798 {
799 Prim.AddForce(totalDownForce * m_vehicleMass, false);
800 // Prim.ApplyForceImpulse(totalDownForce * m_vehicleMass, false);
801 }
802
803 VDetailLog("{0},MoveLinear,done,lmDir={1},lmVel={2},newVel={3},primVel={4},totalDown={5}",
804 Prim.LocalID, m_linearMotorDirection, m_lastLinearVelocityVector, m_newVelocity, Prim.Velocity, totalDownForce);
805
806 } // end MoveLinear()
807 1020
808 // ======================================================================= 1021 // =======================================================================
1022 // =======================================================================
809 // Apply the effect of the angular motor. 1023 // Apply the effect of the angular motor.
1024 // The 'contribution' is how much angular correction velocity each function wants.
1025 // All the contributions are added together and the resulting velocity is
1026 // set directly on the vehicle.
810 private void MoveAngular(float pTimestep) 1027 private void MoveAngular(float pTimestep)
811 { 1028 {
812 // m_angularMotorDirection // angular velocity requested by LSL motor 1029 // The user wants how many radians per second angular change?
813 // m_angularMotorApply // application frame counter 1030 Vector3 angularMotorContribution = m_angularMotor.Step(pTimestep);
814 // m_angularMotorVelocity // current angular motor velocity (ramps up and down) 1031
815 // m_angularMotorTimescale // motor angular velocity ramp up rate 1032 // ==================================================================
816 // m_angularMotorDecayTimescale // motor angular velocity decay rate 1033 // From http://wiki.secondlife.com/wiki/LlSetVehicleFlags :
817 // m_angularFrictionTimescale // body angular velocity decay rate 1034 // This flag prevents linear deflection parallel to world z-axis. This is useful
818 // m_lastAngularVelocity // what was last applied to body 1035 // for preventing ground vehicles with large linear deflection, like bumper cars,
819 1036 // from climbing their linear deflection into the sky.
820 if (m_angularMotorDirection.LengthSquared() > 0.0001) 1037 // That is, NO_DEFLECTION_UP says angular motion should not add any pitch or roll movement
821 { 1038 if ((m_flags & (VehicleFlag.NO_DEFLECTION_UP)) != 0)
822 Vector3 origVel = m_angularMotorVelocity;
823 Vector3 origDir = m_angularMotorDirection;
824
825 // new velocity += error / ( time to get there / step interval)
826 // requested direction - current vehicle direction
827 m_angularMotorVelocity += (m_angularMotorDirection - m_angularMotorVelocity) / (m_angularMotorTimescale / pTimestep);
828 // decay requested direction
829 m_angularMotorDirection *= (1.0f - (pTimestep * 1.0f/m_angularMotorDecayTimescale));
830
831 VDetailLog("{0},MoveAngular,angularMotorApply,angTScale={1},timeStep={2},origvel={3},origDir={4},vel={5}",
832 Prim.LocalID, m_angularMotorTimescale, pTimestep, origVel, origDir, m_angularMotorVelocity);
833 }
834 else
835 { 1039 {
836 m_angularMotorVelocity = Vector3.Zero; 1040 angularMotorContribution.X = 0f;
1041 angularMotorContribution.Y = 0f;
1042 VDetailLog("{0}, MoveAngular,noDeflectionUp,angularMotorContrib={1}", Prim.LocalID, angularMotorContribution);
837 } 1043 }
838 1044
839 #region Vertical attactor 1045 Vector3 verticalAttractionContribution = ComputeAngularVerticalAttraction();
840
841 Vector3 vertattr = Vector3.Zero;
842 Vector3 deflection = Vector3.Zero;
843 Vector3 banking = Vector3.Zero;
844
845 // If vertical attaction timescale is reasonable and we applied an angular force last time...
846 if (m_verticalAttractionTimescale < 300 && m_lastAngularVelocity != Vector3.Zero)
847 {
848 float VAservo = pTimestep * 0.2f / m_verticalAttractionTimescale;
849 if (Prim.IsColliding)
850 VAservo = pTimestep * 0.05f / (m_verticalAttractionTimescale);
851
852 VAservo *= (m_verticalAttractionEfficiency * m_verticalAttractionEfficiency);
853
854 // Create a vector of the vehicle "up" in world coordinates
855 Vector3 verticalError = Vector3.UnitZ * Prim.ForceOrientation;
856 // verticalError.X and .Y are the World error amounts. They are 0 when there is no
857 // error (Vehicle Body is 'vertical'), and .Z will be 1. As the body leans to its
858 // side |.X| will increase to 1 and .Z fall to 0. As body inverts |.X| will fall
859 // and .Z will go // negative. Similar for tilt and |.Y|. .X and .Y must be
860 // modulated to prevent a stable inverted body.
861
862 // Error is 0 (no error) to +/- 2 (max error)
863 if (verticalError.Z < 0.0f)
864 {
865 verticalError.X = 2.0f - verticalError.X;
866 verticalError.Y = 2.0f - verticalError.Y;
867 }
868 // scale it by VAservo (timestep and timescale)
869 verticalError = verticalError * VAservo;
870
871 // As the body rotates around the X axis, then verticalError.Y increases; Rotated around Y
872 // then .X increases, so change Body angular velocity X based on Y, and Y based on X.
873 // Z is not changed.
874 vertattr.X = verticalError.Y;
875 vertattr.Y = - verticalError.X;
876 vertattr.Z = 0f;
877 1046
878 // scaling appears better usingsquare-law 1047 Vector3 deflectionContribution = ComputeAngularDeflection();
879 Vector3 angularVelocity = Prim.ForceRotationalVelocity;
880 float bounce = 1.0f - (m_verticalAttractionEfficiency * m_verticalAttractionEfficiency);
881 vertattr.X += bounce * angularVelocity.X;
882 vertattr.Y += bounce * angularVelocity.Y;
883 1048
884 VDetailLog("{0},MoveAngular,verticalAttraction,VAservo={1},effic={2},verticalError={3},bounce={4},vertattr={5}", 1049 Vector3 bankingContribution = ComputeAngularBanking();
885 Prim.LocalID, VAservo, m_verticalAttractionEfficiency, verticalError, bounce, vertattr);
886 1050
887 } 1051 // ==================================================================
888 #endregion // Vertical attactor 1052 m_lastVertAttractor = verticalAttractionContribution;
889 1053
890 #region Deflection 1054 m_lastAngularVelocity = angularMotorContribution
1055 + verticalAttractionContribution
1056 + deflectionContribution
1057 + bankingContribution;
891 1058
892 if (m_angularDeflectionEfficiency != 0) 1059 // ==================================================================
1060 // Apply the correction velocity.
1061 // TODO: Should this be applied as an angular force (torque)?
1062 if (!m_lastAngularVelocity.ApproxEquals(Vector3.Zero, 0.01f))
893 { 1063 {
894 // Compute a scaled vector that points in the preferred axis (X direction) 1064 VehicleRotationalVelocity = m_lastAngularVelocity;
895 Vector3 scaledDefaultDirection =
896 new Vector3((pTimestep * 10 * (m_angularDeflectionEfficiency / m_angularDeflectionTimescale)), 0, 0);
897 // Adding the current vehicle orientation and reference frame displaces the orientation to the frame.
898 // Rotate the scaled default axix relative to the actual vehicle direction giving where it should point.
899 Vector3 preferredAxisOfMotion = scaledDefaultDirection * Quaternion.Add(Prim.ForceOrientation, m_referenceFrame);
900
901 // Scale by efficiency and timescale
902 deflection = (preferredAxisOfMotion * (m_angularDeflectionEfficiency) / m_angularDeflectionTimescale) * pTimestep;
903
904 VDetailLog("{0},MoveAngular,Deflection,perfAxis={1},deflection={2}",
905 Prim.LocalID, preferredAxisOfMotion, deflection);
906 // This deflection computation is not correct.
907 deflection = Vector3.Zero;
908 }
909
910 #endregion
911
912 #region Banking
913 1065
914 if (m_bankingEfficiency != 0) 1066 VDetailLog("{0}, MoveAngular,done,nonZero,angMotorContrib={1},vertAttrContrib={2},bankContrib={3},deflectContrib={4},totalContrib={5}",
1067 Prim.LocalID,
1068 angularMotorContribution, verticalAttractionContribution,
1069 bankingContribution, deflectionContribution,
1070 m_lastAngularVelocity
1071 );
1072 }
1073 else
915 { 1074 {
916 Vector3 dir = Vector3.One * Prim.ForceOrientation; 1075 // The vehicle is not adding anything angular wise.
917 float mult = (m_bankingMix*m_bankingMix)*-1*(m_bankingMix < 0 ? -1 : 1); 1076 VehicleRotationalVelocity = Vector3.Zero;
918 //Changes which way it banks in and out of turns 1077 VDetailLog("{0}, MoveAngular,done,zero", Prim.LocalID);
919
920 //Use the square of the efficiency, as it looks much more how SL banking works
921 float effSquared = (m_bankingEfficiency*m_bankingEfficiency);
922 if (m_bankingEfficiency < 0)
923 effSquared *= -1; //Keep the negative!
924
925 float mix = Math.Abs(m_bankingMix);
926 if (m_angularMotorVelocity.X == 0)
927 {
928 /*if (!parent.Orientation.ApproxEquals(this.m_referenceFrame, 0.25f))
929 {
930 Vector3 axisAngle;
931 float angle;
932 parent.Orientation.GetAxisAngle(out axisAngle, out angle);
933 Vector3 rotatedVel = parent.Velocity * parent.Orientation;
934 if ((rotatedVel.X < 0 && axisAngle.Y > 0) || (rotatedVel.X > 0 && axisAngle.Y < 0))
935 m_angularMotorVelocity.X += (effSquared * (mult * mix)) * (1f) * 10;
936 else
937 m_angularMotorVelocity.X += (effSquared * (mult * mix)) * (-1f) * 10;
938 }*/
939 }
940 else
941 banking.Z += (effSquared*(mult*mix))*(m_angularMotorVelocity.X) * 4;
942 if (!Prim.IsColliding && Math.Abs(m_angularMotorVelocity.X) > mix)
943 //If they are colliding, we probably shouldn't shove the prim around... probably
944 {
945 float angVelZ = m_angularMotorVelocity.X*-1;
946 /*if(angVelZ > mix)
947 angVelZ = mix;
948 else if(angVelZ < -mix)
949 angVelZ = -mix;*/
950 //This controls how fast and how far the banking occurs
951 Vector3 bankingRot = new Vector3(angVelZ*(effSquared*mult), 0, 0);
952 if (bankingRot.X > 3)
953 bankingRot.X = 3;
954 else if (bankingRot.X < -3)
955 bankingRot.X = -3;
956 bankingRot *= Prim.ForceOrientation;
957 banking += bankingRot;
958 }
959 m_angularMotorVelocity.X *= m_bankingEfficiency == 1 ? 0.0f : 1 - m_bankingEfficiency;
960 VDetailLog("{0},MoveAngular,Banking,bEff={1},angMotVel={2},banking={3}",
961 Prim.LocalID, m_bankingEfficiency, m_angularMotorVelocity, banking);
962 } 1078 }
963 1079
964 #endregion 1080 // ==================================================================
965
966 m_lastVertAttractor = vertattr;
967
968 // Sum velocities
969 m_lastAngularVelocity = m_angularMotorVelocity + vertattr + banking + deflection;
970
971 #region Linear Motor Offset
972
973 //Offset section 1081 //Offset section
974 if (m_linearMotorOffset != Vector3.Zero) 1082 if (m_linearMotorOffset != Vector3.Zero)
975 { 1083 {
@@ -985,8 +1093,8 @@ namespace OpenSim.Region.Physics.BulletSPlugin
985 // 1093 //
986 // The torque created is the linear velocity crossed with the offset 1094 // The torque created is the linear velocity crossed with the offset
987 1095
988 // NOTE: this computation does should be in the linear section 1096 // TODO: this computation should be in the linear section
989 // because there we know the impulse being applied. 1097 // because that is where we know the impulse being applied.
990 Vector3 torqueFromOffset = Vector3.Zero; 1098 Vector3 torqueFromOffset = Vector3.Zero;
991 // torqueFromOffset = Vector3.Cross(m_linearMotorOffset, appliedImpulse); 1099 // torqueFromOffset = Vector3.Cross(m_linearMotorOffset, appliedImpulse);
992 if (float.IsNaN(torqueFromOffset.X)) 1100 if (float.IsNaN(torqueFromOffset.X))
@@ -995,47 +1103,175 @@ namespace OpenSim.Region.Physics.BulletSPlugin
995 torqueFromOffset.Y = 0; 1103 torqueFromOffset.Y = 0;
996 if (float.IsNaN(torqueFromOffset.Z)) 1104 if (float.IsNaN(torqueFromOffset.Z))
997 torqueFromOffset.Z = 0; 1105 torqueFromOffset.Z = 0;
998 torqueFromOffset *= m_vehicleMass; 1106
999 Prim.ApplyTorqueImpulse(torqueFromOffset, true); 1107 VehicleAddAngularForce(torqueFromOffset * m_vehicleMass);
1000 VDetailLog("{0},BSDynamic.MoveAngular,motorOffset,applyTorqueImpulse={1}", Prim.LocalID, torqueFromOffset); 1108 VDetailLog("{0}, BSDynamic.MoveAngular,motorOffset,applyTorqueImpulse={1}", Prim.LocalID, torqueFromOffset);
1001 } 1109 }
1002 1110
1003 #endregion 1111 }
1112 // From http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial:
1113 // Some vehicles, like boats, should always keep their up-side up. This can be done by
1114 // enabling the "vertical attractor" behavior that springs the vehicle's local z-axis to
1115 // the world z-axis (a.k.a. "up"). To take advantage of this feature you would set the
1116 // VEHICLE_VERTICAL_ATTRACTION_TIMESCALE to control the period of the spring frequency,
1117 // and then set the VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY to control the damping. An
1118 // efficiency of 0.0 will cause the spring to wobble around its equilibrium, while an
1119 // efficiency of 1.0 will cause the spring to reach its equilibrium with exponential decay.
1120 public Vector3 ComputeAngularVerticalAttraction()
1121 {
1122 Vector3 ret = Vector3.Zero;
1004 1123
1005 if ((m_flags & (VehicleFlag.NO_DEFLECTION_UP)) != 0) 1124 // If vertical attaction timescale is reasonable
1125 if (m_verticalAttractionTimescale < m_verticalAttractionCutoff)
1006 { 1126 {
1007 m_lastAngularVelocity.X = 0; 1127 // Take a vector pointing up and convert it from world to vehicle relative coords.
1008 m_lastAngularVelocity.Y = 0; 1128 Vector3 verticalError = Vector3.UnitZ * VehicleOrientation;
1009 VDetailLog("{0},MoveAngular,noDeflectionUp,lastAngular={1}", Prim.LocalID, m_lastAngularVelocity); 1129
1130 // If vertical attraction correction is needed, the vector that was pointing up (UnitZ)
1131 // is now:
1132 // leaning to one side: rotated around the X axis with the Y value going
1133 // from zero (nearly straight up) to one (completely to the side)) or
1134 // leaning front-to-back: rotated around the Y axis with the value of X being between
1135 // zero and one.
1136 // The value of Z is how far the rotation is off with 1 meaning none and 0 being 90 degrees.
1137
1138 // If verticalError.Z is negative, the vehicle is upside down. Add additional push.
1139 if (verticalError.Z < 0f)
1140 {
1141 verticalError.X = 2f - verticalError.X;
1142 verticalError.Y = 2f - verticalError.Y;
1143 }
1144
1145 // Y error means needed rotation around X axis and visa versa.
1146 ret.X = verticalError.Y;
1147 ret.Y = - verticalError.X;
1148 ret.Z = 0f;
1149
1150 // Scale the correction force by how far we're off from vertical.
1151 // Z error of one says little error. As Z gets smaller, the vehicle is leaning farther over.
1152 float clampedSqrZError = ClampInRange(0.01f, verticalError.Z * verticalError.Z, 1f);
1153 float vertForce = 1f / clampedSqrZError;
1154
1155 ret *= vertForce;
1156
1157 // Correction happens over a number of seconds.
1158 Vector3 unscaledContrib = ret;
1159 ret /= m_verticalAttractionTimescale;
1160
1161 VDetailLog("{0}, MoveAngular,verticalAttraction,,verticalError={1},unscaled={2},vertForce={3},eff={4},vertAttr={5}",
1162 Prim.LocalID, verticalError, unscaledContrib, vertForce, m_verticalAttractionEfficiency, ret);
1010 } 1163 }
1164 return ret;
1165 }
1166
1167 // Return the angular correction to correct the direction the vehicle is pointing to be
1168 // the direction is should want to be pointing.
1169 // The vehicle is moving in some direction and correct its orientation to it is pointing
1170 // in that direction.
1171 // TODO: implement reference frame.
1172 public Vector3 ComputeAngularDeflection()
1173 {
1174 Vector3 ret = Vector3.Zero;
1175 return ret; // DEBUG DEBUG DEBUG debug one force at a time
1011 1176
1012 if (m_lastAngularVelocity.ApproxEquals(Vector3.Zero, 0.01f)) 1177 if (m_angularDeflectionEfficiency != 0)
1013 { 1178 {
1014 m_lastAngularVelocity = Vector3.Zero; // Reduce small value to zero. 1179 // The direction the vehicle is moving
1015 Prim.ZeroAngularMotion(true); 1180 Vector3 movingDirection = VehicleVelocity;
1016 VDetailLog("{0},MoveAngular,zeroAngularMotion,lastAngular={1}", Prim.LocalID, m_lastAngularVelocity); 1181 movingDirection.Normalize();
1182
1183 // The direction the vehicle is pointing
1184 Vector3 pointingDirection = Vector3.UnitX * VehicleOrientation;
1185 pointingDirection.Normalize();
1186
1187 // The difference between what is and what should be
1188 Vector3 deflectionError = movingDirection - pointingDirection;
1189
1190 // Scale the correction by recovery timescale and efficiency
1191 ret = (-deflectionError * VehicleForwardSpeed) * m_angularDeflectionEfficiency;
1192 ret /= m_angularDeflectionTimescale;
1193
1194 VDetailLog("{0}, MoveAngular,Deflection,movingDir={1},pointingDir={2},deflectError={3},ret={4}",
1195 Prim.LocalID, movingDirection, pointingDirection, deflectionError, ret);
1017 } 1196 }
1018 else 1197 return ret;
1198 }
1199
1200 // Return an angular change to rotate the vehicle around the Z axis when the vehicle
1201 // is tipped around the X axis.
1202 // From http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial:
1203 // The vertical attractor feature must be enabled in order for the banking behavior to
1204 // function. The way banking works is this: a rotation around the vehicle's roll-axis will
1205 // produce a angular velocity around the yaw-axis, causing the vehicle to turn. The magnitude
1206 // of the yaw effect will be proportional to the
1207 // VEHICLE_BANKING_EFFICIENCY, the angle of the roll rotation, and sometimes the vehicle's
1208 // velocity along its preferred axis of motion.
1209 // The VEHICLE_BANKING_EFFICIENCY can vary between -1 and +1. When it is positive then any
1210 // positive rotation (by the right-hand rule) about the roll-axis will effect a
1211 // (negative) torque around the yaw-axis, making it turn to the right--that is the
1212 // vehicle will lean into the turn, which is how real airplanes and motorcycle's work.
1213 // Negating the banking coefficient will make it so that the vehicle leans to the
1214 // outside of the turn (not very "physical" but might allow interesting vehicles so why not?).
1215 // The VEHICLE_BANKING_MIX is a fake (i.e. non-physical) parameter that is useful for making
1216 // banking vehicles do what you want rather than what the laws of physics allow.
1217 // For example, consider a real motorcycle...it must be moving forward in order for
1218 // it to turn while banking, however video-game motorcycles are often configured
1219 // to turn in place when at a dead stop--because they are often easier to control
1220 // that way using the limited interface of the keyboard or game controller. The
1221 // VEHICLE_BANKING_MIX enables combinations of both realistic and non-realistic
1222 // banking by functioning as a slider between a banking that is correspondingly
1223 // totally static (0.0) and totally dynamic (1.0). By "static" we mean that the
1224 // banking effect depends only on the vehicle's rotation about its roll-axis compared
1225 // to "dynamic" where the banking is also proportional to its velocity along its
1226 // roll-axis. Finding the best value of the "mixture" will probably require trial and error.
1227 // The time it takes for the banking behavior to defeat a preexisting angular velocity about the
1228 // world z-axis is determined by the VEHICLE_BANKING_TIMESCALE. So if you want the vehicle to
1229 // bank quickly then give it a banking timescale of about a second or less, otherwise you can
1230 // make a sluggish vehicle by giving it a timescale of several seconds.
1231 public Vector3 ComputeAngularBanking()
1232 {
1233 Vector3 ret = Vector3.Zero;
1234
1235 if (m_bankingEfficiency != 0 && m_verticalAttractionTimescale < m_verticalAttractionCutoff)
1019 { 1236 {
1020 // Apply to the body. 1237 // This works by rotating a unit vector to the orientation of the vehicle. The
1021 // The above calculates the absolute angular velocity needed. Angular velocity is massless. 1238 // roll (tilt) will be Y component of a tilting Z vector (zero for no tilt
1022 // Since we are stuffing the angular velocity directly into the object, the computed 1239 // up to one for full over).
1023 // velocity needs to be scaled by the timestep. 1240 Vector3 rollComponents = Vector3.UnitZ * VehicleOrientation;
1024 Vector3 applyAngularForce = ((m_lastAngularVelocity * pTimestep) - Prim.ForceRotationalVelocity); 1241
1025 Prim.ForceRotationalVelocity = applyAngularForce; 1242 // Figure out the yaw value for this much roll.
1026 1243 float turnComponent = rollComponents.Y * rollComponents.Y * m_bankingEfficiency;
1027 // Decay the angular movement for next time 1244 // Keep the sign
1028 Vector3 decayamount = (Vector3.One / m_angularFrictionTimescale) * pTimestep; 1245 if (rollComponents.Y < 0f)
1029 m_lastAngularVelocity *= Vector3.One - decayamount; 1246 turnComponent = -turnComponent;
1030 1247
1031 VDetailLog("{0},MoveAngular,done,newRotVel={1},decay={2},lastAngular={3}", 1248 // TODO: there must be a better computation of the banking force.
1032 Prim.LocalID, applyAngularForce, decayamount, m_lastAngularVelocity); 1249 float bankingTurnForce = turnComponent;
1250
1251 // actual error = static turn error + dynamic turn error
1252 float mixedBankingError = bankingTurnForce * (1f - m_bankingMix) + bankingTurnForce * m_bankingMix * VehicleForwardSpeed;
1253 // TODO: the banking effect should not go to infinity but what to limit it to?
1254 mixedBankingError = ClampInRange(-20f, mixedBankingError, 20f);
1255
1256 // Build the force vector to change rotation from what it is to what it should be
1257 ret.Z = -mixedBankingError;
1258
1259 // Don't do it all at once.
1260 ret /= m_bankingTimescale;
1261
1262 VDetailLog("{0}, MoveAngular,Banking,rollComp={1},speed={2},turnComp={3},bankErr={4},mixedBankErr={5},ret={6}",
1263 Prim.LocalID, rollComponents, VehicleForwardSpeed, turnComponent, bankingTurnForce, mixedBankingError, ret);
1033 } 1264 }
1034 } //end MoveAngular 1265 return ret;
1266 }
1035 1267
1268 // This is from previous instantiations of XXXDynamics.cs.
1269 // Applies roll reference frame.
1270 // TODO: is this the right way to separate the code to do this operation?
1271 // Should this be in MoveAngular()?
1036 internal void LimitRotation(float timestep) 1272 internal void LimitRotation(float timestep)
1037 { 1273 {
1038 Quaternion rotq = Prim.ForceOrientation; 1274 Quaternion rotq = VehicleOrientation;
1039 Quaternion m_rot = rotq; 1275 Quaternion m_rot = rotq;
1040 if (m_RollreferenceFrame != Quaternion.Identity) 1276 if (m_RollreferenceFrame != Quaternion.Identity)
1041 { 1277 {
@@ -1063,12 +1299,17 @@ namespace OpenSim.Region.Physics.BulletSPlugin
1063 } 1299 }
1064 if (rotq != m_rot) 1300 if (rotq != m_rot)
1065 { 1301 {
1066 Prim.ForceOrientation = m_rot; 1302 VehicleOrientation = m_rot;
1067 VDetailLog("{0},LimitRotation,done,orig={1},new={2}", Prim.LocalID, rotq, m_rot); 1303 VDetailLog("{0}, LimitRotation,done,orig={1},new={2}", Prim.LocalID, rotq, m_rot);
1068 } 1304 }
1069 1305
1070 } 1306 }
1071 1307
1308 private float ClampInRange(float low, float val, float high)
1309 {
1310 return Math.Max(low, Math.Min(val, high));
1311 }
1312
1072 // Invoke the detailed logger and output something if it's enabled. 1313 // Invoke the detailed logger and output something if it's enabled.
1073 private void VDetailLog(string msg, params Object[] args) 1314 private void VDetailLog(string msg, params Object[] args)
1074 { 1315 {
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs
index 0df4310..2017fa5 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs
@@ -32,6 +32,15 @@ using OMV = OpenMetaverse;
32 32
33namespace OpenSim.Region.Physics.BulletSPlugin 33namespace OpenSim.Region.Physics.BulletSPlugin
34{ 34{
35
36// A BSPrim can get individual information about its linkedness attached
37// to it through an instance of a subclass of LinksetInfo.
38// Each type of linkset will define the information needed for its type.
39public abstract class BSLinksetInfo
40{
41 public virtual void Clear() { }
42}
43
35public abstract class BSLinkset 44public abstract class BSLinkset
36{ 45{
37 // private static string LogHeader = "[BULLETSIM LINKSET]"; 46 // private static string LogHeader = "[BULLETSIM LINKSET]";
@@ -87,13 +96,6 @@ public abstract class BSLinkset
87 return BSPhysicsShapeType.SHAPE_UNKNOWN; 96 return BSPhysicsShapeType.SHAPE_UNKNOWN;
88 } 97 }
89 98
90 // Linksets move around the children so the linkset might need to compute the child position
91 public virtual OMV.Vector3 Position(BSPhysObject member)
92 { return member.RawPosition; }
93 public virtual OMV.Quaternion Orientation(BSPhysObject member)
94 { return member.RawOrientation; }
95 // TODO: does this need to be done for Velocity and RotationalVelocityy?
96
97 // We keep the prim's mass in the linkset structure since it could be dependent on other prims 99 // We keep the prim's mass in the linkset structure since it could be dependent on other prims
98 protected float m_mass; 100 protected float m_mass;
99 public float LinksetMass 101 public float LinksetMass
@@ -116,7 +118,7 @@ public abstract class BSLinkset
116 get { return ComputeLinksetGeometricCenter(); } 118 get { return ComputeLinksetGeometricCenter(); }
117 } 119 }
118 120
119 protected void Initialize(BSScene scene, BSPhysObject parent) 121 protected BSLinkset(BSScene scene, BSPhysObject parent)
120 { 122 {
121 // A simple linkset of one (no children) 123 // A simple linkset of one (no children)
122 LinksetID = m_nextLinksetID++; 124 LinksetID = m_nextLinksetID++;
@@ -127,6 +129,7 @@ public abstract class BSLinkset
127 LinksetRoot = parent; 129 LinksetRoot = parent;
128 m_children = new HashSet<BSPhysObject>(); 130 m_children = new HashSet<BSPhysObject>();
129 m_mass = parent.RawMass; 131 m_mass = parent.RawMass;
132 Rebuilding = false;
130 } 133 }
131 134
132 // Link to a linkset where the child knows the parent. 135 // Link to a linkset where the child knows the parent.
@@ -219,7 +222,7 @@ public abstract class BSLinkset
219 // I am the root of a linkset and a new child is being added 222 // I am the root of a linkset and a new child is being added
220 // Called while LinkActivity is locked. 223 // Called while LinkActivity is locked.
221 protected abstract void AddChildToLinkset(BSPhysObject child); 224 protected abstract void AddChildToLinkset(BSPhysObject child);
222 225
223 // I am the root of a linkset and one of my children is being removed. 226 // I am the root of a linkset and one of my children is being removed.
224 // Safe to call even if the child is not really in my linkset. 227 // Safe to call even if the child is not really in my linkset.
225 protected abstract void RemoveChildFromLinkset(BSPhysObject child); 228 protected abstract void RemoveChildFromLinkset(BSPhysObject child);
@@ -229,6 +232,10 @@ public abstract class BSLinkset
229 // May be called at runtime or taint-time. 232 // May be called at runtime or taint-time.
230 public abstract void Refresh(BSPhysObject requestor); 233 public abstract void Refresh(BSPhysObject requestor);
231 234
235 // Flag denoting the linkset is in the process of being rebuilt.
236 // Used to know not the schedule a rebuild in the middle of a rebuild.
237 protected bool Rebuilding { get; set; }
238
232 // The object is going dynamic (physical). Do any setup necessary 239 // The object is going dynamic (physical). Do any setup necessary
233 // for a dynamic linkset. 240 // for a dynamic linkset.
234 // Only the state of the passed object can be modified. The rest of the linkset 241 // Only the state of the passed object can be modified. The rest of the linkset
@@ -245,8 +252,9 @@ public abstract class BSLinkset
245 252
246 // Called when a parameter update comes from the physics engine for any object 253 // Called when a parameter update comes from the physics engine for any object
247 // of the linkset is received. 254 // of the linkset is received.
255 // Passed flag is update came from physics engine (true) or the user (false).
248 // Called at taint-time!! 256 // Called at taint-time!!
249 public abstract void UpdateProperties(BSPhysObject physObject); 257 public abstract void UpdateProperties(BSPhysObject physObject, bool physicalUpdate);
250 258
251 // Routine used when rebuilding the body of the root of the linkset 259 // Routine used when rebuilding the body of the root of the linkset
252 // Destroy all the constraints have have been made to root. 260 // Destroy all the constraints have have been made to root.
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs
index b9c2cf9..2a7b72c 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs
@@ -28,22 +28,54 @@ using System;
28using System.Collections.Generic; 28using System.Collections.Generic;
29using System.Text; 29using System.Text;
30 30
31using OpenSim.Framework;
32
31using OMV = OpenMetaverse; 33using OMV = OpenMetaverse;
32 34
33namespace OpenSim.Region.Physics.BulletSPlugin 35namespace OpenSim.Region.Physics.BulletSPlugin
34{ 36{
37
38// When a child is linked, the relationship position of the child to the parent
39// is remembered so the child's world position can be recomputed when it is
40// removed from the linkset.
41sealed class BSLinksetCompoundInfo : BSLinksetInfo
42{
43 public OMV.Vector3 OffsetPos;
44 public OMV.Quaternion OffsetRot;
45 public BSLinksetCompoundInfo(OMV.Vector3 p, OMV.Quaternion r)
46 {
47 OffsetPos = p;
48 OffsetRot = r;
49 }
50 public override void Clear()
51 {
52 OffsetPos = OMV.Vector3.Zero;
53 OffsetRot = OMV.Quaternion.Identity;
54 }
55 public override string ToString()
56 {
57 StringBuilder buff = new StringBuilder();
58 buff.Append("<p=");
59 buff.Append(OffsetPos.ToString());
60 buff.Append(",r=");
61 buff.Append(OffsetRot.ToString());
62 buff.Append(">");
63 return buff.ToString();
64 }
65};
66
35public sealed class BSLinksetCompound : BSLinkset 67public sealed class BSLinksetCompound : BSLinkset
36{ 68{
37 private static string LogHeader = "[BULLETSIM LINKSET COMPOUND]"; 69 private static string LogHeader = "[BULLETSIM LINKSET COMPOUND]";
38 70
39 public BSLinksetCompound(BSScene scene, BSPhysObject parent) 71 public BSLinksetCompound(BSScene scene, BSPhysObject parent) : base(scene, parent)
40 { 72 {
41 base.Initialize(scene, parent);
42 } 73 }
43 74
44 // For compound implimented linksets, if there are children, use compound shape for the root. 75 // For compound implimented linksets, if there are children, use compound shape for the root.
45 public override BSPhysicsShapeType PreferredPhysicalShape(BSPhysObject requestor) 76 public override BSPhysicsShapeType PreferredPhysicalShape(BSPhysObject requestor)
46 { 77 {
78 // Returning 'unknown' means we don't have a preference.
47 BSPhysicsShapeType ret = BSPhysicsShapeType.SHAPE_UNKNOWN; 79 BSPhysicsShapeType ret = BSPhysicsShapeType.SHAPE_UNKNOWN;
48 if (IsRoot(requestor) && HasAnyChildren) 80 if (IsRoot(requestor) && HasAnyChildren)
49 { 81 {
@@ -55,23 +87,27 @@ public sealed class BSLinksetCompound : BSLinkset
55 87
56 // When physical properties are changed the linkset needs to recalculate 88 // When physical properties are changed the linkset needs to recalculate
57 // its internal properties. 89 // its internal properties.
58 // This is queued in the 'post taint' queue so the
59 // refresh will happen once after all the other taints are applied.
60 public override void Refresh(BSPhysObject requestor) 90 public override void Refresh(BSPhysObject requestor)
61 { 91 {
62 // External request for Refresh (from BSPrim) is not necessary 92 // Something changed so do the rebuilding thing
63 // InternalRefresh(requestor); 93 // ScheduleRebuild();
64 } 94 }
65 95
66 private void InternalRefresh(BSPhysObject requestor) 96 // Schedule a refresh to happen after all the other taint processing.
97 private void ScheduleRebuild(BSPhysObject requestor)
67 { 98 {
68 DetailLog("{0},BSLinksetCompound.Refresh,schedulingRefresh,requestor={1}", LinksetRoot.LocalID, requestor.LocalID); 99 DetailLog("{0},BSLinksetCompound.Refresh,schedulingRefresh,rebuilding={1}",
69 // Queue to happen after all the other taint processing 100 requestor.LocalID, Rebuilding);
70 PhysicsScene.PostTaintObject("BSLinksetCompound.Refresh", requestor.LocalID, delegate() 101 // When rebuilding, it is possible to set properties that would normally require a rebuild.
102 // If already rebuilding, don't request another rebuild.
103 if (!Rebuilding)
71 { 104 {
72 if (IsRoot(requestor) && HasAnyChildren) 105 PhysicsScene.PostTaintObject("BSLinksetCompound.Refresh", LinksetRoot.LocalID, delegate()
73 RecomputeLinksetCompound(); 106 {
74 }); 107 if (HasAnyChildren)
108 RecomputeLinksetCompound();
109 });
110 }
75 } 111 }
76 112
77 // The object is going dynamic (physical). Do any setup necessary 113 // The object is going dynamic (physical). Do any setup necessary
@@ -84,12 +120,23 @@ public sealed class BSLinksetCompound : BSLinkset
84 { 120 {
85 bool ret = false; 121 bool ret = false;
86 DetailLog("{0},BSLinksetCompound.MakeDynamic,call,IsRoot={1}", child.LocalID, IsRoot(child)); 122 DetailLog("{0},BSLinksetCompound.MakeDynamic,call,IsRoot={1}", child.LocalID, IsRoot(child));
87 if (!IsRoot(child)) 123 if (IsRoot(child))
124 {
125 // The root is going dynamic. Make sure mass is properly set.
126 m_mass = ComputeLinksetMass();
127 ScheduleRebuild(LinksetRoot);
128 }
129 else
88 { 130 {
89 // Physical children are removed from the world as the shape ofthe root compound 131 // The origional prims are removed from the world as the shape of the root compound
90 // shape takes over. 132 // shape takes over.
91 BulletSimAPI.AddToCollisionFlags2(child.PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE); 133 BulletSimAPI.AddToCollisionFlags2(child.PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE);
92 BulletSimAPI.ForceActivationState2(child.PhysBody.ptr, ActivationState.DISABLE_SIMULATION); 134 BulletSimAPI.ForceActivationState2(child.PhysBody.ptr, ActivationState.DISABLE_SIMULATION);
135 // We don't want collisions from the old linkset children.
136 BulletSimAPI.RemoveFromCollisionFlags2(child.PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
137
138 child.PhysBody.collisionType = CollisionType.LinksetChild;
139
93 ret = true; 140 ret = true;
94 } 141 }
95 return ret; 142 return ret;
@@ -104,33 +151,39 @@ public sealed class BSLinksetCompound : BSLinkset
104 { 151 {
105 bool ret = false; 152 bool ret = false;
106 DetailLog("{0},BSLinksetCompound.MakeStatic,call,IsRoot={1}", child.LocalID, IsRoot(child)); 153 DetailLog("{0},BSLinksetCompound.MakeStatic,call,IsRoot={1}", child.LocalID, IsRoot(child));
107 if (!IsRoot(child)) 154 if (IsRoot(child))
155 {
156 ScheduleRebuild(LinksetRoot);
157 }
158 else
108 { 159 {
109 // The non-physical children can come back to life. 160 // The non-physical children can come back to life.
110 BulletSimAPI.RemoveFromCollisionFlags2(child.PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE); 161 BulletSimAPI.RemoveFromCollisionFlags2(child.PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE);
111 // Don't force activation so setting of DISABLE_SIMULATION can stay. 162
163 child.PhysBody.collisionType = CollisionType.LinksetChild;
164
165 // Don't force activation so setting of DISABLE_SIMULATION can stay if used.
112 BulletSimAPI.Activate2(child.PhysBody.ptr, false); 166 BulletSimAPI.Activate2(child.PhysBody.ptr, false);
113 ret = true; 167 ret = true;
114 } 168 }
115 return ret; 169 return ret;
116 } 170 }
117 171
118 // Called at taint-time!! 172 public override void UpdateProperties(BSPhysObject updated, bool physicalUpdate)
119 public override void UpdateProperties(BSPhysObject updated)
120 { 173 {
121 // Nothing to do for constraints on property updates 174 // The user moving a child around requires the rebuilding of the linkset compound shape
122 } 175 // One problem is this happens when a border is crossed -- the simulator implementation
123 176 // is to store the position into the group which causes the move of the object
124 // The children move around in relationship to the root. 177 // but it also means all the child positions get updated.
125 // Just grab the current values of wherever it is right now. 178 // What would cause an unnecessary rebuild so we make sure the linkset is in a
126 public override OMV.Vector3 Position(BSPhysObject member) 179 // region before bothering to do a rebuild.
127 { 180 if (!IsRoot(updated)
128 return BulletSimAPI.GetPosition2(member.PhysBody.ptr); 181 && !physicalUpdate
129 } 182 && PhysicsScene.TerrainManager.IsWithinKnownTerrain(LinksetRoot.RawPosition))
130 183 {
131 public override OMV.Quaternion Orientation(BSPhysObject member) 184 updated.LinksetInfo = null;
132 { 185 ScheduleRebuild(updated);
133 return BulletSimAPI.GetOrientation2(member.PhysBody.ptr); 186 }
134 } 187 }
135 188
136 // Routine called when rebuilding the body of some member of the linkset. 189 // Routine called when rebuilding the body of some member of the linkset.
@@ -146,20 +199,58 @@ public sealed class BSLinksetCompound : BSLinkset
146 199
147 if (!IsRoot(child)) 200 if (!IsRoot(child))
148 { 201 {
149 // Cause the current shape to be freed and the new one to be built. 202 // Because it is a convenient time, recompute child world position and rotation based on
150 InternalRefresh(LinksetRoot); 203 // its position in the linkset.
151 ret = true; 204 RecomputeChildWorldPosition(child, true);
152 } 205 }
153 206
207 // Cannot schedule a refresh/rebuild here because this routine is called when
208 // the linkset is being rebuilt.
209 // InternalRefresh(LinksetRoot);
210
154 return ret; 211 return ret;
155 } 212 }
156 213
157 // Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true', 214 // Companion to RemoveBodyDependencies(). If RemoveBodyDependencies() returns 'true',
158 // this routine will restore the removed constraints. 215 // this routine will restore the removed constraints.
159 // Called at taint-time!! 216 // Called at taint-time!!
160 public override void RestoreBodyDependencies(BSPrim child) 217 public override void RestoreBodyDependencies(BSPrim child)
161 { 218 {
162 // The Refresh operation queued by RemoveBodyDependencies() will build any missing constraints. 219 }
220
221 // When the linkset is built, the child shape is added to the compound shape relative to the
222 // root shape. The linkset then moves around but this does not move the actual child
223 // prim. The child prim's location must be recomputed based on the location of the root shape.
224 private void RecomputeChildWorldPosition(BSPhysObject child, bool inTaintTime)
225 {
226 BSLinksetCompoundInfo lci = child.LinksetInfo as BSLinksetCompoundInfo;
227 if (lci != null)
228 {
229 if (inTaintTime)
230 {
231 OMV.Vector3 oldPos = child.RawPosition;
232 child.ForcePosition = LinksetRoot.RawPosition + lci.OffsetPos;
233 child.ForceOrientation = LinksetRoot.RawOrientation * lci.OffsetRot;
234 DetailLog("{0},BSLinksetCompound.RecomputeChildWorldPosition,oldPos={1},lci={2},newPos={3}",
235 child.LocalID, oldPos, lci, child.RawPosition);
236 }
237 else
238 {
239 // TaintedObject is not used here so the raw position is set now and not at taint-time.
240 child.Position = LinksetRoot.RawPosition + lci.OffsetPos;
241 child.Orientation = LinksetRoot.RawOrientation * lci.OffsetRot;
242 }
243 }
244 else
245 {
246 // This happens when children have been added to the linkset but the linkset
247 // has not been constructed yet. So like, at taint time, adding children to a linkset
248 // and then changing properties of the children (makePhysical, for instance)
249 // but the post-print action of actually rebuilding the linkset has not yet happened.
250 // PhysicsScene.Logger.WarnFormat("{0} Restoring linkset child position failed because of no relative position computed. ID={1}",
251 // LogHeader, child.LocalID);
252 DetailLog("{0},BSLinksetCompound.recomputeChildWorldPosition,noRelativePositonInfo", child.LocalID);
253 }
163 } 254 }
164 255
165 // ================================================================ 256 // ================================================================
@@ -174,14 +265,14 @@ public sealed class BSLinksetCompound : BSLinkset
174 265
175 DetailLog("{0},BSLinksetCompound.AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID); 266 DetailLog("{0},BSLinksetCompound.AddChildToLinkset,call,child={1}", LinksetRoot.LocalID, child.LocalID);
176 267
177 // Cause constraints and assorted properties to be recomputed before the next simulation step. 268 // Rebuild the compound shape with the new child shape included
178 InternalRefresh(LinksetRoot); 269 ScheduleRebuild(child);
179 } 270 }
180 return; 271 return;
181 } 272 }
182 273
183 // Remove the specified child from the linkset. 274 // Remove the specified child from the linkset.
184 // Safe to call even if the child is not really in my linkset. 275 // Safe to call even if the child is not really in the linkset.
185 protected override void RemoveChildFromLinkset(BSPhysObject child) 276 protected override void RemoveChildFromLinkset(BSPhysObject child)
186 { 277 {
187 if (m_children.Remove(child)) 278 if (m_children.Remove(child))
@@ -192,6 +283,7 @@ public sealed class BSLinksetCompound : BSLinkset
192 child.LocalID, child.PhysBody.ptr.ToString("X")); 283 child.LocalID, child.PhysBody.ptr.ToString("X"));
193 284
194 // Cause the child's body to be rebuilt and thus restored to normal operation 285 // Cause the child's body to be rebuilt and thus restored to normal operation
286 RecomputeChildWorldPosition(child, false);
195 child.ForceBodyShapeRebuild(false); 287 child.ForceBodyShapeRebuild(false);
196 288
197 if (!HasAnyChildren) 289 if (!HasAnyChildren)
@@ -201,8 +293,8 @@ public sealed class BSLinksetCompound : BSLinkset
201 } 293 }
202 else 294 else
203 { 295 {
204 // Schedule a rebuild of the linkset before the next simulation tick. 296 // Rebuild the compound shape with the child removed
205 InternalRefresh(LinksetRoot); 297 ScheduleRebuild(child);
206 } 298 }
207 } 299 }
208 return; 300 return;
@@ -215,54 +307,83 @@ public sealed class BSLinksetCompound : BSLinkset
215 // Called at taint time!! 307 // Called at taint time!!
216 private void RecomputeLinksetCompound() 308 private void RecomputeLinksetCompound()
217 { 309 {
218 // Cause the root shape to be rebuilt as a compound object with just the root in it 310 try
219 LinksetRoot.ForceBodyShapeRebuild(true); 311 {
312 // Suppress rebuilding while rebuilding
313 Rebuilding = true;
220 314
221 DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,start,rBody={1},rShape={2},numChildren={3}", 315 // Cause the root shape to be rebuilt as a compound object with just the root in it
222 LinksetRoot.LocalID, LinksetRoot.PhysBody, LinksetRoot.PhysShape, NumberOfChildren); 316 LinksetRoot.ForceBodyShapeRebuild(true);
223 317
224 // Add a shape for each of the other children in the linkset 318 DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,start,rBody={1},rShape={2},numChildren={3}",
225 ForEachMember(delegate(BSPhysObject cPrim) 319 LinksetRoot.LocalID, LinksetRoot.PhysBody, LinksetRoot.PhysShape, NumberOfChildren);
226 { 320
227 if (!IsRoot(cPrim)) 321 // Add a shape for each of the other children in the linkset
322 ForEachMember(delegate(BSPhysObject cPrim)
228 { 323 {
229 // Each child position and rotation is given relative to the root. 324 if (!IsRoot(cPrim))
230 OMV.Quaternion invRootOrientation = OMV.Quaternion.Inverse(LinksetRoot.RawOrientation); 325 {
231 OMV.Vector3 displacementPos = (cPrim.RawPosition - LinksetRoot.RawPosition) * invRootOrientation; 326 // Compute the displacement of the child from the root of the linkset.
232 OMV.Quaternion displacementRot = cPrim.RawOrientation * invRootOrientation; 327 // This info is saved in the child prim so the relationship does not
328 // change over time and the new child position can be computed
329 // when the linkset is being disassembled (the linkset may have moved).
330 BSLinksetCompoundInfo lci = cPrim.LinksetInfo as BSLinksetCompoundInfo;
331 if (lci == null)
332 {
333 // Each child position and rotation is given relative to the root.
334 OMV.Quaternion invRootOrientation = OMV.Quaternion.Inverse(LinksetRoot.RawOrientation);
335 OMV.Vector3 displacementPos = (cPrim.RawPosition - LinksetRoot.RawPosition) * invRootOrientation;
336 OMV.Quaternion displacementRot = cPrim.RawOrientation * invRootOrientation;
337
338 // Save relative position for recomputing child's world position after moving linkset.
339 lci = new BSLinksetCompoundInfo(displacementPos, displacementRot);
340 cPrim.LinksetInfo = lci;
341 DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,creatingRelPos,lci={1}", cPrim.LocalID, lci);
342 }
233 343
234 DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addMemberToShape,mID={1},mShape={2},dispPos={3},dispRot={4}", 344 DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addMemberToShape,mID={1},mShape={2},dispPos={3},dispRot={4}",
235 LinksetRoot.LocalID, cPrim.LocalID, cPrim.PhysShape, displacementPos, displacementRot); 345 LinksetRoot.LocalID, cPrim.LocalID, cPrim.PhysShape, lci.OffsetPos, lci.OffsetRot);
236 346
237 if (cPrim.PhysShape.isNativeShape) 347 if (cPrim.PhysShape.isNativeShape)
238 { 348 {
239 // Native shapes are not shared so we need to create a new one. 349 // A native shape is turning into a hull collision shape because native
240 // A mesh or hull is created because scale is not available on a native shape. 350 // shapes are not shared so we have to hullify it so it will be tracked
241 // (TODO: Bullet does have a btScaledCollisionShape. Can that be used?) 351 // and freed at the correct time. This also solves the scaling problem
242 BulletShape saveShape = cPrim.PhysShape; 352 // (native shapes scaled but hull/meshes are assumed to not be).
243 cPrim.PhysShape.ptr = IntPtr.Zero; // Don't let the create free the child's shape 353 // TODO: decide of the native shape can just be used in the compound shape.
244 PhysicsScene.Shapes.CreateGeomMeshOrHull(cPrim, null); 354 // Use call to CreateGeomNonSpecial().
245 BulletShape newShape = cPrim.PhysShape; 355 BulletShape saveShape = cPrim.PhysShape;
246 cPrim.PhysShape = saveShape; 356 cPrim.PhysShape.Clear(); // Don't let the create free the child's shape
247 BulletSimAPI.AddChildShapeToCompoundShape2(LinksetRoot.PhysShape.ptr, newShape.ptr, displacementPos, displacementRot); 357 // PhysicsScene.Shapes.CreateGeomNonSpecial(true, cPrim, null);
248 } 358 PhysicsScene.Shapes.CreateGeomMeshOrHull(cPrim, null);
249 else 359 BulletShape newShape = cPrim.PhysShape;
250 { 360 cPrim.PhysShape = saveShape;
251 // For the shared shapes (meshes and hulls), just use the shape in the child. 361 BulletSimAPI.AddChildShapeToCompoundShape2(LinksetRoot.PhysShape.ptr, newShape.ptr, lci.OffsetPos, lci.OffsetRot);
252 if (PhysicsScene.Shapes.ReferenceShape(cPrim.PhysShape)) 362 }
363 else
253 { 364 {
254 PhysicsScene.Logger.ErrorFormat("{0} Rebuilt sharable shape when building linkset! Region={1}, primID={2}, shape={3}", 365 // For the shared shapes (meshes and hulls), just use the shape in the child.
255 LogHeader, PhysicsScene.RegionName, cPrim.LocalID, cPrim.PhysShape); 366 // The reference count added here will be decremented when the compound shape
367 // is destroyed in BSShapeCollection (the child shapes are looped over and dereferenced).
368 if (PhysicsScene.Shapes.ReferenceShape(cPrim.PhysShape))
369 {
370 PhysicsScene.Logger.ErrorFormat("{0} Rebuilt sharable shape when building linkset! Region={1}, primID={2}, shape={3}",
371 LogHeader, PhysicsScene.RegionName, cPrim.LocalID, cPrim.PhysShape);
372 }
373 BulletSimAPI.AddChildShapeToCompoundShape2(LinksetRoot.PhysShape.ptr, cPrim.PhysShape.ptr, lci.OffsetPos, lci.OffsetRot);
256 } 374 }
257 BulletSimAPI.AddChildShapeToCompoundShape2(LinksetRoot.PhysShape.ptr, cPrim.PhysShape.ptr, displacementPos, displacementRot);
258 } 375 }
259 } 376 return false; // 'false' says to move onto the next child in the list
260 return false; // 'false' says to move onto the next child in the list 377 });
261 });
262 378
263 // With all of the linkset packed into the root prim, it has the mass of everyone. 379 // With all of the linkset packed into the root prim, it has the mass of everyone.
264 float linksetMass = LinksetMass; 380 float linksetMass = LinksetMass;
265 LinksetRoot.UpdatePhysicalMassProperties(linksetMass); 381 LinksetRoot.UpdatePhysicalMassProperties(linksetMass);
382 }
383 finally
384 {
385 Rebuilding = false;
386 }
266 387
267 BulletSimAPI.RecalculateCompoundShapeLocalAabb2(LinksetRoot.PhysShape.ptr); 388 BulletSimAPI.RecalculateCompoundShapeLocalAabb2(LinksetRoot.PhysShape.ptr);
268 389
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs
index c855fda..8c36c31 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetConstraints.cs
@@ -36,9 +36,8 @@ public sealed class BSLinksetConstraints : BSLinkset
36{ 36{
37 // private static string LogHeader = "[BULLETSIM LINKSET CONSTRAINTS]"; 37 // private static string LogHeader = "[BULLETSIM LINKSET CONSTRAINTS]";
38 38
39 public BSLinksetConstraints(BSScene scene, BSPhysObject parent) 39 public BSLinksetConstraints(BSScene scene, BSPhysObject parent) : base(scene, parent)
40 { 40 {
41 base.Initialize(scene, parent);
42 } 41 }
43 42
44 // When physical properties are changed the linkset needs to recalculate 43 // When physical properties are changed the linkset needs to recalculate
@@ -79,23 +78,11 @@ public sealed class BSLinksetConstraints : BSLinkset
79 } 78 }
80 79
81 // Called at taint-time!! 80 // Called at taint-time!!
82 public override void UpdateProperties(BSPhysObject updated) 81 public override void UpdateProperties(BSPhysObject updated, bool inTaintTime)
83 { 82 {
84 // Nothing to do for constraints on property updates 83 // Nothing to do for constraints on property updates
85 } 84 }
86 85
87 // The children of the linkset are moved around by the constraints.
88 // Just grab the current values of wherever it is right now.
89 public override OMV.Vector3 Position(BSPhysObject member)
90 {
91 return BulletSimAPI.GetPosition2(member.PhysBody.ptr);
92 }
93
94 public override OMV.Quaternion Orientation(BSPhysObject member)
95 {
96 return BulletSimAPI.GetOrientation2(member.PhysBody.ptr);
97 }
98
99 // Routine called when rebuilding the body of some member of the linkset. 86 // Routine called when rebuilding the body of some member of the linkset.
100 // Destroy all the constraints have have been made to root and set 87 // Destroy all the constraints have have been made to root and set
101 // up to rebuild the constraints before the next simulation step. 88 // up to rebuild the constraints before the next simulation step.
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSMaterials.cs b/OpenSim/Region/Physics/BulletSPlugin/BSMaterials.cs
new file mode 100755
index 0000000..c113a43
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSMaterials.cs
@@ -0,0 +1,200 @@
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 copyrightD
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 */
27using System;
28using System.Collections.Generic;
29using System.Text;
30using System.Reflection;
31using Nini.Config;
32
33namespace OpenSim.Region.Physics.BulletSPlugin
34{
35
36public struct MaterialAttributes
37{
38 // Material type values that correspond with definitions for LSL
39 public enum Material : int
40 {
41 Stone = 0,
42 Metal,
43 Glass,
44 Wood,
45 Flesh,
46 Plastic,
47 Rubber,
48 Light,
49 // Hereafter are BulletSim additions
50 Avatar,
51 NumberOfTypes // the count of types in the enum.
52 }
53
54 // Names must be in the order of the above enum.
55 // These names must coorespond to the lower case field names in the MaterialAttributes
56 // structure as reflection is used to select the field to put the value in.
57 public static readonly string[] MaterialAttribs = { "Density", "Friction", "Restitution"};
58
59 public MaterialAttributes(string t, float d, float f, float r)
60 {
61 type = t;
62 density = d;
63 friction = f;
64 restitution = r;
65 }
66 public string type;
67 public float density;
68 public float friction;
69 public float restitution;
70}
71
72public static class BSMaterials
73{
74 // Attributes for each material type
75 private static readonly MaterialAttributes[] Attributes;
76
77 // Map of material name to material type code
78 public static readonly Dictionary<string, MaterialAttributes.Material> MaterialMap;
79
80 static BSMaterials()
81 {
82 // Attribute sets for both the non-physical and physical instances of materials.
83 Attributes = new MaterialAttributes[(int)MaterialAttributes.Material.NumberOfTypes * 2];
84
85 // Map of name to type code.
86 MaterialMap = new Dictionary<string, MaterialAttributes.Material>();
87 MaterialMap.Add("Stone", MaterialAttributes.Material.Stone);
88 MaterialMap.Add("Metal", MaterialAttributes.Material.Metal);
89 MaterialMap.Add("Glass", MaterialAttributes.Material.Glass);
90 MaterialMap.Add("Wood", MaterialAttributes.Material.Wood);
91 MaterialMap.Add("Flesh", MaterialAttributes.Material.Flesh);
92 MaterialMap.Add("Plastic", MaterialAttributes.Material.Plastic);
93 MaterialMap.Add("Rubber", MaterialAttributes.Material.Rubber);
94 MaterialMap.Add("Light", MaterialAttributes.Material.Light);
95 MaterialMap.Add("Avatar", MaterialAttributes.Material.Avatar);
96 }
97
98 // This is where all the default material attributes are defined.
99 public static void InitializeFromDefaults(ConfigurationParameters parms)
100 {
101 // Values from http://wiki.secondlife.com/wiki/PRIM_MATERIAL
102 float dDensity = parms.defaultDensity;
103 float dFriction = parms.defaultFriction;
104 float dRestitution = parms.defaultRestitution;
105 Attributes[(int)MaterialAttributes.Material.Stone] =
106 new MaterialAttributes("stone",dDensity, 0.8f, 0.4f);
107 Attributes[(int)MaterialAttributes.Material.Metal] =
108 new MaterialAttributes("metal",dDensity, 0.3f, 0.4f);
109 Attributes[(int)MaterialAttributes.Material.Glass] =
110 new MaterialAttributes("glass",dDensity, 0.2f, 0.7f);
111 Attributes[(int)MaterialAttributes.Material.Wood] =
112 new MaterialAttributes("wood",dDensity, 0.6f, 0.5f);
113 Attributes[(int)MaterialAttributes.Material.Flesh] =
114 new MaterialAttributes("flesh",dDensity, 0.9f, 0.3f);
115 Attributes[(int)MaterialAttributes.Material.Plastic] =
116 new MaterialAttributes("plastic",dDensity, 0.4f, 0.7f);
117 Attributes[(int)MaterialAttributes.Material.Rubber] =
118 new MaterialAttributes("rubber",dDensity, 0.9f, 0.9f);
119 Attributes[(int)MaterialAttributes.Material.Light] =
120 new MaterialAttributes("light",dDensity, dFriction, dRestitution);
121 Attributes[(int)MaterialAttributes.Material.Avatar] =
122 new MaterialAttributes("avatar",60f, 0.2f, 0f);
123
124 Attributes[(int)MaterialAttributes.Material.Stone + (int)MaterialAttributes.Material.NumberOfTypes] =
125 new MaterialAttributes("stonePhysical",dDensity, 0.8f, 0.4f);
126 Attributes[(int)MaterialAttributes.Material.Metal + (int)MaterialAttributes.Material.NumberOfTypes] =
127 new MaterialAttributes("metalPhysical",dDensity, 0.8f, 0.4f);
128 Attributes[(int)MaterialAttributes.Material.Glass + (int)MaterialAttributes.Material.NumberOfTypes] =
129 new MaterialAttributes("glassPhysical",dDensity, 0.8f, 0.7f);
130 Attributes[(int)MaterialAttributes.Material.Wood + (int)MaterialAttributes.Material.NumberOfTypes] =
131 new MaterialAttributes("woodPhysical",dDensity, 0.8f, 0.5f);
132 Attributes[(int)MaterialAttributes.Material.Flesh + (int)MaterialAttributes.Material.NumberOfTypes] =
133 new MaterialAttributes("fleshPhysical",dDensity, 0.8f, 0.3f);
134 Attributes[(int)MaterialAttributes.Material.Plastic + (int)MaterialAttributes.Material.NumberOfTypes] =
135 new MaterialAttributes("plasticPhysical",dDensity, 0.8f, 0.7f);
136 Attributes[(int)MaterialAttributes.Material.Rubber + (int)MaterialAttributes.Material.NumberOfTypes] =
137 new MaterialAttributes("rubberPhysical",dDensity, 0.8f, 0.9f);
138 Attributes[(int)MaterialAttributes.Material.Light + (int)MaterialAttributes.Material.NumberOfTypes] =
139 new MaterialAttributes("lightPhysical",dDensity, dFriction, dRestitution);
140 Attributes[(int)MaterialAttributes.Material.Avatar + (int)MaterialAttributes.Material.NumberOfTypes] =
141 new MaterialAttributes("avatarPhysical",60f, 0.2f, 0f);
142 }
143
144 // Under the [BulletSim] section, one can change the individual material
145 // attribute values. The format of the configuration parameter is:
146 // <materialName><Attribute>["Physical"] = floatValue
147 // For instance:
148 // [BulletSim]
149 // StoneFriction = 0.2
150 // FleshRestitutionPhysical = 0.8
151 // Materials can have different parameters for their static and
152 // physical instantiations. When setting the non-physical value,
153 // both values are changed. Setting the physical value only changes
154 // the physical value.
155 public static void InitializefromParameters(IConfig pConfig)
156 {
157 foreach (KeyValuePair<string, MaterialAttributes.Material> kvp in MaterialMap)
158 {
159 string matName = kvp.Key;
160 foreach (string attribName in MaterialAttributes.MaterialAttribs)
161 {
162 string paramName = matName + attribName;
163 if (pConfig.Contains(paramName))
164 {
165 float paramValue = pConfig.GetFloat(paramName);
166 SetAttributeValue((int)kvp.Value, attribName, paramValue);
167 // set the physical value also
168 SetAttributeValue((int)kvp.Value + (int)MaterialAttributes.Material.NumberOfTypes, attribName, paramValue);
169 }
170 paramName += "Physical";
171 if (pConfig.Contains(paramName))
172 {
173 float paramValue = pConfig.GetFloat(paramName);
174 SetAttributeValue((int)kvp.Value + (int)MaterialAttributes.Material.NumberOfTypes, attribName, paramValue);
175 }
176 }
177 }
178 }
179
180 // Use reflection to set the value in the attribute structure.
181 private static void SetAttributeValue(int matType, string attribName, float val)
182 {
183 MaterialAttributes thisAttrib = Attributes[matType];
184 FieldInfo fieldInfo = thisAttrib.GetType().GetField(attribName.ToLower());
185 if (fieldInfo != null)
186 {
187 fieldInfo.SetValue(thisAttrib, val);
188 Attributes[matType] = thisAttrib;
189 }
190 }
191
192 // Given a material type, return a structure of attributes.
193 public static MaterialAttributes GetAttributes(MaterialAttributes.Material type, bool isPhysical)
194 {
195 int ind = (int)type;
196 if (isPhysical) ind += (int)MaterialAttributes.Material.NumberOfTypes;
197 return Attributes[ind];
198 }
199}
200}
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs b/OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs
index bc6e4c4..e0faf4e 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSMotors.cs
@@ -1,104 +1,220 @@
1using System; 1/*
2using System.Collections.Generic; 2 * Copyright (c) Contributors, http://opensimulator.org/
3using System.Text; 3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4using OpenMetaverse; 4 *
5 5 * Redistribution and use in source and binary forms, with or without
6namespace OpenSim.Region.Physics.BulletSPlugin 6 * modification, are permitted provided that the following conditions are met:
7{ 7 * * Redistributions of source code must retain the above copyright
8public abstract class BSMotor 8 * notice, this list of conditions and the following disclaimer.
9{ 9 * * Redistributions in binary form must reproduce the above copyright
10 public virtual void Reset() { } 10 * notice, this list of conditions and the following disclaimer in the
11 public virtual void Zero() { } 11 * documentation and/or other materials provided with the distribution.
12} 12 * * Neither the name of the OpenSimulator Project nor the
13// Can all the incremental stepping be replaced with motor classes? 13 * names of its contributors may be used to endorse or promote products
14public class BSVMotor : BSMotor 14 * derived from this software without specific prior written permission.
15{ 15 *
16 public Vector3 FrameOfReference { get; set; } 16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 public Vector3 Offset { get; set; } 17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 public float TimeScale { get; set; } 19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 public float TargetValueDecayTimeScale { get; set; } 20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 public Vector3 CurrentValueReductionTimescale { get; set; } 21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 public float Efficiency { get; set; } 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 public Vector3 TargetValue { get; private set; } 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 public Vector3 CurrentValue { get; private set; } 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 26 *
27 27 */
28 28using System;
29 BSVMotor(float timeScale, float decayTimeScale, Vector3 frictionTimeScale, float efficiency) 29using System.Collections.Generic;
30 { 30using System.Text;
31 TimeScale = timeScale; 31using OpenMetaverse;
32 TargetValueDecayTimeScale = decayTimeScale; 32
33 CurrentValueReductionTimescale = frictionTimeScale; 33namespace OpenSim.Region.Physics.BulletSPlugin
34 Efficiency = efficiency; 34{
35 } 35public abstract class BSMotor
36 public void SetCurrent(Vector3 current) 36{
37 { 37 // Timescales and other things can be turned off by setting them to 'infinite'.
38 CurrentValue = current; 38 public const float Infinite = 12345f;
39 } 39 public readonly static Vector3 InfiniteVector = new Vector3(BSMotor.Infinite, BSMotor.Infinite, BSMotor.Infinite);
40 public void SetTarget(Vector3 target) 40
41 { 41 public BSMotor(string useName)
42 TargetValue = target; 42 {
43 } 43 UseName = useName;
44 public Vector3 Step(float timeStep) 44 PhysicsScene = null;
45 { 45 }
46 if (CurrentValue.LengthSquared() > 0.001f) 46 public virtual void Reset() { }
47 { 47 public virtual void Zero() { }
48 // Vector3 origDir = Target; // DEBUG 48
49 // Vector3 origVel = CurrentValue; // DEBUG 49 // A name passed at motor creation for easily identifyable debugging messages.
50 50 public string UseName { get; private set; }
51 // Add (desiredVelocity - currentAppliedVelocity) / howLongItShouldTakeToComplete 51
52 Vector3 addAmount = (TargetValue - CurrentValue)/(TargetValue) * timeStep; 52 // Used only for outputting debug information. Might not be set so check for null.
53 CurrentValue += addAmount; 53 public BSScene PhysicsScene { get; set; }
54 54 protected void MDetailLog(string msg, params Object[] parms)
55 float decayFactor = (1.0f / TargetValueDecayTimeScale) * timeStep; 55 {
56 TargetValue *= (1f - decayFactor); 56 if (PhysicsScene != null)
57 57 {
58 Vector3 frictionFactor = (Vector3.One / CurrentValueReductionTimescale) * timeStep; 58 if (PhysicsScene.VehicleLoggingEnabled)
59 CurrentValue *= (Vector3.One - frictionFactor); 59 {
60 } 60 PhysicsScene.DetailLog(msg, parms);
61 else 61 }
62 { 62 }
63 // if what remains of direction is very small, zero it. 63 }
64 TargetValue = Vector3.Zero; 64}
65 CurrentValue = Vector3.Zero; 65// Can all the incremental stepping be replaced with motor classes?
66 66
67 // VDetailLog("{0},MoveLinear,zeroed", Prim.LocalID); 67// Motor which moves CurrentValue to TargetValue over TimeScale seconds.
68 } 68// The TargetValue decays in TargetValueDecayTimeScale and
69 return CurrentValue; 69// the CurrentValue will be held back by FrictionTimeScale.
70 } 70// TimeScale and TargetDelayTimeScale may be 'infinite' which means go decay.
71} 71
72 72// For instance, if something is moving at speed X and the desired speed is Y,
73public class BSFMotor : BSMotor 73// CurrentValue is X and TargetValue is Y. As the motor is stepped, new
74{ 74// values of CurrentValue are returned that approach the TargetValue.
75 public float TimeScale { get; set; } 75// The feature of decaying TargetValue is so vehicles will eventually
76 public float DecayTimeScale { get; set; } 76// come to a stop rather than run forever. This can be disabled by
77 public float Friction { get; set; } 77// setting TargetValueDecayTimescale to 'infinite'.
78 public float Efficiency { get; set; } 78// The change from CurrentValue to TargetValue is linear over TimeScale seconds.
79 79public class BSVMotor : BSMotor
80 public float Target { get; private set; } 80{
81 public float CurrentValue { get; private set; } 81 // public Vector3 FrameOfReference { get; set; }
82 82 // public Vector3 Offset { get; set; }
83 BSFMotor(float timeScale, float decayTimescale, float friction, float efficiency) 83
84 { 84 public float TimeScale { get; set; }
85 } 85 public float TargetValueDecayTimeScale { get; set; }
86 public void SetCurrent(float target) 86 public Vector3 FrictionTimescale { get; set; }
87 { 87 public float Efficiency { get; set; }
88 } 88
89 public void SetTarget(float target) 89 public Vector3 TargetValue { get; private set; }
90 { 90 public Vector3 CurrentValue { get; private set; }
91 } 91
92 public float Step(float timeStep) 92 public BSVMotor(string useName)
93 { 93 : base(useName)
94 return 0f; 94 {
95 } 95 TimeScale = TargetValueDecayTimeScale = BSMotor.Infinite;
96} 96 Efficiency = 1f;
97public class BSPIDMotor : BSMotor 97 FrictionTimescale = BSMotor.InfiniteVector;
98{ 98 CurrentValue = TargetValue = Vector3.Zero;
99 // TODO: write and use this one 99 }
100 BSPIDMotor() 100 public BSVMotor(string useName, float timeScale, float decayTimeScale, Vector3 frictionTimeScale, float efficiency)
101 { 101 : this(useName)
102 } 102 {
103} 103 TimeScale = timeScale;
104} 104 TargetValueDecayTimeScale = decayTimeScale;
105 FrictionTimescale = frictionTimeScale;
106 Efficiency = efficiency;
107 CurrentValue = TargetValue = Vector3.Zero;
108 }
109 public void SetCurrent(Vector3 current)
110 {
111 CurrentValue = current;
112 }
113 public void SetTarget(Vector3 target)
114 {
115 TargetValue = target;
116 }
117
118 // A form of stepping that does not take the time quantum into account.
119 // The caller must do the right thing later.
120 public Vector3 Step()
121 {
122 return Step(1f);
123 }
124
125 public Vector3 Step(float timeStep)
126 {
127 Vector3 returnCurrent = Vector3.Zero;
128 if (!CurrentValue.ApproxEquals(TargetValue, 0.01f))
129 {
130 Vector3 origTarget = TargetValue; // DEBUG
131 Vector3 origCurrVal = CurrentValue; // DEBUG
132
133 // Addition = (desiredVector - currentAppliedVector) / secondsItShouldTakeToComplete
134 Vector3 addAmount = (TargetValue - CurrentValue)/TimeScale * timeStep;
135 CurrentValue += addAmount;
136
137 // The desired value reduces to zero which also reduces the difference with current.
138 // If the decay time is infinite, don't decay at all.
139 float decayFactor = 0f;
140 if (TargetValueDecayTimeScale != BSMotor.Infinite)
141 {
142 decayFactor = (1.0f / TargetValueDecayTimeScale) * timeStep;
143 TargetValue *= (1f - decayFactor);
144 }
145
146 Vector3 frictionFactor = Vector3.Zero;
147 if (FrictionTimescale != BSMotor.InfiniteVector)
148 {
149 // frictionFactor = (Vector3.One / FrictionTimescale) * timeStep;
150 // Individual friction components can be 'infinite' so compute each separately.
151 frictionFactor.X = FrictionTimescale.X == BSMotor.Infinite ? 0f : (1f / FrictionTimescale.X) * timeStep;
152 frictionFactor.Y = FrictionTimescale.Y == BSMotor.Infinite ? 0f : (1f / FrictionTimescale.Y) * timeStep;
153 frictionFactor.Z = FrictionTimescale.Z == BSMotor.Infinite ? 0f : (1f / FrictionTimescale.Z) * timeStep;
154 CurrentValue *= (Vector3.One - frictionFactor);
155 }
156
157 returnCurrent = CurrentValue;
158
159 MDetailLog("{0}, BSVMotor.Step,nonZero,{1},origCurr={2},origTarget={3},timeStep={4},timeScale={5},addAmnt={6},targetDecay={7},decayFact={8},fricTS={9},frictFact={10}",
160 BSScene.DetailLogZero, UseName, origCurrVal, origTarget,
161 timeStep, TimeScale, addAmount,
162 TargetValueDecayTimeScale, decayFactor,
163 FrictionTimescale, frictionFactor);
164 MDetailLog("{0}, BSVMotor.Step,nonZero,{1},curr={2},target={3},add={4},decay={5},frict={6},ret={7}",
165 BSScene.DetailLogZero, UseName, CurrentValue, TargetValue,
166 addAmount, decayFactor, frictionFactor, returnCurrent);
167 }
168 else
169 {
170 // Difference between what we have and target is small. Motor is done.
171 CurrentValue = Vector3.Zero;
172 TargetValue = Vector3.Zero;
173
174 MDetailLog("{0}, BSVMotor.Step,zero,{1},curr={2},target={3},ret={4}",
175 BSScene.DetailLogZero, UseName, TargetValue, CurrentValue, returnCurrent);
176
177 }
178 return returnCurrent;
179 }
180 public override string ToString()
181 {
182 return String.Format("<{0},curr={1},targ={2},decayTS={3},frictTS={4}>",
183 UseName, CurrentValue, TargetValue, TargetValueDecayTimeScale, FrictionTimescale);
184 }
185}
186
187public class BSFMotor : BSMotor
188{
189 public float TimeScale { get; set; }
190 public float DecayTimeScale { get; set; }
191 public float Friction { get; set; }
192 public float Efficiency { get; set; }
193
194 public float Target { get; private set; }
195 public float CurrentValue { get; private set; }
196
197 public BSFMotor(string useName, float timeScale, float decayTimescale, float friction, float efficiency)
198 : base(useName)
199 {
200 }
201 public void SetCurrent(float target)
202 {
203 }
204 public void SetTarget(float target)
205 {
206 }
207 public float Step(float timeStep)
208 {
209 return 0f;
210 }
211}
212public class BSPIDMotor : BSMotor
213{
214 // TODO: write and use this one
215 public BSPIDMotor(string useName)
216 : base(useName)
217 {
218 }
219}
220}
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs
index f6a890e..92a5f2f 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs
@@ -60,6 +60,9 @@ public abstract class BSPhysObject : PhysicsActor
60 Linkset = BSLinkset.Factory(PhysicsScene, this); 60 Linkset = BSLinkset.Factory(PhysicsScene, this);
61 LastAssetBuildFailed = false; 61 LastAssetBuildFailed = false;
62 62
63 // Default material type
64 Material = MaterialAttributes.Material.Wood;
65
63 CollisionCollection = new CollisionEventUpdate(); 66 CollisionCollection = new CollisionEventUpdate();
64 SubscribedEventsMs = 0; 67 SubscribedEventsMs = 0;
65 CollidingStep = 0; 68 CollidingStep = 0;
@@ -72,6 +75,7 @@ public abstract class BSPhysObject : PhysicsActor
72 public string TypeName { get; protected set; } 75 public string TypeName { get; protected set; }
73 76
74 public BSLinkset Linkset { get; set; } 77 public BSLinkset Linkset { get; set; }
78 public BSLinksetInfo LinksetInfo { get; set; }
75 79
76 // Return the object mass without calculating it or having side effects 80 // Return the object mass without calculating it or having side effects
77 public abstract float RawMass { get; } 81 public abstract float RawMass { get; }
@@ -105,10 +109,17 @@ public abstract class BSPhysObject : PhysicsActor
105 public EntityProperties CurrentEntityProperties { get; set; } 109 public EntityProperties CurrentEntityProperties { get; set; }
106 public EntityProperties LastEntityProperties { get; set; } 110 public EntityProperties LastEntityProperties { get; set; }
107 111
108 public abstract OMV.Vector3 Scale { get; set; } 112 public virtual OMV.Vector3 Scale { get; set; }
109 public abstract bool IsSolid { get; } 113 public abstract bool IsSolid { get; }
110 public abstract bool IsStatic { get; } 114 public abstract bool IsStatic { get; }
111 115
116 // Materialness
117 public MaterialAttributes.Material Material { get; private set; }
118 public override void SetMaterial(int material)
119 {
120 Material = (MaterialAttributes.Material)material;
121 }
122
112 // Stop all physical motion. 123 // Stop all physical motion.
113 public abstract void ZeroMotion(bool inTaintTime); 124 public abstract void ZeroMotion(bool inTaintTime);
114 public abstract void ZeroAngularMotion(bool inTaintTime); 125 public abstract void ZeroAngularMotion(bool inTaintTime);
@@ -128,6 +139,17 @@ public abstract class BSPhysObject : PhysicsActor
128 public abstract OMV.Quaternion RawOrientation { get; set; } 139 public abstract OMV.Quaternion RawOrientation { get; set; }
129 public abstract OMV.Quaternion ForceOrientation { get; set; } 140 public abstract OMV.Quaternion ForceOrientation { get; set; }
130 141
142 // The system is telling us the velocity it wants to move at.
143 protected OMV.Vector3 m_targetVelocity;
144 public override OMV.Vector3 TargetVelocity
145 {
146 get { return m_targetVelocity; }
147 set
148 {
149 m_targetVelocity = value;
150 Velocity = value;
151 }
152 }
131 public abstract OMV.Vector3 ForceVelocity { get; set; } 153 public abstract OMV.Vector3 ForceVelocity { get; set; }
132 154
133 public abstract OMV.Vector3 ForceRotationalVelocity { get; set; } 155 public abstract OMV.Vector3 ForceRotationalVelocity { get; set; }
@@ -192,7 +214,7 @@ public abstract class BSPhysObject : PhysicsActor
192 { 214 {
193 bool ret = true; 215 bool ret = true;
194 // If the 'no collision' call, force it to happen right now so quick collision_end 216 // If the 'no collision' call, force it to happen right now so quick collision_end
195 bool force = CollisionCollection.Count == 0; 217 bool force = (CollisionCollection.Count == 0);
196 218
197 // throttle the collisions to the number of milliseconds specified in the subscription 219 // throttle the collisions to the number of milliseconds specified in the subscription
198 if (force || (PhysicsScene.SimulationNowTime >= NextCollisionOkTime)) 220 if (force || (PhysicsScene.SimulationNowTime >= NextCollisionOkTime))
@@ -210,8 +232,10 @@ public abstract class BSPhysObject : PhysicsActor
210 // DetailLog("{0},{1}.SendCollisionUpdate,call,numCollisions={2}", LocalID, TypeName, CollisionCollection.Count); 232 // DetailLog("{0},{1}.SendCollisionUpdate,call,numCollisions={2}", LocalID, TypeName, CollisionCollection.Count);
211 base.SendCollisionUpdate(CollisionCollection); 233 base.SendCollisionUpdate(CollisionCollection);
212 234
213 // The collisionCollection structure is passed around in the simulator. 235 // The CollisionCollection instance is passed around in the simulator.
214 // Make sure we don't have a handle to that one and that a new one is used for next time. 236 // Make sure we don't have a handle to that one and that a new one is used for next time.
237 // This fixes an interesting 'gotcha'. If we call CollisionCollection.Clear() here,
238 // a race condition is created for the other users of this instance.
215 CollisionCollection = new CollisionEventUpdate(); 239 CollisionCollection = new CollisionEventUpdate();
216 } 240 }
217 return ret; 241 return ret;
@@ -229,7 +253,8 @@ public abstract class BSPhysObject : PhysicsActor
229 253
230 PhysicsScene.TaintedObject(TypeName+".SubscribeEvents", delegate() 254 PhysicsScene.TaintedObject(TypeName+".SubscribeEvents", delegate()
231 { 255 {
232 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); 256 if (PhysBody.HasPhysicalBody)
257 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
233 }); 258 });
234 } 259 }
235 else 260 else
@@ -243,7 +268,9 @@ public abstract class BSPhysObject : PhysicsActor
243 SubscribedEventsMs = 0; 268 SubscribedEventsMs = 0;
244 PhysicsScene.TaintedObject(TypeName+".UnSubscribeEvents", delegate() 269 PhysicsScene.TaintedObject(TypeName+".UnSubscribeEvents", delegate()
245 { 270 {
246 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); 271 // Make sure there is a body there because sometimes destruction happens in an un-ideal order.
272 if (PhysBody.HasPhysicalBody)
273 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
247 }); 274 });
248 } 275 }
249 // Return 'true' if the simulator wants collision events 276 // Return 'true' if the simulator wants collision events
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
index 2b3fa25..758d92b 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
@@ -45,7 +45,6 @@ public sealed class BSPrim : BSPhysObject
45 private static readonly string LogHeader = "[BULLETS PRIM]"; 45 private static readonly string LogHeader = "[BULLETS PRIM]";
46 46
47 // _size is what the user passed. Scale is what we pass to the physics engine with the mesh. 47 // _size is what the user passed. Scale is what we pass to the physics engine with the mesh.
48 // Often Scale is unity because the meshmerizer will apply _size when creating the mesh.
49 private OMV.Vector3 _size; // the multiplier for each mesh dimension as passed by the user 48 private OMV.Vector3 _size; // the multiplier for each mesh dimension as passed by the user
50 49
51 private bool _grabbed; 50 private bool _grabbed;
@@ -93,7 +92,7 @@ public sealed class BSPrim : BSPhysObject
93 _physicsActorType = (int)ActorTypes.Prim; 92 _physicsActorType = (int)ActorTypes.Prim;
94 _position = pos; 93 _position = pos;
95 _size = size; 94 _size = size;
96 Scale = size; // the scale will be set by CreateGeom depending on object type 95 Scale = size; // prims are the size the user wants them to be (different for BSCharactes).
97 _orientation = rotation; 96 _orientation = rotation;
98 _buoyancy = 1f; 97 _buoyancy = 1f;
99 _velocity = OMV.Vector3.Zero; 98 _velocity = OMV.Vector3.Zero;
@@ -108,8 +107,8 @@ public sealed class BSPrim : BSPhysObject
108 _mass = CalculateMass(); 107 _mass = CalculateMass();
109 108
110 // No body or shape yet 109 // No body or shape yet
111 PhysBody = new BulletBody(LocalID, IntPtr.Zero); 110 PhysBody = new BulletBody(LocalID);
112 PhysShape = new BulletShape(IntPtr.Zero); 111 PhysShape = new BulletShape();
113 112
114 DetailLog("{0},BSPrim.constructor,call", LocalID); 113 DetailLog("{0},BSPrim.constructor,call", LocalID);
115 // do the actual object creation at taint time 114 // do the actual object creation at taint time
@@ -143,7 +142,9 @@ public sealed class BSPrim : BSPhysObject
143 DetailLog("{0},BSPrim.Destroy,taint,", LocalID); 142 DetailLog("{0},BSPrim.Destroy,taint,", LocalID);
144 // If there are physical body and shape, release my use of same. 143 // If there are physical body and shape, release my use of same.
145 PhysicsScene.Shapes.DereferenceBody(PhysBody, true, null); 144 PhysicsScene.Shapes.DereferenceBody(PhysBody, true, null);
145 PhysBody.Clear();
146 PhysicsScene.Shapes.DereferenceShape(PhysShape, true, null); 146 PhysicsScene.Shapes.DereferenceShape(PhysShape, true, null);
147 PhysShape.Clear();
147 }); 148 });
148 } 149 }
149 150
@@ -157,12 +158,10 @@ public sealed class BSPrim : BSPhysObject
157 // We presume the scale and size are the same. If scale must be changed for 158 // We presume the scale and size are the same. If scale must be changed for
158 // the physical shape, that is done when the geometry is built. 159 // the physical shape, that is done when the geometry is built.
159 _size = value; 160 _size = value;
161 Scale = _size;
160 ForceBodyShapeRebuild(false); 162 ForceBodyShapeRebuild(false);
161 } 163 }
162 } 164 }
163 // Scale is what we set in the physics engine. It is different than 'size' in that
164 // 'size' can be encorporated into the mesh. In that case, the scale is <1,1,1>.
165 public override OMV.Vector3 Scale { get; set; }
166 165
167 public override PrimitiveBaseShape Shape { 166 public override PrimitiveBaseShape Shape {
168 set { 167 set {
@@ -189,13 +188,17 @@ public sealed class BSPrim : BSPhysObject
189 } 188 }
190 } 189 }
191 public override bool Selected { 190 public override bool Selected {
192 set { 191 set
193 _isSelected = value; 192 {
194 PhysicsScene.TaintedObject("BSPrim.setSelected", delegate() 193 if (value != _isSelected)
195 { 194 {
196 DetailLog("{0},BSPrim.selected,taint,selected={1}", LocalID, _isSelected); 195 _isSelected = value;
197 SetObjectDynamic(false); 196 PhysicsScene.TaintedObject("BSPrim.setSelected", delegate()
198 }); 197 {
198 DetailLog("{0},BSPrim.selected,taint,selected={1}", LocalID, _isSelected);
199 SetObjectDynamic(false);
200 });
201 }
199 } 202 }
200 } 203 }
201 public override void CrossingFailure() { return; } 204 public override void CrossingFailure() { return; }
@@ -244,7 +247,8 @@ public sealed class BSPrim : BSPhysObject
244 // Zero some other properties in the physics engine 247 // Zero some other properties in the physics engine
245 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate() 248 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate()
246 { 249 {
247 BulletSimAPI.ClearAllForces2(PhysBody.ptr); 250 if (PhysBody.HasPhysicalBody)
251 BulletSimAPI.ClearAllForces2(PhysBody.ptr);
248 }); 252 });
249 } 253 }
250 public override void ZeroAngularMotion(bool inTaintTime) 254 public override void ZeroAngularMotion(bool inTaintTime)
@@ -253,8 +257,12 @@ public sealed class BSPrim : BSPhysObject
253 // Zero some other properties in the physics engine 257 // Zero some other properties in the physics engine
254 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate() 258 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate()
255 { 259 {
256 BulletSimAPI.SetInterpolationAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero); 260 // DetailLog("{0},BSPrim.ZeroAngularMotion,call,rotVel={1}", LocalID, _rotationalVelocity);
257 BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero); 261 if (PhysBody.HasPhysicalBody)
262 {
263 BulletSimAPI.SetInterpolationAngularVelocity2(PhysBody.ptr, _rotationalVelocity);
264 BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, _rotationalVelocity);
265 }
258 }); 266 });
259 } 267 }
260 268
@@ -271,9 +279,12 @@ public sealed class BSPrim : BSPhysObject
271 } 279 }
272 public override OMV.Vector3 Position { 280 public override OMV.Vector3 Position {
273 get { 281 get {
282 /* NOTE: this refetch is not necessary. The simulator knows about linkset children
283 * and does not fetch this position info for children. Thus this is commented out.
274 // child prims move around based on their parent. Need to get the latest location 284 // child prims move around based on their parent. Need to get the latest location
275 if (!Linkset.IsRoot(this)) 285 if (!Linkset.IsRoot(this))
276 _position = Linkset.Position(this); 286 _position = Linkset.PositionGet(this);
287 */
277 288
278 // don't do the GetObjectPosition for root elements because this function is called a zillion times. 289 // don't do the GetObjectPosition for root elements because this function is called a zillion times.
279 // _position = BulletSimAPI.GetObjectPosition2(PhysicsScene.World.ptr, BSBody.ptr); 290 // _position = BulletSimAPI.GetObjectPosition2(PhysicsScene.World.ptr, BSBody.ptr);
@@ -281,18 +292,22 @@ public sealed class BSPrim : BSPhysObject
281 } 292 }
282 set { 293 set {
283 // If the position must be forced into the physics engine, use ForcePosition. 294 // If the position must be forced into the physics engine, use ForcePosition.
295 // All positions are given in world positions.
284 if (_position == value) 296 if (_position == value)
285 { 297 {
298 DetailLog("{0},BSPrim.setPosition,taint,positionNotChanging,pos={1},orient={2}", LocalID, _position, _orientation);
286 return; 299 return;
287 } 300 }
288 _position = value; 301 _position = value;
289 // TODO: what does it mean to set the position of a child prim?? Rebuild the constraint?
290 PositionSanityCheck(false); 302 PositionSanityCheck(false);
303
304 // A linkset might need to know if a component information changed.
305 Linkset.UpdateProperties(this, false);
306
291 PhysicsScene.TaintedObject("BSPrim.setPosition", delegate() 307 PhysicsScene.TaintedObject("BSPrim.setPosition", delegate()
292 { 308 {
293 // DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation); 309 DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation);
294 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); 310 ForcePosition = _position;
295 ActivateIfPhysical(false);
296 }); 311 });
297 } 312 }
298 } 313 }
@@ -303,9 +318,11 @@ public sealed class BSPrim : BSPhysObject
303 } 318 }
304 set { 319 set {
305 _position = value; 320 _position = value;
306 // PositionSanityCheck(); // Don't do this! Causes a loop and caller should know better. 321 if (PhysBody.HasPhysicalBody)
307 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); 322 {
308 ActivateIfPhysical(false); 323 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
324 ActivateIfPhysical(false);
325 }
309 } 326 }
310 } 327 }
311 328
@@ -316,39 +333,46 @@ public sealed class BSPrim : BSPhysObject
316 { 333 {
317 bool ret = false; 334 bool ret = false;
318 335
336 if (!PhysicsScene.TerrainManager.IsWithinKnownTerrain(_position))
337 {
338 // The physical object is out of the known/simulated area.
339 // Upper levels of code will handle the transition to other areas so, for
340 // the time, we just ignore the position.
341 return ret;
342 }
343
319 float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position); 344 float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position);
320 OMV.Vector3 upForce = OMV.Vector3.Zero; 345 OMV.Vector3 upForce = OMV.Vector3.Zero;
321 if (Position.Z < terrainHeight) 346 if (RawPosition.Z < terrainHeight)
322 { 347 {
323 DetailLog("{0},BSPrim.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight); 348 DetailLog("{0},BSPrim.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight);
324 float targetHeight = terrainHeight + (Size.Z / 2f); 349 float targetHeight = terrainHeight + (Size.Z / 2f);
325 // Upforce proportional to the distance away from the terrain. Correct the error in 1 sec. 350 // Upforce proportional to the distance away from the terrain. Correct the error in 1 sec.
326 upForce.Z = (terrainHeight - Position.Z) * 1f; 351 upForce.Z = (terrainHeight - RawPosition.Z) * 1f;
327 ret = true; 352 ret = true;
328 } 353 }
329 354
330 if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0) 355 if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0)
331 { 356 {
332 float waterHeight = PhysicsScene.GetWaterLevelAtXYZ(_position); 357 float waterHeight = PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(_position);
333 // TODO: a floating motor so object will bob in the water 358 // TODO: a floating motor so object will bob in the water
334 if (Math.Abs(Position.Z - waterHeight) > 0.1f) 359 if (Math.Abs(RawPosition.Z - waterHeight) > 0.1f)
335 { 360 {
336 // Upforce proportional to the distance away from the water. Correct the error in 1 sec. 361 // Upforce proportional to the distance away from the water. Correct the error in 1 sec.
337 upForce.Z = (waterHeight - Position.Z) * 1f; 362 upForce.Z = (waterHeight - RawPosition.Z) * 1f;
338 ret = true; 363 ret = true;
339 } 364 }
340 } 365 }
341 366
342 // TODO: check for out of bounds
343
344 // The above code computes a force to apply to correct any out-of-bounds problems. Apply same. 367 // The above code computes a force to apply to correct any out-of-bounds problems. Apply same.
368 // TODO: This should be intergrated with a geneal physics action mechanism.
369 // TODO: This should be moderated with PID'ness.
345 if (ret) 370 if (ret)
346 { 371 {
347 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.PositionSanityCheck:belowTerrain", delegate() 372 // Apply upforce and overcome gravity.
348 { 373 OMV.Vector3 correctionForce = upForce - PhysicsScene.DefaultGravity;
349 // Apply upforce and overcome gravity. 374 DetailLog("{0},BSPrim.PositionSanityCheck,applyForce,pos={1},upForce={2},correctionForce={3}", LocalID, _position, upForce, correctionForce);
350 ForceVelocity = ForceVelocity + upForce - PhysicsScene.DefaultGravity; 375 AddForce(correctionForce, false, inTaintTime);
351 });
352 } 376 }
353 return ret; 377 return ret;
354 } 378 }
@@ -408,7 +432,8 @@ public sealed class BSPrim : BSPhysObject
408 PhysicsScene.TaintedObject("BSPrim.setForce", delegate() 432 PhysicsScene.TaintedObject("BSPrim.setForce", delegate()
409 { 433 {
410 // DetailLog("{0},BSPrim.setForce,taint,force={1}", LocalID, _force); 434 // DetailLog("{0},BSPrim.setForce,taint,force={1}", LocalID, _force);
411 BulletSimAPI.SetObjectForce2(PhysBody.ptr, _force); 435 if (PhysBody.HasPhysicalBody)
436 BulletSimAPI.SetObjectForce2(PhysBody.ptr, _force);
412 }); 437 });
413 } 438 }
414 } 439 }
@@ -502,7 +527,8 @@ public sealed class BSPrim : BSPhysObject
502 PhysicsScene.TaintedObject("BSPrim.setVelocity", delegate() 527 PhysicsScene.TaintedObject("BSPrim.setVelocity", delegate()
503 { 528 {
504 // DetailLog("{0},BSPrim.SetVelocity,taint,vel={1}", LocalID, _velocity); 529 // DetailLog("{0},BSPrim.SetVelocity,taint,vel={1}", LocalID, _velocity);
505 BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity); 530 if (PhysBody.HasPhysicalBody)
531 BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity);
506 }); 532 });
507 } 533 }
508 } 534 }
@@ -537,23 +563,32 @@ public sealed class BSPrim : BSPhysObject
537 } 563 }
538 public override OMV.Quaternion Orientation { 564 public override OMV.Quaternion Orientation {
539 get { 565 get {
566 /* NOTE: this refetch is not necessary. The simulator knows about linkset children
567 * and does not fetch this position info for children. Thus this is commented out.
540 // Children move around because tied to parent. Get a fresh value. 568 // Children move around because tied to parent. Get a fresh value.
541 if (!Linkset.IsRoot(this)) 569 if (!Linkset.IsRoot(this))
542 { 570 {
543 _orientation = Linkset.Orientation(this); 571 _orientation = Linkset.OrientationGet(this);
544 } 572 }
573 */
545 return _orientation; 574 return _orientation;
546 } 575 }
547 set { 576 set {
548 if (_orientation == value) 577 if (_orientation == value)
549 return; 578 return;
550 _orientation = value; 579 _orientation = value;
551 // TODO: what does it mean if a child in a linkset changes its orientation? Rebuild the constraint? 580
581 // A linkset might need to know if a component information changed.
582 Linkset.UpdateProperties(this, false);
583
552 PhysicsScene.TaintedObject("BSPrim.setOrientation", delegate() 584 PhysicsScene.TaintedObject("BSPrim.setOrientation", delegate()
553 { 585 {
554 // _position = BulletSimAPI.GetObjectPosition2(PhysicsScene.World.ptr, BSBody.ptr); 586 if (PhysBody.HasPhysicalBody)
555 // DetailLog("{0},BSPrim.setOrientation,taint,pos={1},orient={2}", LocalID, _position, _orientation); 587 {
556 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); 588 // _position = BulletSimAPI.GetObjectPosition2(PhysicsScene.World.ptr, BSBody.ptr);
589 // DetailLog("{0},BSPrim.setOrientation,taint,pos={1},orient={2}", LocalID, _position, _orientation);
590 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
591 }
557 }); 592 });
558 } 593 }
559 } 594 }
@@ -644,10 +679,7 @@ public sealed class BSPrim : BSPhysObject
644 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, PhysBody.ptr); 679 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, PhysBody.ptr);
645 680
646 // Collision filter can be set only when the object is in the world 681 // Collision filter can be set only when the object is in the world
647 if (PhysBody.collisionFilter != 0 || PhysBody.collisionMask != 0) 682 PhysBody.ApplyCollisionMask();
648 {
649 BulletSimAPI.SetCollisionFilterMask2(PhysBody.ptr, (uint)PhysBody.collisionFilter, (uint)PhysBody.collisionMask);
650 }
651 683
652 // Recompute any linkset parameters. 684 // Recompute any linkset parameters.
653 // When going from non-physical to physical, this re-enables the constraints that 685 // When going from non-physical to physical, this re-enables the constraints that
@@ -672,8 +704,12 @@ public sealed class BSPrim : BSPhysObject
672 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_STATIC_OBJECT); 704 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
673 // Stop all movement 705 // Stop all movement
674 ZeroMotion(true); 706 ZeroMotion(true);
675 // Center of mass is at the center of the object 707
676 // DEBUG DEBUG BulletSimAPI.SetCenterOfMassByPosRot2(Linkset.LinksetRoot.PhysBody.ptr, _position, _orientation); 708 // Set various physical properties so other object interact properly
709 MaterialAttributes matAttrib = BSMaterials.GetAttributes(Material, false);
710 BulletSimAPI.SetFriction2(PhysBody.ptr, matAttrib.friction);
711 BulletSimAPI.SetRestitution2(PhysBody.ptr, matAttrib.restitution);
712
677 // Mass is zero which disables a bunch of physics stuff in Bullet 713 // Mass is zero which disables a bunch of physics stuff in Bullet
678 UpdatePhysicalMassProperties(0f); 714 UpdatePhysicalMassProperties(0f);
679 // Set collision detection parameters 715 // Set collision detection parameters
@@ -682,24 +718,27 @@ public sealed class BSPrim : BSPhysObject
682 BulletSimAPI.SetCcdMotionThreshold2(PhysBody.ptr, PhysicsScene.Params.ccdMotionThreshold); 718 BulletSimAPI.SetCcdMotionThreshold2(PhysBody.ptr, PhysicsScene.Params.ccdMotionThreshold);
683 BulletSimAPI.SetCcdSweptSphereRadius2(PhysBody.ptr, PhysicsScene.Params.ccdSweptSphereRadius); 719 BulletSimAPI.SetCcdSweptSphereRadius2(PhysBody.ptr, PhysicsScene.Params.ccdSweptSphereRadius);
684 } 720 }
685 // There can be special things needed for implementing linksets 721
686 Linkset.MakeStatic(this);
687 // The activation state is 'disabled' so Bullet will not try to act on it. 722 // The activation state is 'disabled' so Bullet will not try to act on it.
688 BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.DISABLE_SIMULATION); 723 // BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.DISABLE_SIMULATION);
689 // Start it out sleeping and physical actions could wake it up. 724 // Start it out sleeping and physical actions could wake it up.
690 // BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.ISLAND_SLEEPING); 725 BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.ISLAND_SLEEPING);
726
727 // This collides like a static object
728 PhysBody.collisionType = CollisionType.Static;
691 729
692 PhysBody.collisionFilter = CollisionFilterGroups.StaticObjectFilter; 730 // There can be special things needed for implementing linksets
693 PhysBody.collisionMask = CollisionFilterGroups.StaticObjectMask; 731 Linkset.MakeStatic(this);
694 } 732 }
695 else 733 else
696 { 734 {
697 // Not a Bullet static object 735 // Not a Bullet static object
698 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_STATIC_OBJECT); 736 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
699 737
700 // Set various physical properties so internal dynamic properties will get computed correctly as they are set 738 // Set various physical properties so other object interact properly
701 BulletSimAPI.SetFriction2(PhysBody.ptr, PhysicsScene.Params.defaultFriction); 739 MaterialAttributes matAttrib = BSMaterials.GetAttributes(Material, true);
702 BulletSimAPI.SetRestitution2(PhysBody.ptr, PhysicsScene.Params.defaultRestitution); 740 BulletSimAPI.SetFriction2(PhysBody.ptr, matAttrib.friction);
741 BulletSimAPI.SetRestitution2(PhysBody.ptr, matAttrib.restitution);
703 742
704 // per http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=3382 743 // per http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=3382
705 // Since this can be called multiple times, only zero forces when becoming physical 744 // Since this can be called multiple times, only zero forces when becoming physical
@@ -727,16 +766,15 @@ public sealed class BSPrim : BSPhysObject
727 BulletSimAPI.SetSleepingThresholds2(PhysBody.ptr, PhysicsScene.Params.linearSleepingThreshold, PhysicsScene.Params.angularSleepingThreshold); 766 BulletSimAPI.SetSleepingThresholds2(PhysBody.ptr, PhysicsScene.Params.linearSleepingThreshold, PhysicsScene.Params.angularSleepingThreshold);
728 BulletSimAPI.SetContactProcessingThreshold2(PhysBody.ptr, PhysicsScene.Params.contactProcessingThreshold); 767 BulletSimAPI.SetContactProcessingThreshold2(PhysBody.ptr, PhysicsScene.Params.contactProcessingThreshold);
729 768
730 // There might be special things needed for implementing linksets. 769 // This collides like an object.
731 Linkset.MakeDynamic(this); 770 PhysBody.collisionType = CollisionType.Dynamic;
732 771
733 // Force activation of the object so Bullet will act on it. 772 // Force activation of the object so Bullet will act on it.
734 // Must do the ForceActivationState2() to overcome the DISABLE_SIMULATION from static objects. 773 // Must do the ForceActivationState2() to overcome the DISABLE_SIMULATION from static objects.
735 BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.ACTIVE_TAG); 774 BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.ACTIVE_TAG);
736 // BulletSimAPI.Activate2(BSBody.ptr, true);
737 775
738 PhysBody.collisionFilter = CollisionFilterGroups.ObjectFilter; 776 // There might be special things needed for implementing linksets.
739 PhysBody.collisionMask = CollisionFilterGroups.ObjectMask; 777 Linkset.MakeDynamic(this);
740 } 778 }
741 } 779 }
742 780
@@ -763,8 +801,9 @@ public sealed class BSPrim : BSPhysObject
763 m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for non-solidness. id={1}, type={2}", LogHeader, LocalID, bodyType); 801 m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for non-solidness. id={1}, type={2}", LogHeader, LocalID, bodyType);
764 } 802 }
765 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE); 803 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE);
766 PhysBody.collisionFilter = CollisionFilterGroups.VolumeDetectFilter; 804
767 PhysBody.collisionMask = CollisionFilterGroups.VolumeDetectMask; 805 // Change collision info from a static object to a ghosty collision object
806 PhysBody.collisionType = CollisionType.VolumeDetect;
768 } 807 }
769 } 808 }
770 809
@@ -839,15 +878,6 @@ public sealed class BSPrim : BSPhysObject
839 } 878 }
840 public override OMV.Vector3 RotationalVelocity { 879 public override OMV.Vector3 RotationalVelocity {
841 get { 880 get {
842 /*
843 OMV.Vector3 pv = OMV.Vector3.Zero;
844 // if close to zero, report zero
845 // This is copied from ODE but I'm not sure why it returns zero but doesn't
846 // zero the property in the physics engine.
847 if (_rotationalVelocity.ApproxEquals(pv, 0.2f))
848 return pv;
849 */
850
851 return _rotationalVelocity; 881 return _rotationalVelocity;
852 } 882 }
853 set { 883 set {
@@ -856,7 +886,8 @@ public sealed class BSPrim : BSPhysObject
856 PhysicsScene.TaintedObject("BSPrim.setRotationalVelocity", delegate() 886 PhysicsScene.TaintedObject("BSPrim.setRotationalVelocity", delegate()
857 { 887 {
858 DetailLog("{0},BSPrim.SetRotationalVel,taint,rotvel={1}", LocalID, _rotationalVelocity); 888 DetailLog("{0},BSPrim.SetRotationalVel,taint,rotvel={1}", LocalID, _rotationalVelocity);
859 BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, _rotationalVelocity); 889 if (PhysBody.HasPhysicalBody)
890 BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, _rotationalVelocity);
860 }); 891 });
861 } 892 }
862 } 893 }
@@ -891,8 +922,11 @@ public sealed class BSPrim : BSPhysObject
891 _buoyancy = value; 922 _buoyancy = value;
892 // DetailLog("{0},BSPrim.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy); 923 // DetailLog("{0},BSPrim.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
893 // Buoyancy is faked by changing the gravity applied to the object 924 // Buoyancy is faked by changing the gravity applied to the object
894 float grav = PhysicsScene.Params.gravity * (1f - _buoyancy); 925 if (PhysBody.HasPhysicalBody)
895 BulletSimAPI.SetGravity2(PhysBody.ptr, new OMV.Vector3(0f, 0f, grav)); 926 {
927 float grav = PhysicsScene.Params.gravity * (1f - _buoyancy);
928 BulletSimAPI.SetGravity2(PhysBody.ptr, new OMV.Vector3(0f, 0f, grav));
929 }
896 } 930 }
897 } 931 }
898 932
@@ -960,7 +994,8 @@ public sealed class BSPrim : BSPhysObject
960 } 994 }
961 DetailLog("{0},BSPrim.AddForce,taint,force={1}", LocalID, fSum); 995 DetailLog("{0},BSPrim.AddForce,taint,force={1}", LocalID, fSum);
962 if (fSum != OMV.Vector3.Zero) 996 if (fSum != OMV.Vector3.Zero)
963 BulletSimAPI.ApplyCentralForce2(PhysBody.ptr, fSum); 997 if (PhysBody.HasPhysicalBody)
998 BulletSimAPI.ApplyCentralForce2(PhysBody.ptr, fSum);
964 }); 999 });
965 } 1000 }
966 1001
@@ -971,7 +1006,8 @@ public sealed class BSPrim : BSPhysObject
971 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ApplyForceImpulse", delegate() 1006 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ApplyForceImpulse", delegate()
972 { 1007 {
973 DetailLog("{0},BSPrim.ApplyForceImpulse,taint,tImpulse={1}", LocalID, applyImpulse); 1008 DetailLog("{0},BSPrim.ApplyForceImpulse,taint,tImpulse={1}", LocalID, applyImpulse);
974 BulletSimAPI.ApplyCentralImpulse2(PhysBody.ptr, applyImpulse); 1009 if (PhysBody.HasPhysicalBody)
1010 BulletSimAPI.ApplyCentralImpulse2(PhysBody.ptr, applyImpulse);
975 }); 1011 });
976 } 1012 }
977 1013
@@ -1007,18 +1043,23 @@ public sealed class BSPrim : BSPhysObject
1007 DetailLog("{0},BSPrim.AddAngularForce,taint,aForce={1}", LocalID, fSum); 1043 DetailLog("{0},BSPrim.AddAngularForce,taint,aForce={1}", LocalID, fSum);
1008 if (fSum != OMV.Vector3.Zero) 1044 if (fSum != OMV.Vector3.Zero)
1009 { 1045 {
1010 BulletSimAPI.ApplyTorque2(PhysBody.ptr, fSum); 1046 if (PhysBody.HasPhysicalBody)
1047 BulletSimAPI.ApplyTorque2(PhysBody.ptr, fSum);
1011 _torque = fSum; 1048 _torque = fSum;
1012 } 1049 }
1013 }); 1050 });
1014 } 1051 }
1015 // A torque impulse. 1052 // A torque impulse.
1053 // ApplyTorqueImpulse adds torque directly to the angularVelocity.
1054 // AddAngularForce accumulates the force and applied it to the angular velocity all at once.
1055 // Computed as: angularVelocity += impulse * inertia;
1016 public void ApplyTorqueImpulse(OMV.Vector3 impulse, bool inTaintTime) 1056 public void ApplyTorqueImpulse(OMV.Vector3 impulse, bool inTaintTime)
1017 { 1057 {
1018 OMV.Vector3 applyImpulse = impulse; 1058 OMV.Vector3 applyImpulse = impulse;
1019 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ApplyTorqueImpulse", delegate() 1059 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ApplyTorqueImpulse", delegate()
1020 { 1060 {
1021 BulletSimAPI.ApplyTorqueImpulse2(PhysBody.ptr, applyImpulse); 1061 if (PhysBody.HasPhysicalBody)
1062 BulletSimAPI.ApplyTorqueImpulse2(PhysBody.ptr, applyImpulse);
1022 }); 1063 });
1023 } 1064 }
1024 1065
@@ -1326,7 +1367,7 @@ public sealed class BSPrim : BSPhysObject
1326 // Rebuild the geometry and object. 1367 // Rebuild the geometry and object.
1327 // This is called when the shape changes so we need to recreate the mesh/hull. 1368 // This is called when the shape changes so we need to recreate the mesh/hull.
1328 // Called at taint-time!!! 1369 // Called at taint-time!!!
1329 private void CreateGeomAndObject(bool forceRebuild) 1370 public void CreateGeomAndObject(bool forceRebuild)
1330 { 1371 {
1331 // If this prim is part of a linkset, we must remove and restore the physical 1372 // If this prim is part of a linkset, we must remove and restore the physical
1332 // links if the body is rebuilt. 1373 // links if the body is rebuilt.
@@ -1336,12 +1377,11 @@ public sealed class BSPrim : BSPhysObject
1336 // Create the correct physical representation for this type of object. 1377 // Create the correct physical representation for this type of object.
1337 // Updates PhysBody and PhysShape with the new information. 1378 // Updates PhysBody and PhysShape with the new information.
1338 // Ignore 'forceRebuild'. This routine makes the right choices and changes of necessary. 1379 // Ignore 'forceRebuild'. This routine makes the right choices and changes of necessary.
1339 // Returns 'true' if either the body or the shape was changed.
1340 PhysicsScene.Shapes.GetBodyAndShape(false, PhysicsScene.World, this, null, delegate(BulletBody dBody) 1380 PhysicsScene.Shapes.GetBodyAndShape(false, PhysicsScene.World, this, null, delegate(BulletBody dBody)
1341 { 1381 {
1342 // Called if the current prim body is about to be destroyed. 1382 // Called if the current prim body is about to be destroyed.
1343 // Remove all the physical dependencies on the old body. 1383 // Remove all the physical dependencies on the old body.
1344 // (Maybe someday make the changing of BSShape an event handled by BSLinkset.) 1384 // (Maybe someday make the changing of BSShape an event to be subscribed to by BSLinkset, ...)
1345 needToRestoreLinkset = Linkset.RemoveBodyDependencies(this); 1385 needToRestoreLinkset = Linkset.RemoveBodyDependencies(this);
1346 needToRestoreVehicle = _vehicle.RemoveBodyDependencies(this); 1386 needToRestoreVehicle = _vehicle.RemoveBodyDependencies(this);
1347 }); 1387 });
@@ -1381,54 +1421,16 @@ public sealed class BSPrim : BSPhysObject
1381 1421
1382 public override void UpdateProperties(EntityProperties entprop) 1422 public override void UpdateProperties(EntityProperties entprop)
1383 { 1423 {
1384 /* 1424 // Updates only for individual prims and for the root object of a linkset.
1385 UpdatedProperties changed = 0; 1425 if (Linkset.IsRoot(this))
1386 // assign to the local variables so the normal set action does not happen
1387 // if (_position != entprop.Position)
1388 if (!_position.ApproxEquals(entprop.Position, POSITION_TOLERANCE))
1389 {
1390 _position = entprop.Position;
1391 changed |= UpdatedProperties.Position;
1392 }
1393 // if (_orientation != entprop.Rotation)
1394 if (!_orientation.ApproxEquals(entprop.Rotation, ROTATION_TOLERANCE))
1395 {
1396 _orientation = entprop.Rotation;
1397 changed |= UpdatedProperties.Rotation;
1398 }
1399 // if (_velocity != entprop.Velocity)
1400 if (!_velocity.ApproxEquals(entprop.Velocity, VELOCITY_TOLERANCE))
1401 {
1402 _velocity = entprop.Velocity;
1403 changed |= UpdatedProperties.Velocity;
1404 }
1405 // if (_acceleration != entprop.Acceleration)
1406 if (!_acceleration.ApproxEquals(entprop.Acceleration, ACCELERATION_TOLERANCE))
1407 {
1408 _acceleration = entprop.Acceleration;
1409 changed |= UpdatedProperties.Acceleration;
1410 }
1411 // if (_rotationalVelocity != entprop.RotationalVelocity)
1412 if (!_rotationalVelocity.ApproxEquals(entprop.RotationalVelocity, ROTATIONAL_VELOCITY_TOLERANCE))
1413 {
1414 _rotationalVelocity = entprop.RotationalVelocity;
1415 changed |= UpdatedProperties.RotationalVel;
1416 }
1417 if (changed != 0)
1418 { 1426 {
1419 // Only update the position of single objects and linkset roots 1427 // A temporary kludge to suppress the rotational effects introduced on vehicles by Bullet
1420 if (Linkset.IsRoot(this)) 1428 // TODO: handle physics introduced by Bullet with computed vehicle physics.
1429 if (_vehicle.IsActive)
1421 { 1430 {
1422 base.RequestPhysicsterseUpdate(); 1431 entprop.RotationalVelocity = OMV.Vector3.Zero;
1423 } 1432 }
1424 }
1425 */
1426
1427 // Don't check for damping here -- it's done in BulletSim and SceneObjectPart.
1428 1433
1429 // Updates only for individual prims and for the root object of a linkset.
1430 if (Linkset.IsRoot(this))
1431 {
1432 // Assign directly to the local variables so the normal set action does not happen 1434 // Assign directly to the local variables so the normal set action does not happen
1433 _position = entprop.Position; 1435 _position = entprop.Position;
1434 _orientation = entprop.Rotation; 1436 _orientation = entprop.Rotation;
@@ -1437,21 +1439,19 @@ public sealed class BSPrim : BSPhysObject
1437 _rotationalVelocity = entprop.RotationalVelocity; 1439 _rotationalVelocity = entprop.RotationalVelocity;
1438 1440
1439 // The sanity check can change the velocity and/or position. 1441 // The sanity check can change the velocity and/or position.
1440 if (PositionSanityCheck(true)) 1442 if (IsPhysical && PositionSanityCheck(true))
1441 { 1443 {
1442 entprop.Position = _position; 1444 entprop.Position = _position;
1443 entprop.Velocity = _velocity; 1445 entprop.Velocity = _velocity;
1444 } 1446 }
1445 1447
1446 // remember the current and last set values 1448 OMV.Vector3 direction = OMV.Vector3.UnitX * _orientation; // DEBUG DEBUG DEBUG
1447 LastEntityProperties = CurrentEntityProperties;
1448 CurrentEntityProperties = entprop;
1449
1450 OMV.Vector3 direction = OMV.Vector3.UnitX * _orientation;
1451 DetailLog("{0},BSPrim.UpdateProperties,call,pos={1},orient={2},dir={3},vel={4},rotVel={5}", 1449 DetailLog("{0},BSPrim.UpdateProperties,call,pos={1},orient={2},dir={3},vel={4},rotVel={5}",
1452 LocalID, _position, _orientation, direction, _velocity, _rotationalVelocity); 1450 LocalID, _position, _orientation, direction, _velocity, _rotationalVelocity);
1453 1451
1454 // BulletSimAPI.DumpRigidBody2(PhysicsScene.World.ptr, BSBody.ptr); // DEBUG DEBUG DEBUG 1452 // remember the current and last set values
1453 LastEntityProperties = CurrentEntityProperties;
1454 CurrentEntityProperties = entprop;
1455 1455
1456 base.RequestPhysicsterseUpdate(); 1456 base.RequestPhysicsterseUpdate();
1457 } 1457 }
@@ -1466,7 +1466,7 @@ public sealed class BSPrim : BSPhysObject
1466 */ 1466 */
1467 1467
1468 // The linkset implimentation might want to know about this. 1468 // The linkset implimentation might want to know about this.
1469 Linkset.UpdateProperties(this); 1469 Linkset.UpdateProperties(this, true);
1470 } 1470 }
1471} 1471}
1472} 1472}
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
index 27a78d1..069cb0d 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSScene.cs
@@ -39,23 +39,10 @@ using log4net;
39using OpenMetaverse; 39using OpenMetaverse;
40 40
41// TODOs for BulletSim (for BSScene, BSPrim, BSCharacter and BulletSim) 41// TODOs for BulletSim (for BSScene, BSPrim, BSCharacter and BulletSim)
42// Test sculpties (verified that they don't work)
43// Compute physics FPS reasonably
44// Based on material, set density and friction 42// Based on material, set density and friction
45// Don't use constraints in linksets of non-physical objects. Means having to move children manually.
46// Four states of prim: Physical, regular, phantom and selected. Are we modeling these correctly?
47// In SL one can set both physical and phantom (gravity, does not effect others, makes collisions with ground)
48// At the moment, physical and phantom causes object to drop through the terrain
49// Physical phantom objects and related typing (collision options )
50// Check out llVolumeDetect. Must do something for that.
51// Use collision masks for collision with terrain and phantom objects
52// More efficient memory usage when passing hull information from BSPrim to BulletSim 43// More efficient memory usage when passing hull information from BSPrim to BulletSim
53// Should prim.link() and prim.delink() membership checking happen at taint time?
54// Mesh sharing. Use meshHash to tell if we already have a hull of that shape and only create once.
55// Do attachments need to be handled separately? Need collision events. Do not collide with VolumeDetect 44// Do attachments need to be handled separately? Need collision events. Do not collide with VolumeDetect
56// Implement LockAngularMotion 45// Implement LockAngularMotion
57// Decide if clearing forces is the right thing to do when setting position (BulletSim::SetObjectTranslation)
58// Remove mesh and Hull stuff. Use mesh passed to bullet and use convexdecom from bullet.
59// Add PID movement operations. What does ScenePresence.MoveToTarget do? 46// Add PID movement operations. What does ScenePresence.MoveToTarget do?
60// Check terrain size. 128 or 127? 47// Check terrain size. 128 or 127?
61// Raycast 48// Raycast
@@ -109,6 +96,9 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
109 public long SimulationStep { get { return m_simulationStep; } } 96 public long SimulationStep { get { return m_simulationStep; } }
110 private int m_taintsToProcessPerStep; 97 private int m_taintsToProcessPerStep;
111 98
99 public delegate void PreStepAction(float timeStep);
100 public event PreStepAction BeforeStep;
101
112 // A value of the time now so all the collision and update routines do not have to get their own 102 // A value of the time now so all the collision and update routines do not have to get their own
113 // Set to 'now' just before all the prims and actors are called for collisions and updates 103 // Set to 'now' just before all the prims and actors are called for collisions and updates
114 public int SimulationNowTime { get; private set; } 104 public int SimulationNowTime { get; private set; }
@@ -140,7 +130,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
140 public const uint GROUNDPLANE_ID = 1; 130 public const uint GROUNDPLANE_ID = 1;
141 public const uint CHILDTERRAIN_ID = 2; // Terrain allocated based on our mega-prim childre start here 131 public const uint CHILDTERRAIN_ID = 2; // Terrain allocated based on our mega-prim childre start here
142 132
143 private float m_waterLevel; 133 public float SimpleWaterLevel { get; set; }
144 public BSTerrainManager TerrainManager { get; private set; } 134 public BSTerrainManager TerrainManager { get; private set; }
145 135
146 public ConfigurationParameters Params 136 public ConfigurationParameters Params
@@ -195,8 +185,10 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
195 private string m_physicsLoggingDir; 185 private string m_physicsLoggingDir;
196 private string m_physicsLoggingPrefix; 186 private string m_physicsLoggingPrefix;
197 private int m_physicsLoggingFileMinutes; 187 private int m_physicsLoggingFileMinutes;
188 private bool m_physicsLoggingDoFlush;
198 // 'true' of the vehicle code is to log lots of details 189 // 'true' of the vehicle code is to log lots of details
199 public bool VehicleLoggingEnabled { get; private set; } 190 public bool VehicleLoggingEnabled { get; private set; }
191 public bool VehiclePhysicalLoggingEnabled { get; private set; }
200 192
201 #region Construction and Initialization 193 #region Construction and Initialization
202 public BSScene(string identifier) 194 public BSScene(string identifier)
@@ -234,6 +226,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
234 if (m_physicsLoggingEnabled) 226 if (m_physicsLoggingEnabled)
235 { 227 {
236 PhysicsLogging = new Logging.LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes); 228 PhysicsLogging = new Logging.LogWriter(m_physicsLoggingDir, m_physicsLoggingPrefix, m_physicsLoggingFileMinutes);
229 PhysicsLogging.ErrorLogger = m_log; // for DEBUG. Let's the logger output error messages.
237 } 230 }
238 else 231 else
239 { 232 {
@@ -302,12 +295,22 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
302 m_physicsLoggingDir = pConfig.GetString("PhysicsLoggingDir", "."); 295 m_physicsLoggingDir = pConfig.GetString("PhysicsLoggingDir", ".");
303 m_physicsLoggingPrefix = pConfig.GetString("PhysicsLoggingPrefix", "physics-%REGIONNAME%-"); 296 m_physicsLoggingPrefix = pConfig.GetString("PhysicsLoggingPrefix", "physics-%REGIONNAME%-");
304 m_physicsLoggingFileMinutes = pConfig.GetInt("PhysicsLoggingFileMinutes", 5); 297 m_physicsLoggingFileMinutes = pConfig.GetInt("PhysicsLoggingFileMinutes", 5);
298 m_physicsLoggingDoFlush = pConfig.GetBoolean("PhysicsLoggingDoFlush", false);
305 // Very detailed logging for vehicle debugging 299 // Very detailed logging for vehicle debugging
306 VehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false); 300 VehicleLoggingEnabled = pConfig.GetBoolean("VehicleLoggingEnabled", false);
301 VehiclePhysicalLoggingEnabled = pConfig.GetBoolean("VehiclePhysicalLoggingEnabled", false);
307 302
308 // Do any replacements in the parameters 303 // Do any replacements in the parameters
309 m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName); 304 m_physicsLoggingPrefix = m_physicsLoggingPrefix.Replace("%REGIONNAME%", RegionName);
310 } 305 }
306
307 // The material characteristics.
308 BSMaterials.InitializeFromDefaults(Params);
309 if (pConfig != null)
310 {
311 // Let the user add new and interesting material property values.
312 BSMaterials.InitializefromParameters(pConfig);
313 }
311 } 314 }
312 } 315 }
313 316
@@ -345,8 +348,6 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
345 // make sure no stepping happens while we're deleting stuff 348 // make sure no stepping happens while we're deleting stuff
346 m_initialized = false; 349 m_initialized = false;
347 350
348 TerrainManager.ReleaseGroundPlaneAndTerrain();
349
350 foreach (KeyValuePair<uint, BSPhysObject> kvp in PhysObjects) 351 foreach (KeyValuePair<uint, BSPhysObject> kvp in PhysObjects)
351 { 352 {
352 kvp.Value.Destroy(); 353 kvp.Value.Destroy();
@@ -366,6 +367,13 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
366 Shapes = null; 367 Shapes = null;
367 } 368 }
368 369
370 if (TerrainManager != null)
371 {
372 TerrainManager.ReleaseGroundPlaneAndTerrain();
373 TerrainManager.Dispose();
374 TerrainManager = null;
375 }
376
369 // Anything left in the unmanaged code should be cleaned out 377 // Anything left in the unmanaged code should be cleaned out
370 BulletSimAPI.Shutdown2(World.ptr); 378 BulletSimAPI.Shutdown2(World.ptr);
371 379
@@ -490,8 +498,10 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
490 ProcessTaints(); 498 ProcessTaints();
491 499
492 // Some of the prims operate with special vehicle properties 500 // Some of the prims operate with special vehicle properties
493 ProcessVehicles(timeStep); 501 DoPreStepActions(timeStep);
494 ProcessTaints(); // the vehicles might have added taints 502
503 // the prestep actions might have added taints
504 ProcessTaints();
495 505
496 // step the physical world one interval 506 // step the physical world one interval
497 m_simulationStep++; 507 m_simulationStep++;
@@ -499,16 +509,17 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
499 509
500 try 510 try
501 { 511 {
502 if (VehicleLoggingEnabled) DumpVehicles(); // DEBUG 512 if (VehiclePhysicalLoggingEnabled) DumpVehicles(); // DEBUG
503 if (PhysicsLogging.Enabled) beforeTime = Util.EnvironmentTickCount(); 513 if (PhysicsLogging.Enabled) beforeTime = Util.EnvironmentTickCount();
504 514
505 numSubSteps = BulletSimAPI.PhysicsStep2(World.ptr, timeStep, m_maxSubSteps, m_fixedTimeStep, 515 numSubSteps = BulletSimAPI.PhysicsStep2(World.ptr, timeStep, m_maxSubSteps, m_fixedTimeStep,
506 out updatedEntityCount, out updatedEntitiesPtr, out collidersCount, out collidersPtr); 516 out updatedEntityCount, out updatedEntitiesPtr, out collidersCount, out collidersPtr);
507 517
508 if (PhysicsLogging.Enabled) simTime = Util.EnvironmentTickCountSubtract(beforeTime); 518 if (PhysicsLogging.Enabled) simTime = Util.EnvironmentTickCountSubtract(beforeTime);
509 DetailLog("{0},Simulate,call, frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}", 519 DetailLog("{0},Simulate,call, frame={1}, nTaints={2}, simTime={3}, substeps={4}, updates={5}, colliders={6}, objWColl={7}",
510 DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps, updatedEntityCount, collidersCount); 520 DetailLogZero, m_simulationStep, numTaints, simTime, numSubSteps,
511 if (VehicleLoggingEnabled) DumpVehicles(); // DEBUG 521 updatedEntityCount, collidersCount, ObjectsWithCollisions.Count);
522 if (VehiclePhysicalLoggingEnabled) DumpVehicles(); // DEBUG
512 } 523 }
513 catch (Exception e) 524 catch (Exception e)
514 { 525 {
@@ -520,9 +531,9 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
520 collidersCount = 0; 531 collidersCount = 0;
521 } 532 }
522 533
523 // Don't have to use the pointers passed back since we know it is the same pinned memory we passed in 534 // Don't have to use the pointers passed back since we know it is the same pinned memory we passed in.
524 535
525 // Get a value for 'now' so all the collision and update routines don't have to get their own 536 // Get a value for 'now' so all the collision and update routines don't have to get their own.
526 SimulationNowTime = Util.EnvironmentTickCount(); 537 SimulationNowTime = Util.EnvironmentTickCount();
527 538
528 // If there were collisions, process them by sending the event to the prim. 539 // If there were collisions, process them by sending the event to the prim.
@@ -562,12 +573,16 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
562 573
563 // Objects that are done colliding are removed from the ObjectsWithCollisions list. 574 // Objects that are done colliding are removed from the ObjectsWithCollisions list.
564 // Not done above because it is inside an iteration of ObjectWithCollisions. 575 // Not done above because it is inside an iteration of ObjectWithCollisions.
576 // This complex collision processing is required to create an empty collision
577 // event call after all collisions have happened on an object. This enables
578 // the simulator to generate the 'collision end' event.
565 if (ObjectsWithNoMoreCollisions.Count > 0) 579 if (ObjectsWithNoMoreCollisions.Count > 0)
566 { 580 {
567 foreach (BSPhysObject po in ObjectsWithNoMoreCollisions) 581 foreach (BSPhysObject po in ObjectsWithNoMoreCollisions)
568 ObjectsWithCollisions.Remove(po); 582 ObjectsWithCollisions.Remove(po);
569 ObjectsWithNoMoreCollisions.Clear(); 583 ObjectsWithNoMoreCollisions.Clear();
570 } 584 }
585 // Done with collisions.
571 586
572 // If any of the objects had updated properties, tell the object it has been changed by the physics engine 587 // If any of the objects had updated properties, tell the object it has been changed by the physics engine
573 if (updatedEntityCount > 0) 588 if (updatedEntityCount > 0)
@@ -585,15 +600,14 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
585 600
586 ProcessPostStepTaints(); 601 ProcessPostStepTaints();
587 602
588 // This causes the unmanaged code to output ALL the values found in ALL the objects in the world. 603 // The following causes the unmanaged code to output ALL the values found in ALL the objects in the world.
589 // Only enable this in a limited test world with few objects. 604 // Only enable this in a limited test world with few objects.
590 // BulletSimAPI.DumpAllInfo2(World.ptr); // DEBUG DEBUG DEBUG 605 // BulletSimAPI.DumpAllInfo2(World.ptr); // DEBUG DEBUG DEBUG
591 606
592 // The physics engine returns the number of milliseconds it simulated this call. 607 // The physics engine returns the number of milliseconds it simulated this call.
593 // These are summed and normalized to one second and divided by 1000 to give the reported physics FPS. 608 // These are summed and normalized to one second and divided by 1000 to give the reported physics FPS.
594 // We multiply by 55 to give a recognizable running rate (55 or less). 609 // Multiply by 55 to give a nominal frame rate of 55.
595 return numSubSteps * m_fixedTimeStep * 1000 * 55; 610 return (float)numSubSteps * m_fixedTimeStep * 1000f * 55f;
596 // return timeStep * 1000 * 55;
597 } 611 }
598 612
599 // Something has collided 613 // Something has collided
@@ -639,12 +653,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
639 653
640 public override void SetWaterLevel(float baseheight) 654 public override void SetWaterLevel(float baseheight)
641 { 655 {
642 m_waterLevel = baseheight; 656 SimpleWaterLevel = baseheight;
643 }
644 // Someday....
645 public float GetWaterLevelAtXYZ(Vector3 loc)
646 {
647 return m_waterLevel;
648 } 657 }
649 658
650 public override void DeleteTerrain() 659 public override void DeleteTerrain()
@@ -915,6 +924,16 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
915 } 924 }
916 } 925 }
917 926
927 private void DoPreStepActions(float timeStep)
928 {
929 ProcessVehicles(timeStep);
930
931 PreStepAction actions = BeforeStep;
932 if (actions != null)
933 actions(timeStep);
934
935 }
936
918 // Some prims have extra vehicle actions 937 // Some prims have extra vehicle actions
919 // Called at taint time! 938 // Called at taint time!
920 private void ProcessVehicles(float timeStep) 939 private void ProcessVehicles(float timeStep)
@@ -979,6 +998,8 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
979 // Should handle fetching the right type from the ini file and converting it. 998 // Should handle fetching the right type from the ini file and converting it.
980 // -- a delegate for getting the value as a float 999 // -- a delegate for getting the value as a float
981 // -- a delegate for setting the value from a float 1000 // -- a delegate for setting the value from a float
1001 // -- an optional delegate to update the value in the world. Most often used to
1002 // push the new value to an in-world object.
982 // 1003 //
983 // The single letter parameters for the delegates are: 1004 // The single letter parameters for the delegates are:
984 // s = BSScene 1005 // s = BSScene
@@ -1069,7 +1090,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
1069 (s,p,l,v) => { s.PID_P = v; } ), 1090 (s,p,l,v) => { s.PID_P = v; } ),
1070 1091
1071 new ParameterDefn("DefaultFriction", "Friction factor used on new objects", 1092 new ParameterDefn("DefaultFriction", "Friction factor used on new objects",
1072 0.5f, 1093 0.2f,
1073 (s,cf,p,v) => { s.m_params[0].defaultFriction = cf.GetFloat(p, v); }, 1094 (s,cf,p,v) => { s.m_params[0].defaultFriction = cf.GetFloat(p, v); },
1074 (s) => { return s.m_params[0].defaultFriction; }, 1095 (s) => { return s.m_params[0].defaultFriction; },
1075 (s,p,l,v) => { s.m_params[0].defaultFriction = v; } ), 1096 (s,p,l,v) => { s.m_params[0].defaultFriction = v; } ),
@@ -1084,7 +1105,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
1084 (s) => { return s.m_params[0].defaultRestitution; }, 1105 (s) => { return s.m_params[0].defaultRestitution; },
1085 (s,p,l,v) => { s.m_params[0].defaultRestitution = v; } ), 1106 (s,p,l,v) => { s.m_params[0].defaultRestitution = v; } ),
1086 new ParameterDefn("CollisionMargin", "Margin around objects before collisions are calculated (must be zero!)", 1107 new ParameterDefn("CollisionMargin", "Margin around objects before collisions are calculated (must be zero!)",
1087 0f, 1108 0.04f,
1088 (s,cf,p,v) => { s.m_params[0].collisionMargin = cf.GetFloat(p, v); }, 1109 (s,cf,p,v) => { s.m_params[0].collisionMargin = cf.GetFloat(p, v); },
1089 (s) => { return s.m_params[0].collisionMargin; }, 1110 (s) => { return s.m_params[0].collisionMargin; },
1090 (s,p,l,v) => { s.m_params[0].collisionMargin = v; } ), 1111 (s,p,l,v) => { s.m_params[0].collisionMargin = v; } ),
@@ -1151,7 +1172,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
1151 (s) => { return s.m_params[0].terrainImplementation; }, 1172 (s) => { return s.m_params[0].terrainImplementation; },
1152 (s,p,l,v) => { s.m_params[0].terrainImplementation = v; } ), 1173 (s,p,l,v) => { s.m_params[0].terrainImplementation = v; } ),
1153 new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" , 1174 new ParameterDefn("TerrainFriction", "Factor to reduce movement against terrain surface" ,
1154 0.5f, 1175 0.3f,
1155 (s,cf,p,v) => { s.m_params[0].terrainFriction = cf.GetFloat(p, v); }, 1176 (s,cf,p,v) => { s.m_params[0].terrainFriction = cf.GetFloat(p, v); },
1156 (s) => { return s.m_params[0].terrainFriction; }, 1177 (s) => { return s.m_params[0].terrainFriction; },
1157 (s,p,l,v) => { s.m_params[0].terrainFriction = v; /* TODO: set on real terrain */} ), 1178 (s,p,l,v) => { s.m_params[0].terrainFriction = v; /* TODO: set on real terrain */} ),
@@ -1165,13 +1186,19 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
1165 (s,cf,p,v) => { s.m_params[0].terrainRestitution = cf.GetFloat(p, v); }, 1186 (s,cf,p,v) => { s.m_params[0].terrainRestitution = cf.GetFloat(p, v); },
1166 (s) => { return s.m_params[0].terrainRestitution; }, 1187 (s) => { return s.m_params[0].terrainRestitution; },
1167 (s,p,l,v) => { s.m_params[0].terrainRestitution = v; /* TODO: set on real terrain */ } ), 1188 (s,p,l,v) => { s.m_params[0].terrainRestitution = v; /* TODO: set on real terrain */ } ),
1189 new ParameterDefn("TerrainCollisionMargin", "Margin where collision checking starts" ,
1190 0.04f,
1191 (s,cf,p,v) => { s.m_params[0].terrainCollisionMargin = cf.GetFloat(p, v); },
1192 (s) => { return s.m_params[0].terrainCollisionMargin; },
1193 (s,p,l,v) => { s.m_params[0].terrainCollisionMargin = v; /* TODO: set on real terrain */ } ),
1194
1168 new ParameterDefn("AvatarFriction", "Factor to reduce movement against an avatar. Changed on avatar recreation.", 1195 new ParameterDefn("AvatarFriction", "Factor to reduce movement against an avatar. Changed on avatar recreation.",
1169 0.2f, 1196 0.2f,
1170 (s,cf,p,v) => { s.m_params[0].avatarFriction = cf.GetFloat(p, v); }, 1197 (s,cf,p,v) => { s.m_params[0].avatarFriction = cf.GetFloat(p, v); },
1171 (s) => { return s.m_params[0].avatarFriction; }, 1198 (s) => { return s.m_params[0].avatarFriction; },
1172 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarFriction, p, l, v); } ), 1199 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarFriction, p, l, v); } ),
1173 new ParameterDefn("AvatarStandingFriction", "Avatar friction when standing. Changed on avatar recreation.", 1200 new ParameterDefn("AvatarStandingFriction", "Avatar friction when standing. Changed on avatar recreation.",
1174 10f, 1201 10.0f,
1175 (s,cf,p,v) => { s.m_params[0].avatarStandingFriction = cf.GetFloat(p, v); }, 1202 (s,cf,p,v) => { s.m_params[0].avatarStandingFriction = cf.GetFloat(p, v); },
1176 (s) => { return s.m_params[0].avatarStandingFriction; }, 1203 (s) => { return s.m_params[0].avatarStandingFriction; },
1177 (s,p,l,v) => { s.m_params[0].avatarStandingFriction = v; } ), 1204 (s,p,l,v) => { s.m_params[0].avatarStandingFriction = v; } ),
@@ -1206,6 +1233,11 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
1206 (s) => { return s.m_params[0].avatarContactProcessingThreshold; }, 1233 (s) => { return s.m_params[0].avatarContactProcessingThreshold; },
1207 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarContactProcessingThreshold, p, l, v); } ), 1234 (s,p,l,v) => { s.UpdateParameterObject(ref s.m_params[0].avatarContactProcessingThreshold, p, l, v); } ),
1208 1235
1236 new ParameterDefn("VehicleAngularDamping", "Factor to damp vehicle angular movement per second (0.0 - 1.0)",
1237 0.95f,
1238 (s,cf,p,v) => { s.m_params[0].vehicleAngularDamping = cf.GetFloat(p, v); },
1239 (s) => { return s.m_params[0].vehicleAngularDamping; },
1240 (s,p,l,v) => { s.m_params[0].vehicleAngularDamping = v; } ),
1209 1241
1210 new ParameterDefn("MaxPersistantManifoldPoolSize", "Number of manifolds pooled (0 means default of 4096)", 1242 new ParameterDefn("MaxPersistantManifoldPoolSize", "Number of manifolds pooled (0 means default of 4096)",
1211 0f, 1243 0f,
@@ -1487,7 +1519,7 @@ public sealed class BSScene : PhysicsScene, IPhysicsParameters
1487 { 1519 {
1488 PhysicsLogging.Write(msg, args); 1520 PhysicsLogging.Write(msg, args);
1489 // Add the Flush() if debugging crashes. Gets all the messages written out. 1521 // Add the Flush() if debugging crashes. Gets all the messages written out.
1490 // PhysicsLogging.Flush(); 1522 if (m_physicsLoggingDoFlush) PhysicsLogging.Flush();
1491 } 1523 }
1492 // Used to fill in the LocalID when there isn't one. It's the correct number of characters. 1524 // Used to fill in the LocalID when there isn't one. It's the correct number of characters.
1493 public const string DetailLogZero = "0000000000"; 1525 public const string DetailLogZero = "0000000000";
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs
index 892c34b..d6e2fe9 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSShapeCollection.cs
@@ -65,9 +65,16 @@ public sealed class BSShapeCollection : IDisposable
65 private Dictionary<System.UInt64, MeshDesc> Meshes = new Dictionary<System.UInt64, MeshDesc>(); 65 private Dictionary<System.UInt64, MeshDesc> Meshes = new Dictionary<System.UInt64, MeshDesc>();
66 private Dictionary<System.UInt64, HullDesc> Hulls = new Dictionary<System.UInt64, HullDesc>(); 66 private Dictionary<System.UInt64, HullDesc> Hulls = new Dictionary<System.UInt64, HullDesc>();
67 67
68 private bool DDetail = false;
69
68 public BSShapeCollection(BSScene physScene) 70 public BSShapeCollection(BSScene physScene)
69 { 71 {
70 PhysicsScene = physScene; 72 PhysicsScene = physScene;
73 // Set the next to 'true' for very detailed shape update detailed logging (detailed details?)
74 // While detailed debugging is still active, this is better than commenting out all the
75 // DetailLog statements. When debugging slows down, this and the protected logging
76 // statements can be commented/removed.
77 DDetail = true;
71 } 78 }
72 79
73 public void Dispose() 80 public void Dispose()
@@ -119,6 +126,11 @@ public sealed class BSShapeCollection : IDisposable
119 return ret; 126 return ret;
120 } 127 }
121 128
129 public bool GetBodyAndShape(bool forceRebuild, BulletSim sim, BSPhysObject prim)
130 {
131 return GetBodyAndShape(forceRebuild, sim, prim, null, null);
132 }
133
122 // Track another user of a body. 134 // Track another user of a body.
123 // We presume the caller has allocated the body. 135 // We presume the caller has allocated the body.
124 // Bodies only have one user so the body is just put into the world if not already there. 136 // Bodies only have one user so the body is just put into the world if not already there.
@@ -126,13 +138,13 @@ public sealed class BSShapeCollection : IDisposable
126 { 138 {
127 lock (m_collectionActivityLock) 139 lock (m_collectionActivityLock)
128 { 140 {
129 DetailLog("{0},BSShapeCollection.ReferenceBody,newBody,body={1}", body.ID, body); 141 if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceBody,newBody,body={1}", body.ID, body);
130 PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.ReferenceBody", delegate() 142 PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.ReferenceBody", delegate()
131 { 143 {
132 if (!BulletSimAPI.IsInWorld2(body.ptr)) 144 if (!BulletSimAPI.IsInWorld2(body.ptr))
133 { 145 {
134 BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, body.ptr); 146 BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, body.ptr);
135 DetailLog("{0},BSShapeCollection.ReferenceBody,addedToWorld,ref={1}", body.ID, body); 147 if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceBody,addedToWorld,ref={1}", body.ID, body);
136 } 148 }
137 }); 149 });
138 } 150 }
@@ -142,14 +154,14 @@ public sealed class BSShapeCollection : IDisposable
142 // Called when releasing use of a BSBody. BSShape is handled separately. 154 // Called when releasing use of a BSBody. BSShape is handled separately.
143 public void DereferenceBody(BulletBody body, bool inTaintTime, BodyDestructionCallback bodyCallback ) 155 public void DereferenceBody(BulletBody body, bool inTaintTime, BodyDestructionCallback bodyCallback )
144 { 156 {
145 if (body.ptr == IntPtr.Zero) 157 if (!body.HasPhysicalBody)
146 return; 158 return;
147 159
148 lock (m_collectionActivityLock) 160 lock (m_collectionActivityLock)
149 { 161 {
150 PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.DereferenceBody", delegate() 162 PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.DereferenceBody", delegate()
151 { 163 {
152 DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody,body={1},inTaintTime={2}", 164 if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceBody,DestroyingBody,body={1},inTaintTime={2}",
153 body.ID, body, inTaintTime); 165 body.ID, body, inTaintTime);
154 // If the caller needs to know the old body is going away, pass the event up. 166 // If the caller needs to know the old body is going away, pass the event up.
155 if (bodyCallback != null) bodyCallback(body); 167 if (bodyCallback != null) bodyCallback(body);
@@ -157,7 +169,7 @@ public sealed class BSShapeCollection : IDisposable
157 if (BulletSimAPI.IsInWorld2(body.ptr)) 169 if (BulletSimAPI.IsInWorld2(body.ptr))
158 { 170 {
159 BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, body.ptr); 171 BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, body.ptr);
160 DetailLog("{0},BSShapeCollection.DereferenceBody,removingFromWorld. Body={1}", body.ID, body); 172 if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceBody,removingFromWorld. Body={1}", body.ID, body);
161 } 173 }
162 174
163 // Zero any reference to the shape so it is not freed when the body is deleted. 175 // Zero any reference to the shape so it is not freed when the body is deleted.
@@ -184,7 +196,7 @@ public sealed class BSShapeCollection : IDisposable
184 { 196 {
185 // There is an existing instance of this mesh. 197 // There is an existing instance of this mesh.
186 meshDesc.referenceCount++; 198 meshDesc.referenceCount++;
187 DetailLog("{0},BSShapeCollection.ReferenceShape,existingMesh,key={1},cnt={2}", 199 if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,existingMesh,key={1},cnt={2}",
188 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount); 200 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount);
189 } 201 }
190 else 202 else
@@ -194,7 +206,7 @@ public sealed class BSShapeCollection : IDisposable
194 meshDesc.shapeKey = shape.shapeKey; 206 meshDesc.shapeKey = shape.shapeKey;
195 // We keep a reference to the underlying IMesh data so a hull can be built 207 // We keep a reference to the underlying IMesh data so a hull can be built
196 meshDesc.referenceCount = 1; 208 meshDesc.referenceCount = 1;
197 DetailLog("{0},BSShapeCollection.ReferenceShape,newMesh,key={1},cnt={2}", 209 if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,newMesh,key={1},cnt={2}",
198 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount); 210 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), meshDesc.referenceCount);
199 ret = true; 211 ret = true;
200 } 212 }
@@ -207,7 +219,7 @@ public sealed class BSShapeCollection : IDisposable
207 { 219 {
208 // There is an existing instance of this hull. 220 // There is an existing instance of this hull.
209 hullDesc.referenceCount++; 221 hullDesc.referenceCount++;
210 DetailLog("{0},BSShapeCollection.ReferenceShape,existingHull,key={1},cnt={2}", 222 if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,existingHull,key={1},cnt={2}",
211 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount); 223 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount);
212 } 224 }
213 else 225 else
@@ -216,7 +228,7 @@ public sealed class BSShapeCollection : IDisposable
216 hullDesc.ptr = shape.ptr; 228 hullDesc.ptr = shape.ptr;
217 hullDesc.shapeKey = shape.shapeKey; 229 hullDesc.shapeKey = shape.shapeKey;
218 hullDesc.referenceCount = 1; 230 hullDesc.referenceCount = 1;
219 DetailLog("{0},BSShapeCollection.ReferenceShape,newHull,key={1},cnt={2}", 231 if (DDetail) DetailLog("{0},BSShapeCollection.ReferenceShape,newHull,key={1},cnt={2}",
220 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount); 232 BSScene.DetailLogZero, shape.shapeKey.ToString("X"), hullDesc.referenceCount);
221 ret = true; 233 ret = true;
222 234
@@ -236,17 +248,17 @@ public sealed class BSShapeCollection : IDisposable
236 // Release the usage of a shape. 248 // Release the usage of a shape.
237 public void DereferenceShape(BulletShape shape, bool inTaintTime, ShapeDestructionCallback shapeCallback) 249 public void DereferenceShape(BulletShape shape, bool inTaintTime, ShapeDestructionCallback shapeCallback)
238 { 250 {
239 if (shape.ptr == IntPtr.Zero) 251 if (!shape.HasPhysicalShape)
240 return; 252 return;
241 253
242 PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.DereferenceShape", delegate() 254 PhysicsScene.TaintedObject(inTaintTime, "BSShapeCollection.DereferenceShape", delegate()
243 { 255 {
244 if (shape.ptr != IntPtr.Zero) 256 if (shape.HasPhysicalShape)
245 { 257 {
246 if (shape.isNativeShape) 258 if (shape.isNativeShape)
247 { 259 {
248 // Native shapes are not tracked and are released immediately 260 // Native shapes are not tracked and are released immediately
249 DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,ptr={1},taintTime={2}", 261 if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceShape,deleteNativeShape,ptr={1},taintTime={2}",
250 BSScene.DetailLogZero, shape.ptr.ToString("X"), inTaintTime); 262 BSScene.DetailLogZero, shape.ptr.ToString("X"), inTaintTime);
251 if (shapeCallback != null) shapeCallback(shape); 263 if (shapeCallback != null) shapeCallback(shape);
252 BulletSimAPI.DeleteCollisionShape2(PhysicsScene.World.ptr, shape.ptr); 264 BulletSimAPI.DeleteCollisionShape2(PhysicsScene.World.ptr, shape.ptr);
@@ -286,7 +298,7 @@ public sealed class BSShapeCollection : IDisposable
286 if (shapeCallback != null) shapeCallback(shape); 298 if (shapeCallback != null) shapeCallback(shape);
287 meshDesc.lastReferenced = System.DateTime.Now; 299 meshDesc.lastReferenced = System.DateTime.Now;
288 Meshes[shape.shapeKey] = meshDesc; 300 Meshes[shape.shapeKey] = meshDesc;
289 DetailLog("{0},BSShapeCollection.DereferenceMesh,shape={1},refCnt={2}", 301 if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceMesh,shape={1},refCnt={2}",
290 BSScene.DetailLogZero, shape, meshDesc.referenceCount); 302 BSScene.DetailLogZero, shape, meshDesc.referenceCount);
291 303
292 } 304 }
@@ -307,7 +319,7 @@ public sealed class BSShapeCollection : IDisposable
307 319
308 hullDesc.lastReferenced = System.DateTime.Now; 320 hullDesc.lastReferenced = System.DateTime.Now;
309 Hulls[shape.shapeKey] = hullDesc; 321 Hulls[shape.shapeKey] = hullDesc;
310 DetailLog("{0},BSShapeCollection.DereferenceHull,shape={1},refCnt={2}", 322 if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceHull,shape={1},refCnt={2}",
311 BSScene.DetailLogZero, shape, hullDesc.referenceCount); 323 BSScene.DetailLogZero, shape, hullDesc.referenceCount);
312 } 324 }
313 } 325 }
@@ -325,13 +337,13 @@ public sealed class BSShapeCollection : IDisposable
325 // Failed the sanity check!! 337 // Failed the sanity check!!
326 PhysicsScene.Logger.ErrorFormat("{0} Attempt to free a compound shape that is not compound!! type={1}, ptr={2}", 338 PhysicsScene.Logger.ErrorFormat("{0} Attempt to free a compound shape that is not compound!! type={1}, ptr={2}",
327 LogHeader, shape.type, shape.ptr.ToString("X")); 339 LogHeader, shape.type, shape.ptr.ToString("X"));
328 DetailLog("{0},BSShapeCollection.DereferenceCompound,notACompoundShape,type={1},ptr={2}", 340 if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceCompound,notACompoundShape,type={1},ptr={2}",
329 BSScene.DetailLogZero, shape.type, shape.ptr.ToString("X")); 341 BSScene.DetailLogZero, shape.type, shape.ptr.ToString("X"));
330 return; 342 return;
331 } 343 }
332 344
333 int numChildren = BulletSimAPI.GetNumberOfCompoundChildren2(shape.ptr); 345 int numChildren = BulletSimAPI.GetNumberOfCompoundChildren2(shape.ptr);
334 DetailLog("{0},BSShapeCollection.DereferenceCompound,shape={1},children={2}", BSScene.DetailLogZero, shape, numChildren); 346 if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceCompound,shape={1},children={2}", BSScene.DetailLogZero, shape, numChildren);
335 347
336 for (int ii = numChildren - 1; ii >= 0; ii--) 348 for (int ii = numChildren - 1; ii >= 0; ii--)
337 { 349 {
@@ -379,7 +391,7 @@ public sealed class BSShapeCollection : IDisposable
379 } 391 }
380 } 392 }
381 393
382 DetailLog("{0},BSShapeCollection.DereferenceAnonCollisionShape,shape={1}", BSScene.DetailLogZero, shapeInfo); 394 if (DDetail) DetailLog("{0},BSShapeCollection.DereferenceAnonCollisionShape,shape={1}", BSScene.DetailLogZero, shapeInfo);
383 395
384 if (shapeInfo.type != BSPhysicsShapeType.SHAPE_UNKNOWN) 396 if (shapeInfo.type != BSPhysicsShapeType.SHAPE_UNKNOWN)
385 { 397 {
@@ -408,19 +420,19 @@ public sealed class BSShapeCollection : IDisposable
408 if (!haveShape && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_CAPSULE) 420 if (!haveShape && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_CAPSULE)
409 { 421 {
410 // an avatar capsule is close to a native shape (it is not shared) 422 // an avatar capsule is close to a native shape (it is not shared)
411 ret = GetReferenceToNativeShape(prim, BSPhysicsShapeType.SHAPE_CAPSULE, 423 GetReferenceToNativeShape(prim, BSPhysicsShapeType.SHAPE_CAPSULE,
412 FixedShapeKey.KEY_CAPSULE, shapeCallback); 424 FixedShapeKey.KEY_CAPSULE, shapeCallback);
413 DetailLog("{0},BSShapeCollection.CreateGeom,avatarCapsule,shape={1}", prim.LocalID, prim.PhysShape); 425 if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,avatarCapsule,shape={1}", prim.LocalID, prim.PhysShape);
414 ret = true; 426 ret = true;
415 haveShape = true; 427 haveShape = true;
416 } 428 }
417 429
418 // Compound shapes are handled special as they are rebuilt from scratch. 430 // Compound shapes are handled special as they are rebuilt from scratch.
419 // This isn't too great a hardship since most of the child shapes will already been created. 431 // This isn't too great a hardship since most of the child shapes will have already been created.
420 if (!haveShape && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_COMPOUND) 432 if (!haveShape && prim.PreferredPhysicalShape == BSPhysicsShapeType.SHAPE_COMPOUND)
421 { 433 {
422 ret = GetReferenceToCompoundShape(prim, shapeCallback); 434 ret = GetReferenceToCompoundShape(prim, shapeCallback);
423 DetailLog("{0},BSShapeCollection.CreateGeom,compoundShape,shape={1}", prim.LocalID, prim.PhysShape); 435 if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,compoundShape,shape={1}", prim.LocalID, prim.PhysShape);
424 haveShape = true; 436 haveShape = true;
425 } 437 }
426 438
@@ -433,7 +445,7 @@ public sealed class BSShapeCollection : IDisposable
433 } 445 }
434 446
435 // Create a mesh/hull shape or a native shape if 'nativeShapePossible' is 'true'. 447 // Create a mesh/hull shape or a native shape if 'nativeShapePossible' is 'true'.
436 private bool CreateGeomNonSpecial(bool forceRebuild, BSPhysObject prim, ShapeDestructionCallback shapeCallback) 448 public bool CreateGeomNonSpecial(bool forceRebuild, BSPhysObject prim, ShapeDestructionCallback shapeCallback)
437 { 449 {
438 bool ret = false; 450 bool ret = false;
439 bool haveShape = false; 451 bool haveShape = false;
@@ -453,19 +465,27 @@ public sealed class BSShapeCollection : IDisposable
453 && pbs.PathScaleX == 100 && pbs.PathScaleY == 100 465 && pbs.PathScaleX == 100 && pbs.PathScaleY == 100
454 && pbs.PathShearX == 0 && pbs.PathShearY == 0) ) ) 466 && pbs.PathShearX == 0 && pbs.PathShearY == 0) ) )
455 { 467 {
468 // Get the scale of any existing shape so we can see if the new shape is same native type and same size.
469 OMV.Vector3 scaleOfExistingShape = OMV.Vector3.Zero;
470 if (prim.PhysShape.HasPhysicalShape)
471 scaleOfExistingShape = BulletSimAPI.GetLocalScaling2(prim.PhysShape.ptr);
472
473 if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,maybeNative,force={1},primScale={2},primSize={3},primShape={4}",
474 prim.LocalID, forceRebuild, prim.Scale, prim.Size, prim.PhysShape.type);
475
456 // It doesn't look like Bullet scales spheres so make sure the scales are all equal 476 // It doesn't look like Bullet scales spheres so make sure the scales are all equal
457 if ((pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1) 477 if ((pbs.ProfileShape == ProfileShape.HalfCircle && pbs.PathCurve == (byte)Extrusion.Curve1)
458 && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z) 478 && pbs.Scale.X == pbs.Scale.Y && pbs.Scale.Y == pbs.Scale.Z)
459 { 479 {
460 haveShape = true; 480 haveShape = true;
461 if (forceRebuild 481 if (forceRebuild
462 || prim.Scale != prim.Size 482 || prim.Scale != scaleOfExistingShape
463 || prim.PhysShape.type != BSPhysicsShapeType.SHAPE_SPHERE 483 || prim.PhysShape.type != BSPhysicsShapeType.SHAPE_SPHERE
464 ) 484 )
465 { 485 {
466 ret = GetReferenceToNativeShape(prim, BSPhysicsShapeType.SHAPE_SPHERE, 486 ret = GetReferenceToNativeShape(prim, BSPhysicsShapeType.SHAPE_SPHERE,
467 FixedShapeKey.KEY_SPHERE, shapeCallback); 487 FixedShapeKey.KEY_SPHERE, shapeCallback);
468 DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},shape={2}", 488 if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,sphere,force={1},shape={2}",
469 prim.LocalID, forceRebuild, prim.PhysShape); 489 prim.LocalID, forceRebuild, prim.PhysShape);
470 } 490 }
471 } 491 }
@@ -473,13 +493,13 @@ public sealed class BSShapeCollection : IDisposable
473 { 493 {
474 haveShape = true; 494 haveShape = true;
475 if (forceRebuild 495 if (forceRebuild
476 || prim.Scale != prim.Size 496 || prim.Scale != scaleOfExistingShape
477 || prim.PhysShape.type != BSPhysicsShapeType.SHAPE_BOX 497 || prim.PhysShape.type != BSPhysicsShapeType.SHAPE_BOX
478 ) 498 )
479 { 499 {
480 ret = GetReferenceToNativeShape( prim, BSPhysicsShapeType.SHAPE_BOX, 500 ret = GetReferenceToNativeShape( prim, BSPhysicsShapeType.SHAPE_BOX,
481 FixedShapeKey.KEY_BOX, shapeCallback); 501 FixedShapeKey.KEY_BOX, shapeCallback);
482 DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},shape={2}", 502 if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,box,force={1},shape={2}",
483 prim.LocalID, forceRebuild, prim.PhysShape); 503 prim.LocalID, forceRebuild, prim.PhysShape);
484 } 504 }
485 } 505 }
@@ -504,13 +524,13 @@ public sealed class BSShapeCollection : IDisposable
504 { 524 {
505 // Update prim.BSShape to reference a hull of this shape. 525 // Update prim.BSShape to reference a hull of this shape.
506 ret = GetReferenceToHull(prim,shapeCallback); 526 ret = GetReferenceToHull(prim,shapeCallback);
507 DetailLog("{0},BSShapeCollection.CreateGeom,hull,shape={1},key={2}", 527 if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,hull,shape={1},key={2}",
508 prim.LocalID, prim.PhysShape, prim.PhysShape.shapeKey.ToString("X")); 528 prim.LocalID, prim.PhysShape, prim.PhysShape.shapeKey.ToString("X"));
509 } 529 }
510 else 530 else
511 { 531 {
512 ret = GetReferenceToMesh(prim, shapeCallback); 532 ret = GetReferenceToMesh(prim, shapeCallback);
513 DetailLog("{0},BSShapeCollection.CreateGeom,mesh,shape={1},key={2}", 533 if (DDetail) DetailLog("{0},BSShapeCollection.CreateGeom,mesh,shape={1},key={2}",
514 prim.LocalID, prim.PhysShape, prim.PhysShape.shapeKey.ToString("X")); 534 prim.LocalID, prim.PhysShape, prim.PhysShape.shapeKey.ToString("X"));
515 } 535 }
516 return ret; 536 return ret;
@@ -528,9 +548,10 @@ public sealed class BSShapeCollection : IDisposable
528 BulletShape newShape = BuildPhysicalNativeShape(prim, shapeType, shapeKey); 548 BulletShape newShape = BuildPhysicalNativeShape(prim, shapeType, shapeKey);
529 549
530 // Don't need to do a 'ReferenceShape()' here because native shapes are not shared. 550 // Don't need to do a 'ReferenceShape()' here because native shapes are not shared.
531 DetailLog("{0},BSShapeCollection.AddNativeShapeToPrim,create,newshape={1},scale={2}", 551 if (DDetail) DetailLog("{0},BSShapeCollection.AddNativeShapeToPrim,create,newshape={1},scale={2}",
532 prim.LocalID, newShape, prim.Scale); 552 prim.LocalID, newShape, prim.Scale);
533 553
554 // native shapes are scaled by Bullet
534 prim.PhysShape = newShape; 555 prim.PhysShape = newShape;
535 return true; 556 return true;
536 } 557 }
@@ -554,16 +575,14 @@ public sealed class BSShapeCollection : IDisposable
554 newShape = new BulletShape( 575 newShape = new BulletShape(
555 BulletSimAPI.BuildCapsuleShape2(PhysicsScene.World.ptr, 1f, 1f, prim.Scale) 576 BulletSimAPI.BuildCapsuleShape2(PhysicsScene.World.ptr, 1f, 1f, prim.Scale)
556 , shapeType); 577 , shapeType);
557 DetailLog("{0},BSShapeCollection.BuiletPhysicalNativeShape,capsule,scale={1}", prim.LocalID, prim.Scale); 578 if (DDetail) DetailLog("{0},BSShapeCollection.BuiletPhysicalNativeShape,capsule,scale={1}", prim.LocalID, prim.Scale);
558 } 579 }
559 else 580 else
560 { 581 {
561 // Native shapes are scaled in Bullet so set the scaling to the size 582 // Native shapes are scaled in Bullet so set the scaling to the size
562 prim.Scale = prim.Size;
563 nativeShapeData.Scale = prim.Scale;
564 newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, nativeShapeData), shapeType); 583 newShape = new BulletShape(BulletSimAPI.BuildNativeShape2(PhysicsScene.World.ptr, nativeShapeData), shapeType);
565 } 584 }
566 if (newShape.ptr == IntPtr.Zero) 585 if (!newShape.HasPhysicalShape)
567 { 586 {
568 PhysicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}", 587 PhysicsScene.Logger.ErrorFormat("{0} BuildPhysicalNativeShape failed. ID={1}, shape={2}",
569 LogHeader, prim.LocalID, shapeType); 588 LogHeader, prim.LocalID, shapeType);
@@ -580,7 +599,7 @@ public sealed class BSShapeCollection : IDisposable
580 // Called at taint-time! 599 // Called at taint-time!
581 private bool GetReferenceToMesh(BSPhysObject prim, ShapeDestructionCallback shapeCallback) 600 private bool GetReferenceToMesh(BSPhysObject prim, ShapeDestructionCallback shapeCallback)
582 { 601 {
583 BulletShape newShape = new BulletShape(IntPtr.Zero); 602 BulletShape newShape = new BulletShape();
584 603
585 float lod; 604 float lod;
586 System.UInt64 newMeshKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod); 605 System.UInt64 newMeshKey = ComputeShapeKey(prim.Size, prim.BaseShape, out lod);
@@ -589,7 +608,7 @@ public sealed class BSShapeCollection : IDisposable
589 if (newMeshKey == prim.PhysShape.shapeKey && prim.PhysShape.type == BSPhysicsShapeType.SHAPE_MESH) 608 if (newMeshKey == prim.PhysShape.shapeKey && prim.PhysShape.type == BSPhysicsShapeType.SHAPE_MESH)
590 return false; 609 return false;
591 610
592 DetailLog("{0},BSShapeCollection.GetReferenceToMesh,create,oldKey={1},newKey={2}", 611 if (DDetail) DetailLog("{0},BSShapeCollection.GetReferenceToMesh,create,oldKey={1},newKey={2}",
593 prim.LocalID, prim.PhysShape.shapeKey.ToString("X"), newMeshKey.ToString("X")); 612 prim.LocalID, prim.PhysShape.shapeKey.ToString("X"), newMeshKey.ToString("X"));
594 613
595 // Since we're recreating new, get rid of the reference to the previous shape 614 // Since we're recreating new, get rid of the reference to the previous shape
@@ -601,8 +620,6 @@ public sealed class BSShapeCollection : IDisposable
601 620
602 ReferenceShape(newShape); 621 ReferenceShape(newShape);
603 622
604 // meshes are already scaled by the meshmerizer
605 prim.Scale = new OMV.Vector3(1f, 1f, 1f);
606 prim.PhysShape = newShape; 623 prim.PhysShape = newShape;
607 624
608 return true; // 'true' means a new shape has been added to this prim 625 return true; // 'true' means a new shape has been added to this prim
@@ -620,8 +637,7 @@ public sealed class BSShapeCollection : IDisposable
620 } 637 }
621 else 638 else
622 { 639 {
623 // Pass false for physicalness as this creates some sort of bounding box which we don't need 640 meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, true, false, false, false);
624 meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, false);
625 641
626 if (meshData != null) 642 if (meshData != null)
627 { 643 {
@@ -663,7 +679,7 @@ public sealed class BSShapeCollection : IDisposable
663 if (newHullKey == prim.PhysShape.shapeKey && prim.PhysShape.type == BSPhysicsShapeType.SHAPE_HULL) 679 if (newHullKey == prim.PhysShape.shapeKey && prim.PhysShape.type == BSPhysicsShapeType.SHAPE_HULL)
664 return false; 680 return false;
665 681
666 DetailLog("{0},BSShapeCollection.GetReferenceToHull,create,oldKey={1},newKey={2}", 682 if (DDetail) DetailLog("{0},BSShapeCollection.GetReferenceToHull,create,oldKey={1},newKey={2}",
667 prim.LocalID, prim.PhysShape.shapeKey.ToString("X"), newHullKey.ToString("X")); 683 prim.LocalID, prim.PhysShape.shapeKey.ToString("X"), newHullKey.ToString("X"));
668 684
669 // Remove usage of the previous shape. 685 // Remove usage of the previous shape.
@@ -674,8 +690,6 @@ public sealed class BSShapeCollection : IDisposable
674 690
675 ReferenceShape(newShape); 691 ReferenceShape(newShape);
676 692
677 // hulls are already scaled by the meshmerizer
678 prim.Scale = new OMV.Vector3(1f, 1f, 1f);
679 prim.PhysShape = newShape; 693 prim.PhysShape = newShape;
680 return true; // 'true' means a new shape has been added to this prim 694 return true; // 'true' means a new shape has been added to this prim
681 } 695 }
@@ -694,8 +708,8 @@ public sealed class BSShapeCollection : IDisposable
694 else 708 else
695 { 709 {
696 // Build a new hull in the physical world 710 // Build a new hull in the physical world
697 // Pass false for physicalness as this creates some sort of bounding box which we don't need 711 // Pass true for physicalness as this creates some sort of bounding box which we don't need
698 IMesh meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, false); 712 IMesh meshData = PhysicsScene.mesher.CreateMesh(objName, pbs, size, lod, true, false, false, false);
699 if (meshData != null) 713 if (meshData != null)
700 { 714 {
701 715
@@ -784,7 +798,7 @@ public sealed class BSShapeCollection : IDisposable
784 BulletShape newShape = new BulletShape(hullPtr, BSPhysicsShapeType.SHAPE_HULL); 798 BulletShape newShape = new BulletShape(hullPtr, BSPhysicsShapeType.SHAPE_HULL);
785 newShape.shapeKey = newHullKey; 799 newShape.shapeKey = newHullKey;
786 800
787 return newShape; // 'true' means a new shape has been added to this prim 801 return newShape;
788 } 802 }
789 803
790 // Callback from convex hull creater with a newly created hull. 804 // Callback from convex hull creater with a newly created hull.
@@ -809,7 +823,7 @@ public sealed class BSShapeCollection : IDisposable
809 // Create the shape for the root prim and add it to the compound shape. Cannot be a native shape. 823 // Create the shape for the root prim and add it to the compound shape. Cannot be a native shape.
810 CreateGeomMeshOrHull(prim, shapeCallback); 824 CreateGeomMeshOrHull(prim, shapeCallback);
811 BulletSimAPI.AddChildShapeToCompoundShape2(cShape.ptr, prim.PhysShape.ptr, OMV.Vector3.Zero, OMV.Quaternion.Identity); 825 BulletSimAPI.AddChildShapeToCompoundShape2(cShape.ptr, prim.PhysShape.ptr, OMV.Vector3.Zero, OMV.Quaternion.Identity);
812 DetailLog("{0},BSShapeCollection.GetReferenceToCompoundShape,addRootPrim,compShape={1},rootShape={2}", 826 if (DDetail) DetailLog("{0},BSShapeCollection.GetReferenceToCompoundShape,addRootPrim,compShape={1},rootShape={2}",
813 prim.LocalID, cShape, prim.PhysShape); 827 prim.LocalID, cShape, prim.PhysShape);
814 828
815 prim.PhysShape = cShape; 829 prim.PhysShape = cShape;
@@ -851,7 +865,7 @@ public sealed class BSShapeCollection : IDisposable
851 private BulletShape VerifyMeshCreated(BulletShape newShape, BSPhysObject prim) 865 private BulletShape VerifyMeshCreated(BulletShape newShape, BSPhysObject prim)
852 { 866 {
853 // If the shape was successfully created, nothing more to do 867 // If the shape was successfully created, nothing more to do
854 if (newShape.ptr != IntPtr.Zero) 868 if (newShape.HasPhysicalShape)
855 return newShape; 869 return newShape;
856 870
857 // If this mesh has an underlying asset and we have not failed getting it before, fetch the asset 871 // If this mesh has an underlying asset and we have not failed getting it before, fetch the asset
@@ -910,7 +924,7 @@ public sealed class BSShapeCollection : IDisposable
910 bool ret = false; 924 bool ret = false;
911 925
912 // the mesh, hull or native shape must have already been created in Bullet 926 // the mesh, hull or native shape must have already been created in Bullet
913 bool mustRebuild = (prim.PhysBody.ptr == IntPtr.Zero); 927 bool mustRebuild = !prim.PhysBody.HasPhysicalBody;
914 928
915 // If there is an existing body, verify it's of an acceptable type. 929 // If there is an existing body, verify it's of an acceptable type.
916 // If not a solid object, body is a GhostObject. Otherwise a RigidBody. 930 // If not a solid object, body is a GhostObject. Otherwise a RigidBody.
@@ -936,13 +950,13 @@ public sealed class BSShapeCollection : IDisposable
936 { 950 {
937 bodyPtr = BulletSimAPI.CreateBodyFromShape2(sim.ptr, shape.ptr, 951 bodyPtr = BulletSimAPI.CreateBodyFromShape2(sim.ptr, shape.ptr,
938 prim.LocalID, prim.RawPosition, prim.RawOrientation); 952 prim.LocalID, prim.RawPosition, prim.RawOrientation);
939 DetailLog("{0},BSShapeCollection.CreateBody,mesh,ptr={1}", prim.LocalID, bodyPtr.ToString("X")); 953 if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,mesh,ptr={1}", prim.LocalID, bodyPtr.ToString("X"));
940 } 954 }
941 else 955 else
942 { 956 {
943 bodyPtr = BulletSimAPI.CreateGhostFromShape2(sim.ptr, shape.ptr, 957 bodyPtr = BulletSimAPI.CreateGhostFromShape2(sim.ptr, shape.ptr,
944 prim.LocalID, prim.RawPosition, prim.RawOrientation); 958 prim.LocalID, prim.RawPosition, prim.RawOrientation);
945 DetailLog("{0},BSShapeCollection.CreateBody,ghost,ptr={1}", prim.LocalID, bodyPtr.ToString("X")); 959 if (DDetail) DetailLog("{0},BSShapeCollection.CreateBody,ghost,ptr={1}", prim.LocalID, bodyPtr.ToString("X"));
946 } 960 }
947 aBody = new BulletBody(prim.LocalID, bodyPtr); 961 aBody = new BulletBody(prim.LocalID, bodyPtr);
948 962
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs
index 3ca756c..2b120d6 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainHeightmap.cs
@@ -93,7 +93,7 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys
93 { 93 {
94 m_mapInfo.Ptr = BulletSimAPI.CreateHeightMapInfo2(PhysicsScene.World.ptr, m_mapInfo.ID, 94 m_mapInfo.Ptr = BulletSimAPI.CreateHeightMapInfo2(PhysicsScene.World.ptr, m_mapInfo.ID,
95 m_mapInfo.minCoords, m_mapInfo.maxCoords, 95 m_mapInfo.minCoords, m_mapInfo.maxCoords,
96 m_mapInfo.heightMap, BSTerrainManager.TERRAIN_COLLISION_MARGIN); 96 m_mapInfo.heightMap, PhysicsScene.Params.terrainCollisionMargin);
97 97
98 // Create the terrain shape from the mapInfo 98 // Create the terrain shape from the mapInfo
99 m_mapInfo.terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(m_mapInfo.Ptr), 99 m_mapInfo.terrainShape = new BulletShape(BulletSimAPI.CreateTerrainShape2(m_mapInfo.Ptr),
@@ -121,9 +121,8 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys
121 // redo its bounding box now that it is in the world 121 // redo its bounding box now that it is in the world
122 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr); 122 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr);
123 123
124 BulletSimAPI.SetCollisionFilterMask2(m_mapInfo.terrainBody.ptr, 124 m_mapInfo.terrainBody.collisionType = CollisionType.Terrain;
125 (uint)CollisionFilterGroups.TerrainFilter, 125 m_mapInfo.terrainBody.ApplyCollisionMask();
126 (uint)CollisionFilterGroups.TerrainMask);
127 126
128 // Make it so the terrain will not move or be considered for movement. 127 // Make it so the terrain will not move or be considered for movement.
129 BulletSimAPI.ForceActivationState2(m_mapInfo.terrainBody.ptr, ActivationState.DISABLE_SIMULATION); 128 BulletSimAPI.ForceActivationState2(m_mapInfo.terrainBody.ptr, ActivationState.DISABLE_SIMULATION);
@@ -136,7 +135,7 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys
136 { 135 {
137 if (m_mapInfo != null) 136 if (m_mapInfo != null)
138 { 137 {
139 if (m_mapInfo.terrainBody.ptr != IntPtr.Zero) 138 if (m_mapInfo.terrainBody.HasPhysicalBody)
140 { 139 {
141 BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr); 140 BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_mapInfo.terrainBody.ptr);
142 // Frees both the body and the shape. 141 // Frees both the body and the shape.
@@ -148,7 +147,7 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys
148 } 147 }
149 148
150 // The passed position is relative to the base of the region. 149 // The passed position is relative to the base of the region.
151 public override float GetHeightAtXYZ(Vector3 pos) 150 public override float GetTerrainHeightAtXYZ(Vector3 pos)
152 { 151 {
153 float ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET; 152 float ret = BSTerrainManager.HEIGHT_GETHEIGHT_RET;
154 153
@@ -166,5 +165,11 @@ public sealed class BSTerrainHeightmap : BSTerrainPhys
166 } 165 }
167 return ret; 166 return ret;
168 } 167 }
168
169 // The passed position is relative to the base of the region.
170 public override float GetWaterLevelAtXYZ(Vector3 pos)
171 {
172 return PhysicsScene.SimpleWaterLevel;
173 }
169} 174}
170} 175}
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs
index 23fcfd3..3428b9c 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainManager.cs
@@ -62,11 +62,12 @@ public abstract class BSTerrainPhys : IDisposable
62 ID = id; 62 ID = id;
63 } 63 }
64 public abstract void Dispose(); 64 public abstract void Dispose();
65 public abstract float GetHeightAtXYZ(Vector3 pos); 65 public abstract float GetTerrainHeightAtXYZ(Vector3 pos);
66 public abstract float GetWaterLevelAtXYZ(Vector3 pos);
66} 67}
67 68
68// ========================================================================================== 69// ==========================================================================================
69public sealed class BSTerrainManager 70public sealed class BSTerrainManager : IDisposable
70{ 71{
71 static string LogHeader = "[BULLETSIM TERRAIN MANAGER]"; 72 static string LogHeader = "[BULLETSIM TERRAIN MANAGER]";
72 73
@@ -75,13 +76,12 @@ public sealed class BSTerrainManager
75 public const float HEIGHT_INITIALIZATION = 24.987f; 76 public const float HEIGHT_INITIALIZATION = 24.987f;
76 public const float HEIGHT_INITIAL_LASTHEIGHT = 24.876f; 77 public const float HEIGHT_INITIAL_LASTHEIGHT = 24.876f;
77 public const float HEIGHT_GETHEIGHT_RET = 24.765f; 78 public const float HEIGHT_GETHEIGHT_RET = 24.765f;
79 public const float WATER_HEIGHT_GETHEIGHT_RET = 19.998f;
78 80
79 // If the min and max height are equal, we reduce the min by this 81 // If the min and max height are equal, we reduce the min by this
80 // amount to make sure that a bounding box is built for the terrain. 82 // amount to make sure that a bounding box is built for the terrain.
81 public const float HEIGHT_EQUAL_FUDGE = 0.2f; 83 public const float HEIGHT_EQUAL_FUDGE = 0.2f;
82 84
83 public const float TERRAIN_COLLISION_MARGIN = 0.0f;
84
85 // Until the whole simulator is changed to pass us the region size, we rely on constants. 85 // Until the whole simulator is changed to pass us the region size, we rely on constants.
86 public Vector3 DefaultRegionSize = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight); 86 public Vector3 DefaultRegionSize = new Vector3(Constants.RegionSize, Constants.RegionSize, Constants.RegionHeight);
87 87
@@ -122,6 +122,11 @@ public sealed class BSTerrainManager
122 MegaRegionParentPhysicsScene = null; 122 MegaRegionParentPhysicsScene = null;
123 } 123 }
124 124
125 public void Dispose()
126 {
127 ReleaseGroundPlaneAndTerrain();
128 }
129
125 // Create the initial instance of terrain and the underlying ground plane. 130 // Create the initial instance of terrain and the underlying ground plane.
126 // This is called from the initialization routine so we presume it is 131 // This is called from the initialization routine so we presume it is
127 // safe to call Bullet in real time. We hope no one is moving prims around yet. 132 // safe to call Bullet in real time. We hope no one is moving prims around yet.
@@ -129,7 +134,8 @@ public sealed class BSTerrainManager
129 { 134 {
130 // The ground plane is here to catch things that are trying to drop to negative infinity 135 // The ground plane is here to catch things that are trying to drop to negative infinity
131 BulletShape groundPlaneShape = new BulletShape( 136 BulletShape groundPlaneShape = new BulletShape(
132 BulletSimAPI.CreateGroundPlaneShape2(BSScene.GROUNDPLANE_ID, 1f, TERRAIN_COLLISION_MARGIN), 137 BulletSimAPI.CreateGroundPlaneShape2(BSScene.GROUNDPLANE_ID, 1f,
138 PhysicsScene.Params.terrainCollisionMargin),
133 BSPhysicsShapeType.SHAPE_GROUNDPLANE); 139 BSPhysicsShapeType.SHAPE_GROUNDPLANE);
134 m_groundPlane = new BulletBody(BSScene.GROUNDPLANE_ID, 140 m_groundPlane = new BulletBody(BSScene.GROUNDPLANE_ID,
135 BulletSimAPI.CreateBodyWithDefaultMotionState2(groundPlaneShape.ptr, BSScene.GROUNDPLANE_ID, 141 BulletSimAPI.CreateBodyWithDefaultMotionState2(groundPlaneShape.ptr, BSScene.GROUNDPLANE_ID,
@@ -139,8 +145,8 @@ public sealed class BSTerrainManager
139 // Ground plane does not move 145 // Ground plane does not move
140 BulletSimAPI.ForceActivationState2(m_groundPlane.ptr, ActivationState.DISABLE_SIMULATION); 146 BulletSimAPI.ForceActivationState2(m_groundPlane.ptr, ActivationState.DISABLE_SIMULATION);
141 // Everything collides with the ground plane. 147 // Everything collides with the ground plane.
142 BulletSimAPI.SetCollisionFilterMask2(m_groundPlane.ptr, 148 m_groundPlane.collisionType = CollisionType.Groundplane;
143 (uint)CollisionFilterGroups.GroundPlaneFilter, (uint)CollisionFilterGroups.GroundPlaneMask); 149 m_groundPlane.ApplyCollisionMask();
144 150
145 // Build an initial terrain and put it in the world. This quickly gets replaced by the real region terrain. 151 // Build an initial terrain and put it in the world. This quickly gets replaced by the real region terrain.
146 BSTerrainPhys initialTerrain = new BSTerrainHeightmap(PhysicsScene, Vector3.Zero, BSScene.TERRAIN_ID, DefaultRegionSize); 152 BSTerrainPhys initialTerrain = new BSTerrainHeightmap(PhysicsScene, Vector3.Zero, BSScene.TERRAIN_ID, DefaultRegionSize);
@@ -150,13 +156,13 @@ public sealed class BSTerrainManager
150 // Release all the terrain structures we might have allocated 156 // Release all the terrain structures we might have allocated
151 public void ReleaseGroundPlaneAndTerrain() 157 public void ReleaseGroundPlaneAndTerrain()
152 { 158 {
153 if (m_groundPlane.ptr != IntPtr.Zero) 159 if (m_groundPlane.HasPhysicalBody)
154 { 160 {
155 if (BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_groundPlane.ptr)) 161 if (BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_groundPlane.ptr))
156 { 162 {
157 BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, m_groundPlane.ptr); 163 BulletSimAPI.DestroyObject2(PhysicsScene.World.ptr, m_groundPlane.ptr);
158 } 164 }
159 m_groundPlane.ptr = IntPtr.Zero; 165 m_groundPlane.Clear();
160 } 166 }
161 167
162 ReleaseTerrain(); 168 ReleaseTerrain();
@@ -165,17 +171,22 @@ public sealed class BSTerrainManager
165 // Release all the terrain we have allocated 171 // Release all the terrain we have allocated
166 public void ReleaseTerrain() 172 public void ReleaseTerrain()
167 { 173 {
168 foreach (KeyValuePair<Vector3, BSTerrainPhys> kvp in m_terrains) 174 lock (m_terrains)
169 { 175 {
170 kvp.Value.Dispose(); 176 foreach (KeyValuePair<Vector3, BSTerrainPhys> kvp in m_terrains)
177 {
178 kvp.Value.Dispose();
179 }
180 m_terrains.Clear();
171 } 181 }
172 m_terrains.Clear();
173 } 182 }
174 183
175 // The simulator wants to set a new heightmap for the terrain. 184 // The simulator wants to set a new heightmap for the terrain.
176 public void SetTerrain(float[] heightMap) { 185 public void SetTerrain(float[] heightMap) {
177 float[] localHeightMap = heightMap; 186 float[] localHeightMap = heightMap;
178 PhysicsScene.TaintedObject("TerrainManager.SetTerrain", delegate() 187 // If there are multiple requests for changes to the same terrain between ticks,
188 // only do that last one.
189 PhysicsScene.PostTaintObject("TerrainManager.SetTerrain-"+ m_worldOffset.ToString(), 0, delegate()
179 { 190 {
180 if (m_worldOffset != Vector3.Zero && MegaRegionParentPhysicsScene != null) 191 if (m_worldOffset != Vector3.Zero && MegaRegionParentPhysicsScene != null)
181 { 192 {
@@ -211,6 +222,7 @@ public sealed class BSTerrainManager
211 // terrain shape is created and added to the body. 222 // terrain shape is created and added to the body.
212 // This call is most often used to update the heightMap and parameters of the terrain. 223 // This call is most often used to update the heightMap and parameters of the terrain.
213 // (The above does suggest that some simplification/refactoring is in order.) 224 // (The above does suggest that some simplification/refactoring is in order.)
225 // Called during taint-time.
214 private void UpdateTerrain(uint id, float[] heightMap, 226 private void UpdateTerrain(uint id, float[] heightMap,
215 Vector3 minCoords, Vector3 maxCoords, bool inTaintTime) 227 Vector3 minCoords, Vector3 maxCoords, bool inTaintTime)
216 { 228 {
@@ -220,7 +232,7 @@ public sealed class BSTerrainManager
220 // Find high and low points of passed heightmap. 232 // Find high and low points of passed heightmap.
221 // The min and max passed in is usually the area objects can be in (maximum 233 // The min and max passed in is usually the area objects can be in (maximum
222 // object height, for instance). The terrain wants the bounding box for the 234 // object height, for instance). The terrain wants the bounding box for the
223 // terrain so we replace passed min and max Z with the actual terrain min/max Z. 235 // terrain so replace passed min and max Z with the actual terrain min/max Z.
224 float minZ = float.MaxValue; 236 float minZ = float.MaxValue;
225 float maxZ = float.MinValue; 237 float maxZ = float.MinValue;
226 foreach (float height in heightMap) 238 foreach (float height in heightMap)
@@ -238,15 +250,15 @@ public sealed class BSTerrainManager
238 250
239 Vector3 terrainRegionBase = new Vector3(minCoords.X, minCoords.Y, 0f); 251 Vector3 terrainRegionBase = new Vector3(minCoords.X, minCoords.Y, 0f);
240 252
241 BSTerrainPhys terrainPhys; 253 lock (m_terrains)
242 if (m_terrains.TryGetValue(terrainRegionBase, out terrainPhys))
243 { 254 {
244 // There is already a terrain in this spot. Free the old and build the new. 255 BSTerrainPhys terrainPhys;
245 DetailLog("{0},UpdateTerrain:UpdateExisting,call,id={1},base={2},minC={3},maxC={4}", 256 if (m_terrains.TryGetValue(terrainRegionBase, out terrainPhys))
246 BSScene.DetailLogZero, id, terrainRegionBase, minCoords, minCoords);
247
248 PhysicsScene.TaintedObject(inTaintTime, "BSScene.UpdateTerrain:UpdateExisting", delegate()
249 { 257 {
258 // There is already a terrain in this spot. Free the old and build the new.
259 DetailLog("{0},UpdateTerrain:UpdateExisting,call,id={1},base={2},minC={3},maxC={4}",
260 BSScene.DetailLogZero, id, terrainRegionBase, minCoords, minCoords);
261
250 // Remove old terrain from the collection 262 // Remove old terrain from the collection
251 m_terrains.Remove(terrainRegionBase); 263 m_terrains.Remove(terrainRegionBase);
252 // Release any physical memory it may be using. 264 // Release any physical memory it may be using.
@@ -271,35 +283,24 @@ public sealed class BSTerrainManager
271 // I hate doing this, but just bail 283 // I hate doing this, but just bail
272 return; 284 return;
273 } 285 }
274 }); 286 }
275 } 287 else
276 else 288 {
277 { 289 // We don't know about this terrain so either we are creating a new terrain or
278 // We don't know about this terrain so either we are creating a new terrain or 290 // our mega-prim child is giving us a new terrain to add to the phys world
279 // our mega-prim child is giving us a new terrain to add to the phys world
280
281 // if this is a child terrain, calculate a unique terrain id
282 uint newTerrainID = id;
283 if (newTerrainID >= BSScene.CHILDTERRAIN_ID)
284 newTerrainID = ++m_terrainCount;
285
286 float[] heightMapX = heightMap;
287 Vector3 minCoordsX = minCoords;
288 Vector3 maxCoordsX = maxCoords;
289 291
290 DetailLog("{0},UpdateTerrain:NewTerrain,call,id={1}, minC={2}, maxC={3}", 292 // if this is a child terrain, calculate a unique terrain id
291 BSScene.DetailLogZero, newTerrainID, minCoords, minCoords); 293 uint newTerrainID = id;
294 if (newTerrainID >= BSScene.CHILDTERRAIN_ID)
295 newTerrainID = ++m_terrainCount;
292 296
293 // Code that must happen at taint-time 297 DetailLog("{0},UpdateTerrain:NewTerrain,taint,newID={1},minCoord={2},maxCoord={3}",
294 PhysicsScene.TaintedObject(inTaintTime, "BSScene.UpdateTerrain:NewTerrain", delegate() 298 BSScene.DetailLogZero, newTerrainID, minCoords, minCoords);
295 {
296 DetailLog("{0},UpdateTerrain:NewTerrain,taint,baseX={1},baseY={2}",
297 BSScene.DetailLogZero, minCoordsX.X, minCoordsX.Y);
298 BSTerrainPhys newTerrainPhys = BuildPhysicalTerrain(terrainRegionBase, id, heightMap, minCoords, maxCoords); 299 BSTerrainPhys newTerrainPhys = BuildPhysicalTerrain(terrainRegionBase, id, heightMap, minCoords, maxCoords);
299 m_terrains.Add(terrainRegionBase, newTerrainPhys); 300 m_terrains.Add(terrainRegionBase, newTerrainPhys);
300 301
301 m_terrainModified = true; 302 m_terrainModified = true;
302 }); 303 }
303 } 304 }
304 } 305 }
305 306
@@ -331,6 +332,13 @@ public sealed class BSTerrainManager
331 return newTerrainPhys; 332 return newTerrainPhys;
332 } 333 }
333 334
335 // Return 'true' of this position is somewhere in known physical terrain space
336 public bool IsWithinKnownTerrain(Vector3 pos)
337 {
338 Vector3 terrainBaseXYZ;
339 BSTerrainPhys physTerrain;
340 return GetTerrainPhysicalAtXYZ(pos, out physTerrain, out terrainBaseXYZ);
341 }
334 342
335 // Given an X and Y, find the height of the terrain. 343 // Given an X and Y, find the height of the terrain.
336 // Since we could be handling multiple terrains for a mega-region, 344 // Since we could be handling multiple terrains for a mega-region,
@@ -341,40 +349,74 @@ public sealed class BSTerrainManager
341 private float lastHeightTX = 999999f; 349 private float lastHeightTX = 999999f;
342 private float lastHeightTY = 999999f; 350 private float lastHeightTY = 999999f;
343 private float lastHeight = HEIGHT_INITIAL_LASTHEIGHT; 351 private float lastHeight = HEIGHT_INITIAL_LASTHEIGHT;
344 public float GetTerrainHeightAtXYZ(Vector3 loc) 352 public float GetTerrainHeightAtXYZ(Vector3 pos)
345 { 353 {
346 float tX = loc.X; 354 float tX = pos.X;
347 float tY = loc.Y; 355 float tY = pos.Y;
348 // You'd be surprized at the number of times this routine is called 356 // You'd be surprized at the number of times this routine is called
349 // with the same parameters as last time. 357 // with the same parameters as last time.
350 if (!m_terrainModified && lastHeightTX == tX && lastHeightTY == tY) 358 if (!m_terrainModified && (lastHeightTX == tX) && (lastHeightTY == tY))
351 return lastHeight; 359 return lastHeight;
360 m_terrainModified = false;
352 361
353 lastHeightTX = tX; 362 lastHeightTX = tX;
354 lastHeightTY = tY; 363 lastHeightTY = tY;
355 float ret = HEIGHT_GETHEIGHT_RET; 364 float ret = HEIGHT_GETHEIGHT_RET;
356 365
357 int offsetX = ((int)(tX / (int)DefaultRegionSize.X)) * (int)DefaultRegionSize.X; 366 Vector3 terrainBaseXYZ;
358 int offsetY = ((int)(tY / (int)DefaultRegionSize.Y)) * (int)DefaultRegionSize.Y;
359 Vector3 terrainBaseXYZ = new Vector3(offsetX, offsetY, 0f);
360
361 BSTerrainPhys physTerrain; 367 BSTerrainPhys physTerrain;
362 if (m_terrains.TryGetValue(terrainBaseXYZ, out physTerrain)) 368 if (GetTerrainPhysicalAtXYZ(pos, out physTerrain, out terrainBaseXYZ))
363 { 369 {
364 ret = physTerrain.GetHeightAtXYZ(loc - terrainBaseXYZ); 370 ret = physTerrain.GetTerrainHeightAtXYZ(pos - terrainBaseXYZ);
365 DetailLog("{0},BSTerrainManager.GetTerrainHeightAtXYZ,loc={1},base={2},height={3}",
366 BSScene.DetailLogZero, loc, terrainBaseXYZ, ret);
367 } 371 }
368 else 372 else
369 { 373 {
370 PhysicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: region={1}, x={2}, y={3}", 374 PhysicsScene.Logger.ErrorFormat("{0} GetTerrainHeightAtXY: terrain not found: region={1}, x={2}, y={3}",
371 LogHeader, PhysicsScene.RegionName, tX, tY); 375 LogHeader, PhysicsScene.RegionName, tX, tY);
376 DetailLog("{0},BSTerrainManager.GetTerrainHeightAtXYZ,terrainNotFound,pos={1},base={2}",
377 BSScene.DetailLogZero, pos, terrainBaseXYZ);
372 } 378 }
373 m_terrainModified = false; 379
374 lastHeight = ret; 380 lastHeight = ret;
375 return ret; 381 return ret;
376 } 382 }
377 383
384 public float GetWaterLevelAtXYZ(Vector3 pos)
385 {
386 float ret = WATER_HEIGHT_GETHEIGHT_RET;
387
388 Vector3 terrainBaseXYZ;
389 BSTerrainPhys physTerrain;
390 if (GetTerrainPhysicalAtXYZ(pos, out physTerrain, out terrainBaseXYZ))
391 {
392 ret = physTerrain.GetWaterLevelAtXYZ(pos);
393 }
394 else
395 {
396 PhysicsScene.Logger.ErrorFormat("{0} GetWaterHeightAtXY: terrain not found: pos={1}, terrainBase={2}, height={3}",
397 LogHeader, PhysicsScene.RegionName, pos, terrainBaseXYZ, ret);
398 }
399 return ret;
400 }
401
402 // Given an address, return 'true' of there is a description of that terrain and output
403 // the descriptor class and the 'base' fo the addresses therein.
404 private bool GetTerrainPhysicalAtXYZ(Vector3 pos, out BSTerrainPhys outPhysTerrain, out Vector3 outTerrainBase)
405 {
406 int offsetX = ((int)(pos.X / (int)DefaultRegionSize.X)) * (int)DefaultRegionSize.X;
407 int offsetY = ((int)(pos.Y / (int)DefaultRegionSize.Y)) * (int)DefaultRegionSize.Y;
408 Vector3 terrainBaseXYZ = new Vector3(offsetX, offsetY, 0f);
409
410 BSTerrainPhys physTerrain = null;
411 lock (m_terrains)
412 {
413 m_terrains.TryGetValue(terrainBaseXYZ, out physTerrain);
414 }
415 outTerrainBase = terrainBaseXYZ;
416 outPhysTerrain = physTerrain;
417 return (physTerrain != null);
418 }
419
378 // Although no one seems to check this, I do support combining. 420 // Although no one seems to check this, I do support combining.
379 public bool SupportsCombining() 421 public bool SupportsCombining()
380 { 422 {
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs
index dca7150..6dc0d92 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSTerrainMesh.cs
@@ -88,11 +88,13 @@ public sealed class BSTerrainMesh : BSTerrainPhys
88 // Something is very messed up and a crash is in our future. 88 // Something is very messed up and a crash is in our future.
89 return; 89 return;
90 } 90 }
91 PhysicsScene.DetailLog("{0},BSTerrainMesh.create,meshed,indices={1},indSz={2},vertices={3},vertSz={4}",
92 ID, indicesCount, indices.Length, verticesCount, vertices.Length);
91 93
92 m_terrainShape = new BulletShape(BulletSimAPI.CreateMeshShape2(PhysicsScene.World.ptr, 94 m_terrainShape = new BulletShape(BulletSimAPI.CreateMeshShape2(PhysicsScene.World.ptr,
93 indicesCount, indices, verticesCount, vertices), 95 indicesCount, indices, verticesCount, vertices),
94 BSPhysicsShapeType.SHAPE_MESH); 96 BSPhysicsShapeType.SHAPE_MESH);
95 if (m_terrainShape.ptr == IntPtr.Zero) 97 if (!m_terrainShape.HasPhysicalShape)
96 { 98 {
97 // DISASTER!! 99 // DISASTER!!
98 PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedCreationOfShape", ID); 100 PhysicsScene.DetailLog("{0},BSTerrainMesh.create,failedCreationOfShape", ID);
@@ -105,7 +107,7 @@ public sealed class BSTerrainMesh : BSTerrainPhys
105 Quaternion rot = Quaternion.Identity; 107 Quaternion rot = Quaternion.Identity;
106 108
107 m_terrainBody = new BulletBody(id, BulletSimAPI.CreateBodyWithDefaultMotionState2( m_terrainShape.ptr, ID, pos, rot)); 109 m_terrainBody = new BulletBody(id, BulletSimAPI.CreateBodyWithDefaultMotionState2( m_terrainShape.ptr, ID, pos, rot));
108 if (m_terrainBody.ptr == IntPtr.Zero) 110 if (!m_terrainBody.HasPhysicalBody)
109 { 111 {
110 // DISASTER!! 112 // DISASTER!!
111 physicsScene.Logger.ErrorFormat("{0} Failed creation of terrain body! base={1}", LogHeader, TerrainBase); 113 physicsScene.Logger.ErrorFormat("{0} Failed creation of terrain body! base={1}", LogHeader, TerrainBase);
@@ -122,15 +124,14 @@ public sealed class BSTerrainMesh : BSTerrainPhys
122 // Static objects are not very massive. 124 // Static objects are not very massive.
123 BulletSimAPI.SetMassProps2(m_terrainBody.ptr, 0f, Vector3.Zero); 125 BulletSimAPI.SetMassProps2(m_terrainBody.ptr, 0f, Vector3.Zero);
124 126
125 // Return the new terrain to the world of physical objects 127 // Put the new terrain to the world of physical objects
126 BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_terrainBody.ptr); 128 BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, m_terrainBody.ptr);
127 129
128 // redo its bounding box now that it is in the world 130 // Redo its bounding box now that it is in the world
129 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_terrainBody.ptr); 131 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, m_terrainBody.ptr);
130 132
131 BulletSimAPI.SetCollisionFilterMask2(m_terrainBody.ptr, 133 m_terrainBody.collisionType = CollisionType.Terrain;
132 (uint)CollisionFilterGroups.TerrainFilter, 134 m_terrainBody.ApplyCollisionMask();
133 (uint)CollisionFilterGroups.TerrainMask);
134 135
135 // Make it so the terrain will not move or be considered for movement. 136 // Make it so the terrain will not move or be considered for movement.
136 BulletSimAPI.ForceActivationState2(m_terrainBody.ptr, ActivationState.DISABLE_SIMULATION); 137 BulletSimAPI.ForceActivationState2(m_terrainBody.ptr, ActivationState.DISABLE_SIMULATION);
@@ -138,7 +139,7 @@ public sealed class BSTerrainMesh : BSTerrainPhys
138 139
139 public override void Dispose() 140 public override void Dispose()
140 { 141 {
141 if (m_terrainBody.ptr != IntPtr.Zero) 142 if (m_terrainBody.HasPhysicalBody)
142 { 143 {
143 BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_terrainBody.ptr); 144 BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, m_terrainBody.ptr);
144 // Frees both the body and the shape. 145 // Frees both the body and the shape.
@@ -146,7 +147,7 @@ public sealed class BSTerrainMesh : BSTerrainPhys
146 } 147 }
147 } 148 }
148 149
149 public override float GetHeightAtXYZ(Vector3 pos) 150 public override float GetTerrainHeightAtXYZ(Vector3 pos)
150 { 151 {
151 // For the moment use the saved heightmap to get the terrain height. 152 // For the moment use the saved heightmap to get the terrain height.
152 // TODO: raycast downward to find the true terrain below the position. 153 // TODO: raycast downward to find the true terrain below the position.
@@ -167,6 +168,12 @@ public sealed class BSTerrainMesh : BSTerrainPhys
167 return ret; 168 return ret;
168 } 169 }
169 170
171 // The passed position is relative to the base of the region.
172 public override float GetWaterLevelAtXYZ(Vector3 pos)
173 {
174 return PhysicsScene.SimpleWaterLevel;
175 }
176
170 // Convert the passed heightmap to mesh information suitable for CreateMeshShape2(). 177 // Convert the passed heightmap to mesh information suitable for CreateMeshShape2().
171 // Return 'true' if successfully created. 178 // Return 'true' if successfully created.
172 public static bool ConvertHeightmapToMesh( 179 public static bool ConvertHeightmapToMesh(
@@ -188,6 +195,11 @@ public sealed class BSTerrainMesh : BSTerrainPhys
188 // Simple mesh creation which assumes magnification == 1. 195 // Simple mesh creation which assumes magnification == 1.
189 // TODO: do a more general solution that scales, adds new vertices and smoothes the result. 196 // TODO: do a more general solution that scales, adds new vertices and smoothes the result.
190 197
198 // Create an array of vertices that is sizeX+1 by sizeY+1 (note the loop
199 // from zero to <= sizeX). The triangle indices are then generated as two triangles
200 // per heightmap point. There are sizeX by sizeY of these squares. The extra row and
201 // column of vertices are used to complete the triangles of the last row and column
202 // of the heightmap.
191 try 203 try
192 { 204 {
193 // One vertice per heightmap value plus the vertices off the top and bottom edge. 205 // One vertice per heightmap value plus the vertices off the top and bottom edge.
@@ -200,16 +212,18 @@ public sealed class BSTerrainMesh : BSTerrainPhys
200 float magY = (float)sizeY / extentY; 212 float magY = (float)sizeY / extentY;
201 physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh,totVert={1},totInd={2},extentBase={3},magX={4},magY={5}", 213 physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh,totVert={1},totInd={2},extentBase={3},magX={4},magY={5}",
202 BSScene.DetailLogZero, totalVertices, totalIndices, extentBase, magX, magY); 214 BSScene.DetailLogZero, totalVertices, totalIndices, extentBase, magX, magY);
215 float minHeight = float.MaxValue;
203 // Note that sizeX+1 vertices are created since there is land between this and the next region. 216 // Note that sizeX+1 vertices are created since there is land between this and the next region.
204 for (int yy = 0; yy <= sizeY; yy++) 217 for (int yy = 0; yy <= sizeY; yy++)
205 { 218 {
206 for (int xx = 0; xx <= sizeX; xx++) // Hint: the "<=" means we got through sizeX + 1 times 219 for (int xx = 0; xx <= sizeX; xx++) // Hint: the "<=" means we go around sizeX + 1 times
207 { 220 {
208 int offset = yy * sizeX + xx; 221 int offset = yy * sizeX + xx;
209 // Extend the height from the height from the last row or column 222 // Extend the height with the height from the last row or column
210 if (yy == sizeY) offset -= sizeX; 223 if (yy == sizeY) offset -= sizeX;
211 if (xx == sizeX) offset -= 1; 224 if (xx == sizeX) offset -= 1;
212 float height = heightMap[offset]; 225 float height = heightMap[offset];
226 minHeight = Math.Min(minHeight, height);
213 vertices[verticesCount + 0] = (float)xx * magX + extentBase.X; 227 vertices[verticesCount + 0] = (float)xx * magX + extentBase.X;
214 vertices[verticesCount + 1] = (float)yy * magY + extentBase.Y; 228 vertices[verticesCount + 1] = (float)yy * magY + extentBase.Y;
215 vertices[verticesCount + 2] = height + extentBase.Z; 229 vertices[verticesCount + 2] = height + extentBase.Z;
@@ -217,14 +231,12 @@ public sealed class BSTerrainMesh : BSTerrainPhys
217 } 231 }
218 } 232 }
219 verticesCount = verticesCount / 3; 233 verticesCount = verticesCount / 3;
220 physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh,completeVerts,verCount={1}",
221 BSScene.DetailLogZero, verticesCount);
222 234
223 for (int yy = 0; yy < sizeY; yy++) 235 for (int yy = 0; yy < sizeY; yy++)
224 { 236 {
225 for (int xx = 0; xx < sizeX; xx++) 237 for (int xx = 0; xx < sizeX; xx++)
226 { 238 {
227 int offset = yy * sizeX + xx; 239 int offset = yy * (sizeX + 1) + xx;
228 // Each vertices is presumed to be the upper left corner of a box of two triangles 240 // Each vertices is presumed to be the upper left corner of a box of two triangles
229 indices[indicesCount + 0] = offset; 241 indices[indicesCount + 0] = offset;
230 indices[indicesCount + 1] = offset + 1; 242 indices[indicesCount + 1] = offset + 1;
@@ -235,8 +247,7 @@ public sealed class BSTerrainMesh : BSTerrainPhys
235 indicesCount += 6; 247 indicesCount += 6;
236 } 248 }
237 } 249 }
238 physicsScene.DetailLog("{0},BSTerrainMesh.ConvertHeightMapToMesh,completeIndices,indCount={1}", // DEEBUG DEBUG DEBUG 250
239 LogHeader, indicesCount); // DEBUG
240 ret = true; 251 ret = true;
241 } 252 }
242 catch (Exception e) 253 catch (Exception e)
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs
index e60a760..962b540 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimAPI.cs
@@ -25,6 +25,7 @@
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */ 26 */
27using System; 27using System;
28using System.Collections.Generic;
28using System.Runtime.InteropServices; 29using System.Runtime.InteropServices;
29using System.Security; 30using System.Security;
30using System.Text; 31using System.Text;
@@ -32,93 +33,6 @@ using OpenMetaverse;
32 33
33namespace OpenSim.Region.Physics.BulletSPlugin { 34namespace OpenSim.Region.Physics.BulletSPlugin {
34 35
35// Classes to allow some type checking for the API
36// These hold pointers to allocated objects in the unmanaged space.
37
38// The physics engine controller class created at initialization
39public struct BulletSim
40{
41 public BulletSim(uint worldId, BSScene bss, IntPtr xx)
42 {
43 ptr = xx;
44 worldID = worldId;
45 physicsScene = bss;
46 }
47 public IntPtr ptr;
48 public uint worldID;
49 // The scene is only in here so very low level routines have a handle to print debug/error messages
50 public BSScene physicsScene;
51}
52
53// An allocated Bullet btRigidBody
54public struct BulletBody
55{
56 public BulletBody(uint id, IntPtr xx)
57 {
58 ID = id;
59 ptr = xx;
60 collisionFilter = 0;
61 collisionMask = 0;
62 }
63 public IntPtr ptr;
64 public uint ID;
65 public CollisionFilterGroups collisionFilter;
66 public CollisionFilterGroups collisionMask;
67 public override string ToString()
68 {
69 StringBuilder buff = new StringBuilder();
70 buff.Append("<id=");
71 buff.Append(ID.ToString());
72 buff.Append(",p=");
73 buff.Append(ptr.ToString("X"));
74 if (collisionFilter != 0 || collisionMask != 0)
75 {
76 buff.Append(",f=");
77 buff.Append(collisionFilter.ToString("X"));
78 buff.Append(",m=");
79 buff.Append(collisionMask.ToString("X"));
80 }
81 buff.Append(">");
82 return buff.ToString();
83 }
84}
85
86public struct BulletShape
87{
88 public BulletShape(IntPtr xx)
89 {
90 ptr = xx;
91 type=BSPhysicsShapeType.SHAPE_UNKNOWN;
92 shapeKey = (System.UInt64)FixedShapeKey.KEY_NONE;
93 isNativeShape = false;
94 }
95 public BulletShape(IntPtr xx, BSPhysicsShapeType typ)
96 {
97 ptr = xx;
98 type = typ;
99 shapeKey = 0;
100 isNativeShape = false;
101 }
102 public IntPtr ptr;
103 public BSPhysicsShapeType type;
104 public System.UInt64 shapeKey;
105 public bool isNativeShape;
106 public override string ToString()
107 {
108 StringBuilder buff = new StringBuilder();
109 buff.Append("<p=");
110 buff.Append(ptr.ToString("X"));
111 buff.Append(",s=");
112 buff.Append(type.ToString());
113 buff.Append(",k=");
114 buff.Append(shapeKey.ToString("X"));
115 buff.Append(",n=");
116 buff.Append(isNativeShape.ToString());
117 buff.Append(">");
118 return buff.ToString();
119 }
120}
121
122 // Constraint type values as defined by Bullet 36 // Constraint type values as defined by Bullet
123public enum ConstraintType : int 37public enum ConstraintType : int
124{ 38{
@@ -132,44 +46,6 @@ public enum ConstraintType : int
132 MAX_CONSTRAINT_TYPE 46 MAX_CONSTRAINT_TYPE
133} 47}
134 48
135// An allocated Bullet btConstraint
136public struct BulletConstraint
137{
138 public BulletConstraint(IntPtr xx)
139 {
140 ptr = xx;
141 }
142 public IntPtr ptr;
143}
144
145// An allocated HeightMapThing which holds various heightmap info.
146// Made a class rather than a struct so there would be only one
147// instance of this and C# will pass around pointers rather
148// than making copies.
149public class BulletHeightMapInfo
150{
151 public BulletHeightMapInfo(uint id, float[] hm, IntPtr xx) {
152 ID = id;
153 Ptr = xx;
154 heightMap = hm;
155 terrainRegionBase = Vector3.Zero;
156 minCoords = new Vector3(100f, 100f, 25f);
157 maxCoords = new Vector3(101f, 101f, 26f);
158 minZ = maxZ = 0f;
159 sizeX = sizeY = 256f;
160 }
161 public uint ID;
162 public IntPtr Ptr;
163 public float[] heightMap;
164 public Vector3 terrainRegionBase;
165 public Vector3 minCoords;
166 public Vector3 maxCoords;
167 public float sizeX, sizeY;
168 public float minZ, maxZ;
169 public BulletShape terrainShape;
170 public BulletBody terrainBody;
171}
172
173// =============================================================================== 49// ===============================================================================
174[StructLayout(LayoutKind.Sequential)] 50[StructLayout(LayoutKind.Sequential)]
175public struct ConvexHull 51public struct ConvexHull
@@ -287,6 +163,8 @@ public struct ConfigurationParameters
287 public float terrainFriction; 163 public float terrainFriction;
288 public float terrainHitFraction; 164 public float terrainHitFraction;
289 public float terrainRestitution; 165 public float terrainRestitution;
166 public float terrainCollisionMargin;
167
290 public float avatarFriction; 168 public float avatarFriction;
291 public float avatarStandingFriction; 169 public float avatarStandingFriction;
292 public float avatarDensity; 170 public float avatarDensity;
@@ -296,6 +174,8 @@ public struct ConfigurationParameters
296 public float avatarCapsuleHeight; 174 public float avatarCapsuleHeight;
297 public float avatarContactProcessingThreshold; 175 public float avatarContactProcessingThreshold;
298 176
177 public float vehicleAngularDamping;
178
299 public float maxPersistantManifoldPoolSize; 179 public float maxPersistantManifoldPoolSize;
300 public float maxCollisionAlgorithmPoolSize; 180 public float maxCollisionAlgorithmPoolSize;
301 public float shouldDisableContactPoolDynamicAllocation; 181 public float shouldDisableContactPoolDynamicAllocation;
@@ -353,56 +233,35 @@ public enum CollisionFlags : uint
353 CF_CHARACTER_OBJECT = 1 << 4, 233 CF_CHARACTER_OBJECT = 1 << 4,
354 CF_DISABLE_VISUALIZE_OBJECT = 1 << 5, 234 CF_DISABLE_VISUALIZE_OBJECT = 1 << 5,
355 CF_DISABLE_SPU_COLLISION_PROCESS = 1 << 6, 235 CF_DISABLE_SPU_COLLISION_PROCESS = 1 << 6,
356 // Following used by BulletSim to control collisions 236 // Following used by BulletSim to control collisions and updates
357 BS_SUBSCRIBE_COLLISION_EVENTS = 1 << 10, 237 BS_SUBSCRIBE_COLLISION_EVENTS = 1 << 10,
358 BS_FLOATS_ON_WATER = 1 << 11, 238 BS_FLOATS_ON_WATER = 1 << 11,
239 BS_VEHICLE_COLLISIONS = 1 << 12,
359 BS_NONE = 0, 240 BS_NONE = 0,
360 BS_ALL = 0xFFFFFFFF, 241 BS_ALL = 0xFFFFFFFF
361
362 // These are the collision flags switched depending on physical state.
363 // The other flags are used for other things and should not be fooled with.
364 BS_ACTIVE = CF_STATIC_OBJECT
365 | CF_KINEMATIC_OBJECT
366 | CF_NO_CONTACT_RESPONSE
367}; 242};
368 243
369// Values for collisions groups and masks 244// Values f collisions groups and masks
370public enum CollisionFilterGroups : uint 245public enum CollisionFilterGroups : uint
371{ 246{
372 // Don't use the bit definitions!! Define the use in a 247 // Don't use the bit definitions!! Define the use in a
373 // filter/mask definition below. This way collision interactions 248 // filter/mask definition below. This way collision interactions
374 // are more easily debugged. 249 // are more easily found and debugged.
375 BNoneFilter = 0, 250 BNoneGroup = 0,
376 BDefaultFilter = 1 << 0, 251 BDefaultGroup = 1 << 0,
377 BStaticFilter = 1 << 1, 252 BStaticGroup = 1 << 1,
378 BKinematicFilter = 1 << 2, 253 BKinematicGroup = 1 << 2,
379 BDebrisFilter = 1 << 3, 254 BDebrisGroup = 1 << 3,
380 BSensorTrigger = 1 << 4, 255 BSensorTrigger = 1 << 4,
381 BCharacterFilter = 1 << 5, 256 BCharacterGroup = 1 << 5,
382 BAllFilter = 0xFFFFFFFF, 257 BAllGroup = 0xFFFFFFFF,
383 // Filter groups defined by BulletSim 258 // Filter groups defined by BulletSim
384 BGroundPlaneFilter = 1 << 10, 259 BGroundPlaneGroup = 1 << 10,
385 BTerrainFilter = 1 << 11, 260 BTerrainGroup = 1 << 11,
386 BRaycastFilter = 1 << 12, 261 BRaycastGroup = 1 << 12,
387 BSolidFilter = 1 << 13, 262 BSolidGroup = 1 << 13,
388 BLinksetFilter = 1 << 14, 263 // BLinksetGroup = xx // a linkset proper is either static or dynamic
389 264 BLinksetChildGroup = 1 << 14,
390 // The collsion filters and masked are defined in one place -- don't want them scattered
391 AvatarFilter = BCharacterFilter,
392 AvatarMask = BAllFilter,
393 ObjectFilter = BSolidFilter,
394 ObjectMask = BAllFilter,
395 StaticObjectFilter = BStaticFilter,
396 StaticObjectMask = BAllFilter & ~BStaticFilter, // static objects don't collide with each other
397 LinksetFilter = BLinksetFilter,
398 LinksetMask = BAllFilter & ~BLinksetFilter, // linkset objects don't collide with each other
399 VolumeDetectFilter = BSensorTrigger,
400 VolumeDetectMask = ~BSensorTrigger,
401 TerrainFilter = BTerrainFilter,
402 TerrainMask = BAllFilter & ~BStaticFilter, // static objects on the ground don't collide
403 GroundPlaneFilter = BGroundPlaneFilter,
404 GroundPlaneMask = BAllFilter
405
406}; 265};
407 266
408// CFM controls the 'hardness' of the constraint. 0=fixed, 0..1=violatable. Default=0 267// CFM controls the 'hardness' of the constraint. 0=fixed, 0..1=violatable. Default=0
@@ -429,7 +288,7 @@ public enum ConstraintParamAxis : int
429 288
430// =============================================================================== 289// ===============================================================================
431static class BulletSimAPI { 290static class BulletSimAPI {
432 291// ===============================================================================
433// Link back to the managed code for outputting log messages 292// Link back to the managed code for outputting log messages
434[UnmanagedFunctionPointer(CallingConvention.Cdecl)] 293[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
435public delegate void DebugLogCallback([MarshalAs(UnmanagedType.LPStr)]string msg); 294public delegate void DebugLogCallback([MarshalAs(UnmanagedType.LPStr)]string msg);
@@ -482,6 +341,9 @@ public static extern IntPtr BuildNativeShape2(IntPtr world, ShapeData shapeData)
482public static extern bool IsNativeShape2(IntPtr shape); 341public static extern bool IsNativeShape2(IntPtr shape);
483 342
484[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] 343[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
344public static extern void SetShapeCollisionMargin(IntPtr shape, float margin);
345
346[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
485public static extern IntPtr BuildCapsuleShape2(IntPtr world, float radius, float height, Vector3 scale); 347public static extern IntPtr BuildCapsuleShape2(IntPtr world, float radius, float height, Vector3 scale);
486 348
487[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] 349[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
@@ -938,7 +800,7 @@ public static extern IntPtr GetConstraintRef2(IntPtr obj, int index);
938public static extern int GetNumConstraintRefs2(IntPtr obj); 800public static extern int GetNumConstraintRefs2(IntPtr obj);
939 801
940[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] 802[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
941public static extern void SetCollisionFilterMask2(IntPtr body, uint filter, uint mask); 803public static extern bool SetCollisionGroupMask2(IntPtr body, uint filter, uint mask);
942 804
943// ===================================================================================== 805// =====================================================================================
944// btCollisionShape entries 806// btCollisionShape entries
@@ -1000,13 +862,16 @@ public static extern void DumpRigidBody2(IntPtr sim, IntPtr collisionObject);
1000public static extern void DumpCollisionShape2(IntPtr sim, IntPtr collisionShape); 862public static extern void DumpCollisionShape2(IntPtr sim, IntPtr collisionShape);
1001 863
1002[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] 864[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
865public static extern void DumpMapInfo2(IntPtr sim, IntPtr manInfo);
866
867[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
1003public static extern void DumpConstraint2(IntPtr sim, IntPtr constrain); 868public static extern void DumpConstraint2(IntPtr sim, IntPtr constrain);
1004 869
1005[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] 870[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
1006public static extern void DumpAllInfo2(IntPtr sim); 871public static extern void DumpActivationInfo2(IntPtr sim);
1007 872
1008[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] 873[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
1009public static extern void DumpMapInfo2(IntPtr sim, IntPtr manInfo); 874public static extern void DumpAllInfo2(IntPtr sim);
1010 875
1011[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity] 876[DllImport("BulletSim", CallingConvention = CallingConvention.Cdecl), SuppressUnmanagedCodeSecurity]
1012public static extern void DumpPhysicsStatistics2(IntPtr sim); 877public static extern void DumpPhysicsStatistics2(IntPtr sim);
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimData.cs b/OpenSim/Region/Physics/BulletSPlugin/BulletSimData.cs
new file mode 100755
index 0000000..662177f
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimData.cs
@@ -0,0 +1,278 @@
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 copyrightD
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 */
27using System;
28using System.Collections.Generic;
29using System.Text;
30using OMV = OpenMetaverse;
31
32namespace OpenSim.Region.Physics.BulletSPlugin
33{
34// Classes to allow some type checking for the API
35// These hold pointers to allocated objects in the unmanaged space.
36
37// The physics engine controller class created at initialization
38public struct BulletSim
39{
40 public BulletSim(uint worldId, BSScene bss, IntPtr xx)
41 {
42 ptr = xx;
43 worldID = worldId;
44 physicsScene = bss;
45 }
46 public IntPtr ptr;
47 public uint worldID;
48 // The scene is only in here so very low level routines have a handle to print debug/error messages
49 public BSScene physicsScene;
50}
51
52// An allocated Bullet btRigidBody
53public struct BulletBody
54{
55 public BulletBody(uint id) : this(id, IntPtr.Zero)
56 {
57 }
58 public BulletBody(uint id, IntPtr xx)
59 {
60 ID = id;
61 ptr = xx;
62 collisionType = CollisionType.Static;
63 }
64 public IntPtr ptr;
65 public uint ID;
66 public CollisionType collisionType;
67
68 public void Clear()
69 {
70 ptr = IntPtr.Zero;
71 }
72 public bool HasPhysicalBody { get { return ptr != IntPtr.Zero; } }
73
74 // Apply the specificed collision mask into the physical world
75 public void ApplyCollisionMask()
76 {
77 // Should assert the body has been added to the physical world.
78 // (The collision masks are stored in the collision proxy cache which only exists for
79 // a collision body that is in the world.)
80 BulletSimAPI.SetCollisionGroupMask2(ptr,
81 BulletSimData.CollisionTypeMasks[collisionType].group,
82 BulletSimData.CollisionTypeMasks[collisionType].mask);
83 }
84
85 public override string ToString()
86 {
87 StringBuilder buff = new StringBuilder();
88 buff.Append("<id=");
89 buff.Append(ID.ToString());
90 buff.Append(",p=");
91 buff.Append(ptr.ToString("X"));
92 buff.Append(",c=");
93 buff.Append(collisionType);
94 buff.Append(">");
95 return buff.ToString();
96 }
97}
98
99public struct BulletShape
100{
101 public BulletShape(IntPtr xx) : this(xx, BSPhysicsShapeType.SHAPE_UNKNOWN)
102 {
103 }
104 public BulletShape(IntPtr xx, BSPhysicsShapeType typ)
105 {
106 ptr = xx;
107 type = typ;
108 shapeKey = (System.UInt64)FixedShapeKey.KEY_NONE;
109 isNativeShape = false;
110 }
111 public IntPtr ptr;
112 public BSPhysicsShapeType type;
113 public System.UInt64 shapeKey;
114 public bool isNativeShape;
115
116 public void Clear()
117 {
118 ptr = IntPtr.Zero;
119 }
120 public bool HasPhysicalShape { get { return ptr != IntPtr.Zero; } }
121
122 public override string ToString()
123 {
124 StringBuilder buff = new StringBuilder();
125 buff.Append("<p=");
126 buff.Append(ptr.ToString("X"));
127 buff.Append(",s=");
128 buff.Append(type.ToString());
129 buff.Append(",k=");
130 buff.Append(shapeKey.ToString("X"));
131 buff.Append(",n=");
132 buff.Append(isNativeShape.ToString());
133 buff.Append(">");
134 return buff.ToString();
135 }
136}
137
138// An allocated Bullet btConstraint
139public struct BulletConstraint
140{
141 public BulletConstraint(IntPtr xx)
142 {
143 ptr = xx;
144 }
145 public IntPtr ptr;
146
147 public void Clear()
148 {
149 ptr = IntPtr.Zero;
150 }
151 public bool HasPhysicalConstraint { get { return ptr != IntPtr.Zero; } }
152}
153
154// An allocated HeightMapThing which holds various heightmap info.
155// Made a class rather than a struct so there would be only one
156// instance of this and C# will pass around pointers rather
157// than making copies.
158public class BulletHeightMapInfo
159{
160 public BulletHeightMapInfo(uint id, float[] hm, IntPtr xx) {
161 ID = id;
162 Ptr = xx;
163 heightMap = hm;
164 terrainRegionBase = OMV.Vector3.Zero;
165 minCoords = new OMV.Vector3(100f, 100f, 25f);
166 maxCoords = new OMV.Vector3(101f, 101f, 26f);
167 minZ = maxZ = 0f;
168 sizeX = sizeY = 256f;
169 }
170 public uint ID;
171 public IntPtr Ptr;
172 public float[] heightMap;
173 public OMV.Vector3 terrainRegionBase;
174 public OMV.Vector3 minCoords;
175 public OMV.Vector3 maxCoords;
176 public float sizeX, sizeY;
177 public float minZ, maxZ;
178 public BulletShape terrainShape;
179 public BulletBody terrainBody;
180}
181
182// The general class of collsion object.
183public enum CollisionType
184{
185 Avatar,
186 Groundplane,
187 Terrain,
188 Static,
189 Dynamic,
190 VolumeDetect,
191 // Linkset, // A linkset should be either Static or Dynamic
192 LinksetChild,
193 Unknown
194};
195
196// Hold specification of group and mask collision flags for a CollisionType
197public struct CollisionTypeFilterGroup
198{
199 public CollisionTypeFilterGroup(CollisionType t, uint g, uint m)
200 {
201 type = t;
202 group = g;
203 mask = m;
204 }
205 public CollisionType type;
206 public uint group;
207 public uint mask;
208};
209
210 /* NOTE: old definitions kept for reference. Delete when things are working.
211 // The collsion filters and masked are defined in one place -- don't want them scattered
212 AvatarGroup = BCharacterGroup,
213 AvatarMask = BAllGroup,
214 ObjectGroup = BSolidGroup,
215 ObjectMask = BAllGroup,
216 StaticObjectGroup = BStaticGroup,
217 StaticObjectMask = AvatarGroup | ObjectGroup, // static things don't interact with much
218 LinksetGroup = BLinksetGroup,
219 LinksetMask = BAllGroup,
220 LinksetChildGroup = BLinksetChildGroup,
221 LinksetChildMask = BNoneGroup, // Linkset children disappear from the world
222 VolumeDetectGroup = BSensorTrigger,
223 VolumeDetectMask = ~BSensorTrigger,
224 TerrainGroup = BTerrainGroup,
225 TerrainMask = BAllGroup & ~BStaticGroup, // static objects on the ground don't collide
226 GroundPlaneGroup = BGroundPlaneGroup,
227 GroundPlaneMask = BAllGroup
228 */
229
230public static class BulletSimData
231{
232
233// Map of collisionTypes to flags for collision groups and masks.
234// As mentioned above, don't use the CollisionFilterGroups definitions directly in the code
235// but, instead, use references to this dictionary. Finding and debugging
236// collision flag problems will be made easier.
237public static Dictionary<CollisionType, CollisionTypeFilterGroup> CollisionTypeMasks
238 = new Dictionary<CollisionType, CollisionTypeFilterGroup>()
239{
240 { CollisionType.Avatar,
241 new CollisionTypeFilterGroup(CollisionType.Avatar,
242 (uint)CollisionFilterGroups.BCharacterGroup,
243 (uint)CollisionFilterGroups.BAllGroup)
244 },
245 { CollisionType.Groundplane,
246 new CollisionTypeFilterGroup(CollisionType.Groundplane,
247 (uint)CollisionFilterGroups.BGroundPlaneGroup,
248 (uint)CollisionFilterGroups.BAllGroup)
249 },
250 { CollisionType.Terrain,
251 new CollisionTypeFilterGroup(CollisionType.Terrain,
252 (uint)CollisionFilterGroups.BTerrainGroup,
253 (uint)(CollisionFilterGroups.BAllGroup & ~CollisionFilterGroups.BStaticGroup))
254 },
255 { CollisionType.Static,
256 new CollisionTypeFilterGroup(CollisionType.Static,
257 (uint)CollisionFilterGroups.BStaticGroup,
258 (uint)(CollisionFilterGroups.BCharacterGroup | CollisionFilterGroups.BSolidGroup))
259 },
260 { CollisionType.Dynamic,
261 new CollisionTypeFilterGroup(CollisionType.Dynamic,
262 (uint)CollisionFilterGroups.BSolidGroup,
263 (uint)(CollisionFilterGroups.BAllGroup))
264 },
265 { CollisionType.VolumeDetect,
266 new CollisionTypeFilterGroup(CollisionType.VolumeDetect,
267 (uint)CollisionFilterGroups.BSensorTrigger,
268 (uint)(~CollisionFilterGroups.BSensorTrigger))
269 },
270 { CollisionType.LinksetChild,
271 new CollisionTypeFilterGroup(CollisionType.LinksetChild,
272 (uint)CollisionFilterGroups.BTerrainGroup,
273 (uint)(CollisionFilterGroups.BNoneGroup))
274 },
275};
276
277}
278}
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt b/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt
new file mode 100755
index 0000000..0d9a156
--- /dev/null
+++ b/OpenSim/Region/Physics/BulletSPlugin/BulletSimTODO.txt
@@ -0,0 +1,204 @@
1CURRENT PRIORITIES
2=================================================
3Eliminate all crashes (DONEish)
4 Editing/deleting physical linkset (DONE)
5 Border crossing of physical linkset (DONE)
6Enable vehicle border crossings (at least as poorly as ODE)
7 Avatar created in previous region and not new region when crossing border
8 Vehicle recreated in new sim at small Z value (offset from root value?) (DONE)
9Calibrate turning radius
10limitMotorUp calibration (more down?)
11study PID motors (include 'efficiency' implementation
12 Add to avatar movement
13
14CRASHES
15=================================================
1620121129.1411: editting/moving phys object across region boundries causes crash
17 getPos-> btRigidBody::upcast -> getBodyType -> BOOM
1820121128.1600: mesh object not rezzing (no physics mesh).
19 Causes many errors. Doesn't stop after first error with box shape.
20 Eventually crashes when deleting the object.
2120121206.1434: rez Sam-pan into OSGrid BulletSim11 region
22 Immediate simulator crash. Mono does not output any stacktrace and
23 log just stops after reporting taint-time linking of the linkset.
24
25VEHICLES TODO LIST:
26=================================================
27Border crossing with linked vehicle causes crash
28Neb vehicle taking > 25ms of physics time!!
29Vehicles (Move smoothly)
30Add vehicle collisions so IsColliding is properly reported.
31 Needed for banking, limitMotorUp, movementLimiting, ...
32Some vehicles should not be able to turn if no speed or off ground.
33Cannot edit/move a vehicle being ridden: it jumps back to the origional position.
34Neb car jiggling left and right
35 Happens on terrain and any other mesh object. Flat cubes are much smoother.
36 This has been reduced but not eliminated.
37Light cycle falling over when driving
38Implement referenceFrame for all the motion routines.
39Angular motion around Z moves the vehicle in world Z and not vehicle Z in ODE.
40 Verify that angular motion specified around Z moves in the vehicle coordinates.
41Verify llGetVel() is returning a smooth and good value for vehicle movement.
42llGetVel() should return the root's velocity if requested in a child prim.
43Implement function efficiency for lineaar and angular motion.
44Should vehicle angular/linear movement friction happen after all the components
45 or does it only apply to the basic movement?
46After getting off a vehicle, the root prim is phantom (can be walked through)
47 Need to force a position update for the root prim after compound shape destruction
48Linkset explosion after three "rides" on Nebadon lite vehicle (LinksetConstraint)
49For limitMotorUp, use raycast down to find if vehicle is in the air.
50Remove vehicle angular velocity zeroing in BSPrim.UpdateProperties().
51 A kludge that isn't fixing the real problem of Bullet adding extra motion.
52
53BULLETSIM TODO LIST:
54=================================================
55Revisit CollisionMargin. Builders notice the 0.04 spacing between prims.
56Avatar height off after unsitting (floats off ground)
57 Editting appearance then moving restores.
58 Must not be initializing height when recreating capsule after unsit.
59Duplicating a physical prim causes old prim to jump away
60 Dup a phys prim and the original become unselected and thus interacts w/ selected prim.
61Scenes with hundred of thousands of static objects take a lot of physics CPU time.
62BSPrim.Force should set a continious force on the prim. The force should be
63 applied each tick. Some limits?
64Gun sending shooter flying.
65Collision margin (gap between physical objects lying on each other)
66Boundry checking (crashes related to crossing boundry)
67 Add check for border edge position for avatars and objects.
68 Verify the events are created for border crossings.
69Avatar rotation (check out changes to ScenePresence for physical rotation)
70Avatar running (what does phys engine need to do?)
71Small physical objects do not interact correctly
72 Create chain of .5x.5x.1 torui and make all but top physical so to hang.
73 The chain will fall apart and pairs will dance around on ground
74 Chains of 1x1x.2 will stay connected but will dance.
75 Chains above 2x2x.4 are move stable and get stablier as torui get larger.
76Add PID motor for avatar movement (slow to stop, ...)
77setForce should set a constant force. Different than AddImpulse.
78Implement raycast.
79Implement ShapeCollection.Dispose()
80Implement water as a plain so raycasting and collisions can happen with same.
81Add osGetPhysicsEngineName() so scripters can tell whether BulletSim or ODE
82 Also osGetPhysicsEngineVerion() maybe.
83Linkset.Position and Linkset.Orientation requre rewrite to properly return
84 child position. LinksetConstraint acts like it's at taint time!!
85
86LINKSETS
87======================================================
88Linksets should allow collisions to individual children
89 Add LocalID to children shapes in LinksetCompound and create events for individuals
90LinksetCompound: when one of the children changes orientation (like tires
91 turning on a vehicle, the whole compound object is rebuilt. Optimize this
92 so orientation/position of individual children can change without a rebuild.
93Verify/think through scripts in children of linksets. What do they reference
94 and return when getting position, velocity, ...
95Confirm constraint linksets still work after making all the changes for compound linksets.
96Add 'changed' flag or similar to reduce the number of times a linkset is rebuilt.
97 For compound linksets, add ability to remove or reposition individual child shapes.
98Disable activity of passive linkset children.
99 Since the linkset is a compound object, the old prims are left lying
100 around and need to be phantomized so they don't collide, ...
101Speed up creation of large physical linksets
102 For instance, sitting in Neb's car (130 prims) takes several seconds to become physical
103Eliminate collisions between objects in a linkset. (LinksetConstraint)
104 Have UserPointer point to struct with localID and linksetID?
105 Objects in original linkset still collide with each other?
106
107MORE
108======================================================
109Find/remove avatar collision with ID=0.
110Test avatar walking up stairs. How does compare with SL.
111 Radius of the capsule affects ability to climb edges.
112Tune terrain/object friction to be closer to SL.
113Debounce avatar contact so legs don't keep folding up when standing.
114Implement LSL physics controls. Like STATUS_ROTATE_X.
115Add border extensions to terrain to help region crossings and objects leaving region.
116
117Performance test with lots of avatars. Can BulletSim support a thousand?
118Optimize collisions in C++: only send up to the object subscribed to collisions.
119 Use collision subscription and remove the collsion(A,B) and collision(B,A)
120Check whether SimMotionState needs large if statement (see TODO).
121
122Implement 'top colliders' info.
123Avatar jump
124Performance measurement and changes to make quicker.
125Implement detailed physics stats (GetStats()).
126
127Measure performance improvement from hulls
128Test not using ghost objects for volume detect implementation.
129Performance of closures and delegates for taint processing
130 Are there faster ways?
131 Is any slowdown introduced by the existing implementation significant?
132Is there are more efficient method of implementing pre and post step actions?
133 See http://www.codeproject.com/Articles/29922/Weak-Events-in-C
134
135Physics Arena central pyramid: why is one side permiable?
136
137INTERNAL IMPROVEMENT/CLEANUP
138=================================================
139Consider moving prim/character body and shape destruction in destroy()
140 to postTimeTime rather than protecting all the potential sets that
141 might have been queued up.
142Remove unused fields from ShapeData (not used in API2)
143Breakout code for mesh/hull/compound/native into separate BSShape* classes
144 Standardize access to building and reference code.
145 The skeleton classes are in the sources but are not complete or linked in.
146Make BSBody and BSShape real classes to centralize creation/changin/destruction
147 Convert state and parameter calls from BulletSimAPI direct calls to
148 calls on BSBody and BSShape
149Generalize Dynamics and PID with standardized motors.
150Generalize Linkset and vehicles into PropertyManagers
151 Methods for Refresh, RemoveBodyDependencies, RestoreBodyDependencies
152 Potentially add events for shape destruction, etc.
153Complete implemention of preStepActions
154 Replace vehicle step call with prestep event.
155 Is there a need for postStepActions? postStepTaints?
156Implement linkset by setting position of children when root updated. (LinksetManual)
157 Linkset implementation using manual prim movement.
158LinkablePrim class? Would that simplify/centralize the linkset logic?
159BSScene.UpdateParameterSet() is broken. How to set params on objects?
160Remove HeightmapInfo from terrain specification
161 Since C++ code does not need terrain height, this structure et al are not needed.
162Add floating motor for BS_FLOATS_ON_WATER so prim and avatar will
163 bob at the water level. BSPrim.PositionSanityCheck().
164Should taints check for existance or activeness of target?
165 When destroying linksets/etc, taints can be generated for objects that are
166 actually gone when the taint happens. Crashes don't happen because the taint closure
167 keeps the object from being freed, but that is just an accident.
168 Possibly have and 'active' flag that is checked by the taint processor?
169
170THREADING
171=================================================
172Do taint action immediately if not actually executing Bullet.
173 Add lock around Bullet execution and just do taint actions if simulation is not happening.
174
175DONE DONE DONE DONE
176=================================================
177Cleanup code in BSDynamics by using motors. (Resolution: started)
178Consider implementing terrain with a mesh rather than heightmap. (Resolution: done)
179 Would have better and adjustable resolution.
180Build terrain mesh so heighmap is height of the center of the square meter.
181 Resolution: NOT DONE: SL and ODE define meter square as being at one corner with one diagional.
182Terrain as mesh. (Resolution: done)
183How are static linksets seen by the physics engine?
184 Resolution: they are not linked in physics. When moved, all the children are repositioned.
185Convert BSCharacter to use all API2 (Resolution: done)
186Avatar pushing difficult (too heavy?)
187Use asset service passed to BulletSim to get sculptie bodies, etc. (Resolution: done)
188Remove old code in DLL (all non-API2 stuff). (Resolution: done)
189Measurements of mega-physical prim performance (with graph) (Resolution: done, email)
190Debug Bullet internal stats output (why is timing all wrong?)
191 Resolution: Bullet stats logging only works with a single instance of Bullet (one region).
192Implement meshes or just verify that they work. (Resolution: they do!)
193Do prim hash codes work for sculpties and meshes? (Resolution: yes)
194Linkset implementation using compound shapes. (Resolution: implemented LinksetCompound)
195 Compound shapes will need the LocalID in the shapes and collision
196 processing to get it from there.
197Light cycle not banking (Resolution: It doesn't. Banking is roll adding yaw.)
198Package Bullet source mods for Bullet internal stats output
199 (Resolution: move code into WorldData.h rather than relying on patches)
200Single prim vehicles don't seem to properly vehiclize.
201 (Resolution: mass was not getting set properly for single prim linksets)
202Add material type linkage and input all the material property definitions.
203 Skeleton classes and table are in the sources but are not filled or used.
204 (Resolution:
diff --git a/OpenSim/Region/Physics/ChOdePlugin/ODEPrim.cs b/OpenSim/Region/Physics/ChOdePlugin/ODEPrim.cs
index 8de70ef..ba24aa7 100644
--- a/OpenSim/Region/Physics/ChOdePlugin/ODEPrim.cs
+++ b/OpenSim/Region/Physics/ChOdePlugin/ODEPrim.cs
@@ -2190,7 +2190,7 @@ namespace OpenSim.Region.Physics.OdePlugin
2190 convex = false; 2190 convex = false;
2191 try 2191 try
2192 { 2192 {
2193 _mesh = _parent_scene.mesher.CreateMesh(m_primName, _pbs, _size, (int)LevelOfDetail.High, true,convex,false); 2193 _mesh = _parent_scene.mesher.CreateMesh(m_primName, _pbs, _size, (int)LevelOfDetail.High, true,false,convex,false);
2194 } 2194 }
2195 catch 2195 catch
2196 { 2196 {
@@ -2557,7 +2557,7 @@ namespace OpenSim.Region.Physics.OdePlugin
2557 2557
2558 try 2558 try
2559 { 2559 {
2560 mesh = _parent_scene.mesher.CreateMesh(oldname, _pbs, _size, (int)LevelOfDetail.High, true, convex,false); 2560 mesh = _parent_scene.mesher.CreateMesh(oldname, _pbs, _size, (int)LevelOfDetail.High, true, false,convex,false);
2561 } 2561 }
2562 catch 2562 catch
2563 { 2563 {
diff --git a/OpenSim/Region/Physics/Manager/IMesher.cs b/OpenSim/Region/Physics/Manager/IMesher.cs
index ecc2918..df980ab 100644
--- a/OpenSim/Region/Physics/Manager/IMesher.cs
+++ b/OpenSim/Region/Physics/Manager/IMesher.cs
@@ -37,7 +37,7 @@ namespace OpenSim.Region.Physics.Manager
37 { 37 {
38 IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod); 38 IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod);
39 IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical); 39 IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical);
40 IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool convex, bool forOde); 40 IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool shouldCache, bool convex, bool forOde);
41 IMesh GetMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool convex); 41 IMesh GetMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool convex);
42 void ReleaseMesh(IMesh mesh); 42 void ReleaseMesh(IMesh mesh);
43 void ExpireReleaseMeshs(); 43 void ExpireReleaseMeshs();
diff --git a/OpenSim/Region/Physics/Manager/PhysicsActor.cs b/OpenSim/Region/Physics/Manager/PhysicsActor.cs
index 9338130..e2789d6 100644
--- a/OpenSim/Region/Physics/Manager/PhysicsActor.cs
+++ b/OpenSim/Region/Physics/Manager/PhysicsActor.cs
@@ -349,17 +349,20 @@ namespace OpenSim.Region.Physics.Manager
349 } 349 }
350 350
351 /// <summary> 351 /// <summary>
352 /// Velocity of this actor. 352 /// The desired velocity of this actor.
353 /// </summary> 353 /// </summary>
354 /// <remarks> 354 /// <remarks>
355 /// Setting this provides a target velocity for physics scene updates. 355 /// Setting this provides a target velocity for physics scene updates.
356 /// Getting this returns the velocity calculated by physics scene updates, using factors such as target velocity, 356 /// Getting this returns the last set target. Fetch Velocity to get the current velocity.
357 /// time to accelerate and collisions.
358 /// </remarks> 357 /// </remarks>
358 protected Vector3 m_targetVelocity;
359 public virtual Vector3 TargetVelocity 359 public virtual Vector3 TargetVelocity
360 { 360 {
361 get { return Velocity; } 361 get { return m_targetVelocity; }
362 set { Velocity = value; } 362 set {
363 m_targetVelocity = value;
364 Velocity = m_targetVelocity;
365 }
363 } 366 }
364 367
365 public abstract Vector3 Velocity { get; set; } 368 public abstract Vector3 Velocity { get; set; }
diff --git a/OpenSim/Region/Physics/Manager/ZeroMesher.cs b/OpenSim/Region/Physics/Manager/ZeroMesher.cs
index 16846e6..80ecf66 100644
--- a/OpenSim/Region/Physics/Manager/ZeroMesher.cs
+++ b/OpenSim/Region/Physics/Manager/ZeroMesher.cs
@@ -64,16 +64,21 @@ namespace OpenSim.Region.Physics.Manager
64 { 64 {
65 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod) 65 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod)
66 { 66 {
67 return CreateMesh(primName, primShape, size, lod, false); 67 return CreateMesh(primName, primShape, size, lod, false, false);
68 } 68 }
69 69
70 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool convex,bool forOde) 70 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool shouldCache, bool convex,bool forOde)
71 { 71 {
72 return CreateMesh(primName, primShape, size, lod, false); 72 return CreateMesh(primName, primShape, size, lod, false);
73 } 73 }
74 74
75 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical) 75 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical)
76 { 76 {
77 return CreateMesh(primName, primShape, size, lod, false, false);
78 }
79
80 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool shouldCache)
81 {
77 // Remove the reference to the encoded JPEG2000 data so it can be GCed 82 // Remove the reference to the encoded JPEG2000 data so it can be GCed
78 primShape.SculptData = OpenMetaverse.Utils.EmptyBytes; 83 primShape.SculptData = OpenMetaverse.Utils.EmptyBytes;
79 84
diff --git a/OpenSim/Region/Physics/Meshing/Meshmerizer.cs b/OpenSim/Region/Physics/Meshing/Meshmerizer.cs
index f629c4d..d181b78 100644
--- a/OpenSim/Region/Physics/Meshing/Meshmerizer.cs
+++ b/OpenSim/Region/Physics/Meshing/Meshmerizer.cs
@@ -321,6 +321,9 @@ namespace OpenSim.Region.Physics.Meshing
321 321
322 if (primShape.SculptData.Length <= 0) 322 if (primShape.SculptData.Length <= 0)
323 { 323 {
324 // XXX: At the moment we can not log here since ODEPrim, for instance, ends up triggering this
325 // method twice - once before it has loaded sculpt data from the asset service and once afterwards.
326 // The first time will always call with unloaded SculptData if this needs to be uploaded.
324// m_log.ErrorFormat("[MESH]: asset data for {0} is zero length", primName); 327// m_log.ErrorFormat("[MESH]: asset data for {0} is zero length", primName);
325 return false; 328 return false;
326 } 329 }
@@ -699,16 +702,21 @@ namespace OpenSim.Region.Physics.Meshing
699 702
700 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod) 703 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod)
701 { 704 {
702 return CreateMesh(primName, primShape, size, lod, false); 705 return CreateMesh(primName, primShape, size, lod, false, true);
703 } 706 }
704 707
705 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool convex, bool forOde) 708 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool shouldCache, bool convex, bool forOde)
706 { 709 {
707 return CreateMesh(primName, primShape, size, lod, false); 710 return CreateMesh(primName, primShape, size, lod, false);
708 } 711 }
709 712
710 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical) 713 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical)
711 { 714 {
715 return CreateMesh(primName, primShape, size, lod, isPhysical, true);
716 }
717
718 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool shouldCache)
719 {
712#if SPAM 720#if SPAM
713 m_log.DebugFormat("[MESH]: Creating mesh for {0}", primName); 721 m_log.DebugFormat("[MESH]: Creating mesh for {0}", primName);
714#endif 722#endif
@@ -718,9 +726,12 @@ namespace OpenSim.Region.Physics.Meshing
718 726
719 // If this mesh has been created already, return it instead of creating another copy 727 // If this mesh has been created already, return it instead of creating another copy
720 // For large regions with 100k+ prims and hundreds of copies of each, this can save a GB or more of memory 728 // For large regions with 100k+ prims and hundreds of copies of each, this can save a GB or more of memory
721 key = primShape.GetMeshKey(size, lod); 729 if (shouldCache)
722 if (m_uniqueMeshes.TryGetValue(key, out mesh)) 730 {
723 return mesh; 731 key = primShape.GetMeshKey(size, lod);
732 if (m_uniqueMeshes.TryGetValue(key, out mesh))
733 return mesh;
734 }
724 735
725 if (size.X < 0.01f) size.X = 0.01f; 736 if (size.X < 0.01f) size.X = 0.01f;
726 if (size.Y < 0.01f) size.Y = 0.01f; 737 if (size.Y < 0.01f) size.Y = 0.01f;
@@ -743,7 +754,10 @@ namespace OpenSim.Region.Physics.Meshing
743 // trim the vertex and triangle lists to free up memory 754 // trim the vertex and triangle lists to free up memory
744 mesh.TrimExcess(); 755 mesh.TrimExcess();
745 756
746 m_uniqueMeshes.Add(key, mesh); 757 if (shouldCache)
758 {
759 m_uniqueMeshes.Add(key, mesh);
760 }
747 } 761 }
748 762
749 return mesh; 763 return mesh;
diff --git a/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs b/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs
index a59f63f..d09aa62 100644
--- a/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs
+++ b/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs
@@ -3367,6 +3367,11 @@ Console.WriteLine(" JointCreateFixed");
3367 _pbs.SculptData = new byte[asset.Data.Length]; 3367 _pbs.SculptData = new byte[asset.Data.Length];
3368 asset.Data.CopyTo(_pbs.SculptData, 0); 3368 asset.Data.CopyTo(_pbs.SculptData, 0);
3369// m_assetFailed = false; 3369// m_assetFailed = false;
3370
3371// m_log.DebugFormat(
3372// "[ODE PRIM]: Received mesh/sculpt data asset {0} with {1} bytes for {2} at {3} in {4}",
3373// _pbs.SculptTexture, _pbs.SculptData.Length, Name, _position, _parent_scene.Name);
3374
3370 m_taintshape = true; 3375 m_taintshape = true;
3371 _parent_scene.AddPhysicsActorTaint(this); 3376 _parent_scene.AddPhysicsActorTaint(this);
3372 } 3377 }
diff --git a/OpenSim/Region/Physics/OdePlugin/Tests/ODETestClass.cs b/OpenSim/Region/Physics/OdePlugin/Tests/ODETestClass.cs
index cbc6b95..16404c6 100644
--- a/OpenSim/Region/Physics/OdePlugin/Tests/ODETestClass.cs
+++ b/OpenSim/Region/Physics/OdePlugin/Tests/ODETestClass.cs
@@ -32,13 +32,14 @@ using OpenMetaverse;
32using OpenSim.Framework; 32using OpenSim.Framework;
33using OpenSim.Region.Physics.Manager; 33using OpenSim.Region.Physics.Manager;
34using OpenSim.Region.Physics.OdePlugin; 34using OpenSim.Region.Physics.OdePlugin;
35using OpenSim.Tests.Common;
35using log4net; 36using log4net;
36using System.Reflection; 37using System.Reflection;
37 38
38namespace OpenSim.Region.Physics.OdePlugin.Tests 39namespace OpenSim.Region.Physics.OdePlugin.Tests
39{ 40{
40 [TestFixture] 41 [TestFixture]
41 public class ODETestClass 42 public class ODETestClass : OpenSimTestCase
42 { 43 {
43 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 44 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
44 45
diff --git a/OpenSim/Region/Physics/UbitMeshing/Meshmerizer.cs b/OpenSim/Region/Physics/UbitMeshing/Meshmerizer.cs
index 6e1a105..00cbfbd 100644
--- a/OpenSim/Region/Physics/UbitMeshing/Meshmerizer.cs
+++ b/OpenSim/Region/Physics/UbitMeshing/Meshmerizer.cs
@@ -1031,12 +1031,12 @@ namespace OpenSim.Region.Physics.Meshing
1031 1031
1032 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod) 1032 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod)
1033 { 1033 {
1034 return CreateMesh(primName, primShape, size, lod, false,false,false); 1034 return CreateMesh(primName, primShape, size, lod, false,false,false,false);
1035 } 1035 }
1036 1036
1037 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical) 1037 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical)
1038 { 1038 {
1039 return CreateMesh(primName, primShape, size, lod, false,false,false); 1039 return CreateMesh(primName, primShape, size, lod, false,false,false,false);
1040 } 1040 }
1041 1041
1042 public IMesh GetMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool convex) 1042 public IMesh GetMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool convex)
@@ -1080,7 +1080,7 @@ namespace OpenSim.Region.Physics.Meshing
1080 1080
1081 private static Vector3 m_MeshUnitSize = new Vector3(1.0f, 1.0f, 1.0f); 1081 private static Vector3 m_MeshUnitSize = new Vector3(1.0f, 1.0f, 1.0f);
1082 1082
1083 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool convex, bool forOde) 1083 public IMesh CreateMesh(String primName, PrimitiveBaseShape primShape, Vector3 size, float lod, bool isPhysical, bool shouldCache, bool convex, bool forOde)
1084 { 1084 {
1085#if SPAM 1085#if SPAM
1086 m_log.DebugFormat("[MESH]: Creating mesh for {0}", primName); 1086 m_log.DebugFormat("[MESH]: Creating mesh for {0}", primName);
diff --git a/OpenSim/Region/Physics/UbitOdePlugin/ODEMeshWorker.cs b/OpenSim/Region/Physics/UbitOdePlugin/ODEMeshWorker.cs
index 5030cec..0df71eb 100644
--- a/OpenSim/Region/Physics/UbitOdePlugin/ODEMeshWorker.cs
+++ b/OpenSim/Region/Physics/UbitOdePlugin/ODEMeshWorker.cs
@@ -448,7 +448,7 @@ namespace OpenSim.Region.Physics.OdePlugin
448 else 448 else
449 { 449 {
450 repData.meshState = MeshState.needMesh; 450 repData.meshState = MeshState.needMesh;
451 mesh = m_mesher.CreateMesh(actor.Name, pbs, size, clod, true, convex, true); 451 mesh = m_mesher.CreateMesh(actor.Name, pbs, size, clod, true, false, convex, true);
452 if (mesh == null) 452 if (mesh == null)
453 { 453 {
454 repData.meshState = MeshState.MeshFailed; 454 repData.meshState = MeshState.MeshFailed;
@@ -513,7 +513,7 @@ namespace OpenSim.Region.Physics.OdePlugin
513 clod = (int)LevelOfDetail.Low; 513 clod = (int)LevelOfDetail.Low;
514 } 514 }
515 515
516 mesh = m_mesher.CreateMesh(actor.Name, pbs, size, clod, true, convex, true); 516 mesh = m_mesher.CreateMesh(actor.Name, pbs, size, clod, true, false, convex, true);
517 517
518 if (mesh == null) 518 if (mesh == null)
519 { 519 {
@@ -929,4 +929,4 @@ namespace OpenSim.Region.Physics.OdePlugin
929 repData.actor.Name); 929 repData.actor.Name);
930 } 930 }
931 } 931 }
932} \ No newline at end of file 932}
diff --git a/OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs b/OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs
index b04f6b6..2f5b526 100644
--- a/OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs
+++ b/OpenSim/Region/ScriptEngine/Interfaces/IScriptInstance.cs
@@ -59,6 +59,18 @@ namespace OpenSim.Region.ScriptEngine.Interfaces
59 public interface IScriptInstance 59 public interface IScriptInstance
60 { 60 {
61 /// <summary> 61 /// <summary>
62 /// Debug level for this script instance.
63 /// </summary>
64 /// <remarks>
65 /// Level == 0, no extra data is logged.
66 /// Level >= 1, state changes are logged.
67 /// Level >= 2, event firing is logged.
68 /// <value>
69 /// The debug level.
70 /// </value>
71 int DebugLevel { get; set; }
72
73 /// <summary>
62 /// Is the script currently running? 74 /// Is the script currently running?
63 /// </summary> 75 /// </summary>
64 bool Running { get; set; } 76 bool Running { get; set; }
@@ -114,6 +126,16 @@ namespace OpenSim.Region.ScriptEngine.Interfaces
114 UUID AssetID { get; } 126 UUID AssetID { get; }
115 Queue EventQueue { get; } 127 Queue EventQueue { get; }
116 128
129 /// <summary>
130 /// Number of events queued for processing.
131 /// </summary>
132 long EventsQueued { get; }
133
134 /// <summary>
135 /// Number of events processed by this script instance.
136 /// </summary>
137 long EventsProcessed { get; }
138
117 void ClearQueue(); 139 void ClearQueue();
118 int StartParam { get; set; } 140 int StartParam { get; set; }
119 141
@@ -125,7 +147,13 @@ namespace OpenSim.Region.ScriptEngine.Interfaces
125 /// <summary> 147 /// <summary>
126 /// Stop the script instance. 148 /// Stop the script instance.
127 /// </summary> 149 /// </summary>
150 /// <remarks>
151 /// This must not be called by a thread that is in the process of handling an event for this script. Otherwise
152 /// there is a danger that it will self-abort and not complete the reset.
153 /// </remarks>
128 /// <param name="timeout"></param> 154 /// <param name="timeout"></param>
155 /// How many milliseconds we will wait for an existing script event to finish before
156 /// forcibly aborting that event.
129 /// <returns>true if the script was successfully stopped, false otherwise</returns> 157 /// <returns>true if the script was successfully stopped, false otherwise</returns>
130 bool Stop(int timeout); 158 bool Stop(int timeout);
131 159
@@ -147,8 +175,31 @@ namespace OpenSim.Region.ScriptEngine.Interfaces
147 object EventProcessor(); 175 object EventProcessor();
148 176
149 int EventTime(); 177 int EventTime();
150 void ResetScript(); 178
179 /// <summary>
180 /// Reset the script.
181 /// </summary>
182 /// <remarks>
183 /// This must not be called by a thread that is in the process of handling an event for this script. Otherwise
184 /// there is a danger that it will self-abort and not complete the reset. Such a thread must call
185 /// ApiResetScript() instead.
186 /// </remarks>
187 /// <param name='timeout'>
188 /// How many milliseconds we will wait for an existing script event to finish before
189 /// forcibly aborting that event prior to script reset.
190 /// </param>
191 void ResetScript(int timeout);
192
193 /// <summary>
194 /// Reset the script.
195 /// </summary>
196 /// <remarks>
197 /// This must not be called by any thread other than the one executing the scripts current event. This is
198 /// because there is no wait or abort logic if another thread is in the middle of processing a script event.
199 /// Such an external thread should use ResetScript() instead.
200 /// </remarks>
151 void ApiResetScript(); 201 void ApiResetScript();
202
152 Dictionary<string, object> GetVars(); 203 Dictionary<string, object> GetVars();
153 void SetVars(Dictionary<string, object> vars); 204 void SetVars(Dictionary<string, object> vars);
154 DetectParams GetDetectParams(int idx); 205 DetectParams GetDetectParams(int idx);
diff --git a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs
index 7ff30ca..3a9d0ff 100644
--- a/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/Api/Implementation/LSL_Api.cs
@@ -7334,6 +7334,13 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
7334 public void llCloseRemoteDataChannel(string channel) 7334 public void llCloseRemoteDataChannel(string channel)
7335 { 7335 {
7336 m_host.AddScriptLPS(1); 7336 m_host.AddScriptLPS(1);
7337
7338 IXmlRpcRouter xmlRpcRouter = m_ScriptEngine.World.RequestModuleInterface<IXmlRpcRouter>();
7339 if (xmlRpcRouter != null)
7340 {
7341 xmlRpcRouter.UnRegisterReceiver(channel, m_item.ItemID);
7342 }
7343
7337 IXMLRPC xmlrpcMod = m_ScriptEngine.World.RequestModuleInterface<IXMLRPC>(); 7344 IXMLRPC xmlrpcMod = m_ScriptEngine.World.RequestModuleInterface<IXMLRPC>();
7338 xmlrpcMod.CloseXMLRPCChannel((UUID)channel); 7345 xmlrpcMod.CloseXMLRPCChannel((UUID)channel);
7339 ScriptSleep(1000); 7346 ScriptSleep(1000);
diff --git a/OpenSim/Region/ScriptEngine/Shared/CodeTools/Tests/CSCodeGeneratorTest.cs b/OpenSim/Region/ScriptEngine/Shared/CodeTools/Tests/CSCodeGeneratorTest.cs
index 7763619..77e087c 100644
--- a/OpenSim/Region/ScriptEngine/Shared/CodeTools/Tests/CSCodeGeneratorTest.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/CodeTools/Tests/CSCodeGeneratorTest.cs
@@ -39,7 +39,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools.Tests
39 /// The generated C# code is compared against the expected C# code. 39 /// The generated C# code is compared against the expected C# code.
40 /// </summary> 40 /// </summary>
41 [TestFixture] 41 [TestFixture]
42 public class CSCodeGeneratorTest 42 public class CSCodeGeneratorTest : OpenSimTestCase
43 { 43 {
44 [Test] 44 [Test]
45 public void TestDefaultState() 45 public void TestDefaultState()
diff --git a/OpenSim/Region/ScriptEngine/Shared/CodeTools/Tests/CompilerTest.cs b/OpenSim/Region/ScriptEngine/Shared/CodeTools/Tests/CompilerTest.cs
index 1fa6954..05a8756 100644
--- a/OpenSim/Region/ScriptEngine/Shared/CodeTools/Tests/CompilerTest.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/CodeTools/Tests/CompilerTest.cs
@@ -41,7 +41,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.CodeTools.Tests
41 /// the LSL source. 41 /// the LSL source.
42 /// </summary> 42 /// </summary>
43 [TestFixture] 43 [TestFixture]
44 public class CompilerTest 44 public class CompilerTest : OpenSimTestCase
45 { 45 {
46 private string m_testDir; 46 private string m_testDir;
47 private CSharpCodeProvider m_CSCodeProvider; 47 private CSharpCodeProvider m_CSCodeProvider;
diff --git a/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs b/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs
index 771db0c..ff4d130 100644
--- a/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/Instance/ScriptInstance.cs
@@ -94,6 +94,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
94 private UUID m_CurrentStateHash; 94 private UUID m_CurrentStateHash;
95 private UUID m_RegionID; 95 private UUID m_RegionID;
96 96
97 public int DebugLevel { get; set; }
98
97 public Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> LineMap { get; set; } 99 public Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> LineMap { get; set; }
98 100
99 private Dictionary<string,IScriptApi> m_Apis = new Dictionary<string,IScriptApi>(); 101 private Dictionary<string,IScriptApi> m_Apis = new Dictionary<string,IScriptApi>();
@@ -174,6 +176,17 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
174 176
175 public Queue EventQueue { get; private set; } 177 public Queue EventQueue { get; private set; }
176 178
179 public long EventsQueued
180 {
181 get
182 {
183 lock (EventQueue)
184 return EventQueue.Count;
185 }
186 }
187
188 public long EventsProcessed { get; private set; }
189
177 public int StartParam { get; set; } 190 public int StartParam { get; set; }
178 191
179 public TaskInventoryItem ScriptTask { get; private set; } 192 public TaskInventoryItem ScriptTask { get; private set; }
@@ -538,9 +551,9 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
538 // forcibly abort the work item (this aborts the underlying thread). 551 // forcibly abort the work item (this aborts the underlying thread).
539 if (!m_InSelfDelete) 552 if (!m_InSelfDelete)
540 { 553 {
541// m_log.ErrorFormat( 554 m_log.DebugFormat(
542// "[SCRIPT INSTANCE]: Aborting script {0} {1} in prim {2} {3} {4} {5}", 555 "[SCRIPT INSTANCE]: Aborting unstopped script {0} {1} in prim {2}, localID {3}, timeout was {4} ms",
543// ScriptName, ItemID, PrimName, ObjectID, m_InSelfDelete, DateTime.Now.Ticks); 556 ScriptName, ItemID, PrimName, LocalID, timeout);
544 557
545 workItem.Abort(); 558 workItem.Abort();
546 } 559 }
@@ -696,19 +709,41 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
696 { 709 {
697 710
698// m_log.DebugFormat("[XEngine]: Processing event {0} for {1}", data.EventName, this); 711// m_log.DebugFormat("[XEngine]: Processing event {0} for {1}", data.EventName, this);
712 SceneObjectPart part = Engine.World.GetSceneObjectPart(LocalID);
713
714 if (DebugLevel >= 2)
715 m_log.DebugFormat(
716 "[SCRIPT INSTANCE]: Processing event {0} for {1}/{2}({3})/{4}({5}) @ {6}/{7}",
717 data.EventName,
718 ScriptName,
719 part.Name,
720 part.LocalId,
721 part.ParentGroup.Name,
722 part.ParentGroup.UUID,
723 part.AbsolutePosition,
724 part.ParentGroup.Scene.Name);
699 725
700 m_DetectParams = data.DetectParams; 726 m_DetectParams = data.DetectParams;
701 727
702 if (data.EventName == "state") // Hardcoded state change 728 if (data.EventName == "state") // Hardcoded state change
703 { 729 {
704 // m_log.DebugFormat("[Script] Script {0}.{1} state set to {2}",
705 // PrimName, ScriptName, data.Params[0].ToString());
706 State = data.Params[0].ToString(); 730 State = data.Params[0].ToString();
731
732 if (DebugLevel >= 1)
733 m_log.DebugFormat(
734 "[SCRIPT INSTANCE]: Changing state to {0} for {1}/{2}({3})/{4}({5}) @ {6}/{7}",
735 State,
736 ScriptName,
737 part.Name,
738 part.LocalId,
739 part.ParentGroup.Name,
740 part.ParentGroup.UUID,
741 part.AbsolutePosition,
742 part.ParentGroup.Scene.Name);
743
707 AsyncCommandManager.RemoveScript(Engine, 744 AsyncCommandManager.RemoveScript(Engine,
708 LocalID, ItemID); 745 LocalID, ItemID);
709 746
710 SceneObjectPart part = Engine.World.GetSceneObjectPart(
711 LocalID);
712 if (part != null) 747 if (part != null)
713 { 748 {
714 part.SetScriptEvents(ItemID, 749 part.SetScriptEvents(ItemID,
@@ -720,8 +755,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
720 if (Engine.World.PipeEventsForScript(LocalID) || 755 if (Engine.World.PipeEventsForScript(LocalID) ||
721 data.EventName == "control") // Don't freeze avies! 756 data.EventName == "control") // Don't freeze avies!
722 { 757 {
723 SceneObjectPart part = Engine.World.GetSceneObjectPart(
724 LocalID);
725 // m_log.DebugFormat("[Script] Delivered event {2} in state {3} to {0}.{1}", 758 // m_log.DebugFormat("[Script] Delivered event {2} in state {3} to {0}.{1}",
726 // PrimName, ScriptName, data.EventName, State); 759 // PrimName, ScriptName, data.EventName, State);
727 760
@@ -776,6 +809,17 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
776 ChatTypeEnum.DebugChannel, 2147483647, 809 ChatTypeEnum.DebugChannel, 2147483647,
777 part.AbsolutePosition, 810 part.AbsolutePosition,
778 part.Name, part.UUID, false); 811 part.Name, part.UUID, false);
812
813
814 m_log.DebugFormat(
815 "[SCRIPT INSTANCE]: Runtime error in script {0}, part {1} {2} at {3} in {4}, displayed error {5}, actual exception {6}",
816 ScriptName,
817 PrimName,
818 part.UUID,
819 part.AbsolutePosition,
820 part.ParentGroup.Scene.Name,
821 text.Replace("\n", "\\n"),
822 e.InnerException);
779 } 823 }
780 catch (Exception) 824 catch (Exception)
781 { 825 {
@@ -810,6 +854,8 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
810 // script engine to run the next event. 854 // script engine to run the next event.
811 lock (EventQueue) 855 lock (EventQueue)
812 { 856 {
857 EventsProcessed++;
858
813 if (EventQueue.Count > 0 && Running && !ShuttingDown) 859 if (EventQueue.Count > 0 && Running && !ShuttingDown)
814 { 860 {
815 m_CurrentWorkItem = Engine.QueueEventHandler(this); 861 m_CurrentWorkItem = Engine.QueueEventHandler(this);
@@ -834,7 +880,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
834 return (DateTime.Now - m_EventStart).Seconds; 880 return (DateTime.Now - m_EventStart).Seconds;
835 } 881 }
836 882
837 public void ResetScript() 883 public void ResetScript(int timeout)
838 { 884 {
839 if (m_Script == null) 885 if (m_Script == null)
840 return; 886 return;
@@ -844,7 +890,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
844 RemoveState(); 890 RemoveState();
845 ReleaseControls(); 891 ReleaseControls();
846 892
847 Stop(0); 893 Stop(timeout);
848 SceneObjectPart part = Engine.World.GetSceneObjectPart(LocalID); 894 SceneObjectPart part = Engine.World.GetSceneObjectPart(LocalID);
849 part.Inventory.GetInventoryItem(ItemID).PermsMask = 0; 895 part.Inventory.GetInventoryItem(ItemID).PermsMask = 0;
850 part.Inventory.GetInventoryItem(ItemID).PermsGranter = UUID.Zero; 896 part.Inventory.GetInventoryItem(ItemID).PermsGranter = UUID.Zero;
@@ -1015,7 +1061,6 @@ namespace OpenSim.Region.ScriptEngine.Shared.Instance
1015 "({0}): {1}", scriptLine - 1, 1061 "({0}): {1}", scriptLine - 1,
1016 e.InnerException.Message); 1062 e.InnerException.Message);
1017 1063
1018 System.Console.WriteLine(e.ToString()+"\n");
1019 return message; 1064 return message;
1020 } 1065 }
1021 } 1066 }
diff --git a/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_ApiInventoryTests.cs b/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_ApiInventoryTests.cs
index c73e22f..cb7291a 100644
--- a/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_ApiInventoryTests.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_ApiInventoryTests.cs
@@ -51,14 +51,16 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
51 /// Tests for inventory functions in LSL 51 /// Tests for inventory functions in LSL
52 /// </summary> 52 /// </summary>
53 [TestFixture] 53 [TestFixture]
54 public class LSL_ApiInventoryTests 54 public class LSL_ApiInventoryTests : OpenSimTestCase
55 { 55 {
56 protected Scene m_scene; 56 protected Scene m_scene;
57 protected XEngine.XEngine m_engine; 57 protected XEngine.XEngine m_engine;
58 58
59 [SetUp] 59 [SetUp]
60 public void SetUp() 60 public override void SetUp()
61 { 61 {
62 base.SetUp();
63
62 IConfigSource initConfigSource = new IniConfigSource(); 64 IConfigSource initConfigSource = new IniConfigSource();
63 IConfig config = initConfigSource.AddConfig("XEngine"); 65 IConfig config = initConfigSource.AddConfig("XEngine");
64 config.Set("Enabled", "true"); 66 config.Set("Enabled", "true");
diff --git a/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_ApiLinkingTests.cs b/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_ApiLinkingTests.cs
index 2565ae7..d9b17d7 100644
--- a/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_ApiLinkingTests.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_ApiLinkingTests.cs
@@ -56,14 +56,16 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
56 /// OpenSim.Region.Framework.Scenes.Tests.SceneObjectLinkingTests. 56 /// OpenSim.Region.Framework.Scenes.Tests.SceneObjectLinkingTests.
57 /// </remarks> 57 /// </remarks>
58 [TestFixture] 58 [TestFixture]
59 public class LSL_ApiLinkingTests 59 public class LSL_ApiLinkingTests : OpenSimTestCase
60 { 60 {
61 protected Scene m_scene; 61 protected Scene m_scene;
62 protected XEngine.XEngine m_engine; 62 protected XEngine.XEngine m_engine;
63 63
64 [SetUp] 64 [SetUp]
65 public void SetUp() 65 public override void SetUp()
66 { 66 {
67 base.SetUp();
68
67 IConfigSource initConfigSource = new IniConfigSource(); 69 IConfigSource initConfigSource = new IniConfigSource();
68 IConfig config = initConfigSource.AddConfig("XEngine"); 70 IConfig config = initConfigSource.AddConfig("XEngine");
69 config.Set("Enabled", "true"); 71 config.Set("Enabled", "true");
diff --git a/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_ApiListTests.cs b/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_ApiListTests.cs
index dd23be8..98017d8 100644
--- a/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_ApiListTests.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_ApiListTests.cs
@@ -46,13 +46,15 @@ using LSL_String = OpenSim.Region.ScriptEngine.Shared.LSL_Types.LSLString;
46namespace OpenSim.Region.ScriptEngine.Shared.Tests 46namespace OpenSim.Region.ScriptEngine.Shared.Tests
47{ 47{
48 [TestFixture] 48 [TestFixture]
49 public class LSL_ApiListTests 49 public class LSL_ApiListTests : OpenSimTestCase
50 { 50 {
51 private LSL_Api m_lslApi; 51 private LSL_Api m_lslApi;
52 52
53 [SetUp] 53 [SetUp]
54 public void SetUp() 54 public override void SetUp()
55 { 55 {
56 base.SetUp();
57
56 IConfigSource initConfigSource = new IniConfigSource(); 58 IConfigSource initConfigSource = new IniConfigSource();
57 IConfig config = initConfigSource.AddConfig("XEngine"); 59 IConfig config = initConfigSource.AddConfig("XEngine");
58 config.Set("Enabled", "true"); 60 config.Set("Enabled", "true");
diff --git a/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_TypesTestLSLFloat.cs b/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_TypesTestLSLFloat.cs
index 3ed2562..c8c7f82 100644
--- a/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_TypesTestLSLFloat.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_TypesTestLSLFloat.cs
@@ -33,7 +33,7 @@ using OpenSim.Region.ScriptEngine.Shared;
33namespace OpenSim.Region.ScriptEngine.Shared.Tests 33namespace OpenSim.Region.ScriptEngine.Shared.Tests
34{ 34{
35 [TestFixture] 35 [TestFixture]
36 public class LSL_TypesTestLSLFloat 36 public class LSL_TypesTestLSLFloat : OpenSimTestCase
37 { 37 {
38 // Used for testing equality of two floats. 38 // Used for testing equality of two floats.
39 private double _lowPrecisionTolerance = 0.000001; 39 private double _lowPrecisionTolerance = 0.000001;
diff --git a/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_TypesTestLSLInteger.cs b/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_TypesTestLSLInteger.cs
index 8d1169a..c664108 100644
--- a/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_TypesTestLSLInteger.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_TypesTestLSLInteger.cs
@@ -33,7 +33,7 @@ using OpenSim.Region.ScriptEngine.Shared;
33namespace OpenSim.Region.ScriptEngine.Shared.Tests 33namespace OpenSim.Region.ScriptEngine.Shared.Tests
34{ 34{
35 [TestFixture] 35 [TestFixture]
36 public class LSL_TypesTestLSLInteger 36 public class LSL_TypesTestLSLInteger : OpenSimTestCase
37 { 37 {
38 private Dictionary<double, int> m_doubleIntSet; 38 private Dictionary<double, int> m_doubleIntSet;
39 private Dictionary<string, int> m_stringIntSet; 39 private Dictionary<string, int> m_stringIntSet;
diff --git a/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_TypesTestLSLString.cs b/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_TypesTestLSLString.cs
index c4ca1a8..8550f2d 100644
--- a/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_TypesTestLSLString.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_TypesTestLSLString.cs
@@ -33,7 +33,7 @@ using OpenSim.Region.ScriptEngine.Shared;
33namespace OpenSim.Region.ScriptEngine.Shared.Tests 33namespace OpenSim.Region.ScriptEngine.Shared.Tests
34{ 34{
35 [TestFixture] 35 [TestFixture]
36 public class LSL_TypesTestLSLString 36 public class LSL_TypesTestLSLString : OpenSimTestCase
37 { 37 {
38 private Dictionary<double, string> m_doubleStringSet; 38 private Dictionary<double, string> m_doubleStringSet;
39 39
diff --git a/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_TypesTestList.cs b/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_TypesTestList.cs
index b81225f..71b88bc 100644
--- a/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_TypesTestList.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_TypesTestList.cs
@@ -36,7 +36,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
36 /// Tests the LSL_Types.list class. 36 /// Tests the LSL_Types.list class.
37 /// </summary> 37 /// </summary>
38 [TestFixture] 38 [TestFixture]
39 public class LSL_TypesTestList 39 public class LSL_TypesTestList : OpenSimTestCase
40 { 40 {
41 /// <summary> 41 /// <summary>
42 /// Tests concatenating a string to a list. 42 /// Tests concatenating a string to a list.
diff --git a/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_TypesTestVector3.cs b/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_TypesTestVector3.cs
index ebf8001..0c838af 100644
--- a/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_TypesTestVector3.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/Tests/LSL_TypesTestVector3.cs
@@ -36,7 +36,7 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
36 /// Tests for Vector3 36 /// Tests for Vector3
37 /// </summary> 37 /// </summary>
38 [TestFixture] 38 [TestFixture]
39 public class LSL_TypesTestVector3 39 public class LSL_TypesTestVector3 : OpenSimTestCase
40 { 40 {
41 [Test] 41 [Test]
42 public void TestDotProduct() 42 public void TestDotProduct()
diff --git a/OpenSim/Region/ScriptEngine/Shared/Tests/OSSL_ApiAppearanceTest.cs b/OpenSim/Region/ScriptEngine/Shared/Tests/OSSL_ApiAppearanceTest.cs
index c401794..1381d2b 100644
--- a/OpenSim/Region/ScriptEngine/Shared/Tests/OSSL_ApiAppearanceTest.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/Tests/OSSL_ApiAppearanceTest.cs
@@ -51,14 +51,16 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
51 /// Tests for OSSL_Api 51 /// Tests for OSSL_Api
52 /// </summary> 52 /// </summary>
53 [TestFixture] 53 [TestFixture]
54 public class OSSL_ApiAppearanceTest 54 public class OSSL_ApiAppearanceTest : OpenSimTestCase
55 { 55 {
56 protected Scene m_scene; 56 protected Scene m_scene;
57 protected XEngine.XEngine m_engine; 57 protected XEngine.XEngine m_engine;
58 58
59 [SetUp] 59 [SetUp]
60 public void SetUp() 60 public override void SetUp()
61 { 61 {
62 base.SetUp();
63
62 IConfigSource initConfigSource = new IniConfigSource(); 64 IConfigSource initConfigSource = new IniConfigSource();
63 IConfig config = initConfigSource.AddConfig("XEngine"); 65 IConfig config = initConfigSource.AddConfig("XEngine");
64 config.Set("Enabled", "true"); 66 config.Set("Enabled", "true");
diff --git a/OpenSim/Region/ScriptEngine/Shared/Tests/OSSL_ApiNpcTests.cs b/OpenSim/Region/ScriptEngine/Shared/Tests/OSSL_ApiNpcTests.cs
index b49bcc2..d6c82f1 100644
--- a/OpenSim/Region/ScriptEngine/Shared/Tests/OSSL_ApiNpcTests.cs
+++ b/OpenSim/Region/ScriptEngine/Shared/Tests/OSSL_ApiNpcTests.cs
@@ -127,12 +127,10 @@ namespace OpenSim.Region.ScriptEngine.Shared.Tests
127 OSSL_Api osslApi = new OSSL_Api(); 127 OSSL_Api osslApi = new OSSL_Api();
128 osslApi.Initialize(m_engine, so.RootPart, null); 128 osslApi.Initialize(m_engine, so.RootPart, null);
129 129
130 string npcRaw;
131 bool gotExpectedException = false; 130 bool gotExpectedException = false;
132 try 131 try
133 { 132 {
134 npcRaw 133 osslApi.osNpcCreate("Jane", "Doe", new LSL_Types.Vector3(128, 128, 128), "not existing notecard name");
135 = osslApi.osNpcCreate("Jane", "Doe", new LSL_Types.Vector3(128, 128, 128), "not existing notecard name");
136 } 134 }
137 catch (ScriptException) 135 catch (ScriptException)
138 { 136 {
diff --git a/OpenSim/Region/ScriptEngine/XEngine/Tests/XEngineTest.cs b/OpenSim/Region/ScriptEngine/XEngine/Tests/XEngineTest.cs
index f331658..5abfe9a 100644
--- a/OpenSim/Region/ScriptEngine/XEngine/Tests/XEngineTest.cs
+++ b/OpenSim/Region/ScriptEngine/XEngine/Tests/XEngineTest.cs
@@ -44,7 +44,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine.Tests
44 /// XEngine tests. 44 /// XEngine tests.
45 /// </summary> 45 /// </summary>
46 [TestFixture] 46 [TestFixture]
47 public class XEngineTest 47 public class XEngineTest : OpenSimTestCase
48 { 48 {
49 private TestScene m_scene; 49 private TestScene m_scene;
50 private XEngine m_xEngine; 50 private XEngine m_xEngine;
diff --git a/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs b/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs
index 9f05666..0bd9a06 100644
--- a/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs
+++ b/OpenSim/Region/ScriptEngine/XEngine/XEngine.cs
@@ -31,6 +31,7 @@ using System.Collections.Generic;
31using System.Diagnostics; //for [DebuggerNonUserCode] 31using System.Diagnostics; //for [DebuggerNonUserCode]
32using System.Globalization; 32using System.Globalization;
33using System.IO; 33using System.IO;
34using System.Linq;
34using System.Reflection; 35using System.Reflection;
35using System.Security; 36using System.Security;
36using System.Security.Policy; 37using System.Security.Policy;
@@ -107,6 +108,24 @@ namespace OpenSim.Region.ScriptEngine.XEngine
107 private IXmlRpcRouter m_XmlRpcRouter; 108 private IXmlRpcRouter m_XmlRpcRouter;
108 private int m_EventLimit; 109 private int m_EventLimit;
109 private bool m_KillTimedOutScripts; 110 private bool m_KillTimedOutScripts;
111
112 /// <summary>
113 /// Number of milliseconds we will wait for a script event to complete on script stop before we forcibly abort
114 /// its thread.
115 /// </summary>
116 /// <remarks>
117 /// It appears that if a script thread is aborted whilst it is holding ReaderWriterLockSlim (possibly the write
118 /// lock) then the lock is not properly released. This causes mono 2.6, 2.10 and possibly
119 /// later to crash, sometimes with symptoms such as a leap to 100% script usage and a vm thead dump showing
120 /// all threads waiting on release of ReaderWriterLockSlim write thread which none of the threads listed
121 /// actually hold.
122 ///
123 /// Pausing for event completion reduces the risk of this happening. However, it may be that aborting threads
124 /// is not a mono issue per se but rather a risky activity in itself in an AppDomain that is not immediately
125 /// shutting down.
126 /// </remarks>
127 private int m_WaitForEventCompletionOnScriptStop = 1000;
128
110 private string m_ScriptEnginesPath = null; 129 private string m_ScriptEnginesPath = null;
111 130
112 private ExpiringCache<UUID, bool> m_runFlags = new ExpiringCache<UUID, bool>(); 131 private ExpiringCache<UUID, bool> m_runFlags = new ExpiringCache<UUID, bool>();
@@ -316,6 +335,9 @@ namespace OpenSim.Region.ScriptEngine.XEngine
316 m_EventLimit = m_ScriptConfig.GetInt("EventLimit", 30); 335 m_EventLimit = m_ScriptConfig.GetInt("EventLimit", 30);
317 m_KillTimedOutScripts = m_ScriptConfig.GetBoolean("KillTimedOutScripts", false); 336 m_KillTimedOutScripts = m_ScriptConfig.GetBoolean("KillTimedOutScripts", false);
318 m_SaveTime = m_ScriptConfig.GetInt("SaveInterval", 120) * 1000; 337 m_SaveTime = m_ScriptConfig.GetInt("SaveInterval", 120) * 1000;
338 m_WaitForEventCompletionOnScriptStop
339 = m_ScriptConfig.GetInt("WaitForEventCompletionOnScriptStop", m_WaitForEventCompletionOnScriptStop);
340
319 m_ScriptEnginesPath = m_ScriptConfig.GetString("ScriptEnginesPath", "ScriptEngines"); 341 m_ScriptEnginesPath = m_ScriptConfig.GetString("ScriptEnginesPath", "ScriptEngines");
320 342
321 m_Prio = ThreadPriority.BelowNormal; 343 m_Prio = ThreadPriority.BelowNormal;
@@ -371,7 +393,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine
371 393
372 MainConsole.Instance.Commands.AddCommand( 394 MainConsole.Instance.Commands.AddCommand(
373 "Scripts", false, "scripts show", "scripts show [<script-item-uuid>]", "Show script information", 395 "Scripts", false, "scripts show", "scripts show [<script-item-uuid>]", "Show script information",
374 "Show information on all scripts known to the script engine." 396 "Show information on all scripts known to the script engine.\n"
375 + "If a <script-item-uuid> is given then only information on that script will be shown.", 397 + "If a <script-item-uuid> is given then only information on that script will be shown.",
376 HandleShowScripts); 398 HandleShowScripts);
377 399
@@ -390,22 +412,30 @@ namespace OpenSim.Region.ScriptEngine.XEngine
390 MainConsole.Instance.Commands.AddCommand( 412 MainConsole.Instance.Commands.AddCommand(
391 "Scripts", false, "scripts resume", "scripts resume [<script-item-uuid>]", "Resumes all suspended scripts", 413 "Scripts", false, "scripts resume", "scripts resume [<script-item-uuid>]", "Resumes all suspended scripts",
392 "Resumes all currently suspended scripts.\n" 414 "Resumes all currently suspended scripts.\n"
393 + "Resumed scripts will process all events accumulated whilst suspended." 415 + "Resumed scripts will process all events accumulated whilst suspended.\n"
394 + "If a <script-item-uuid> is given then only that script will be resumed. Otherwise, all suitable scripts are resumed.", 416 + "If a <script-item-uuid> is given then only that script will be resumed. Otherwise, all suitable scripts are resumed.",
395 (module, cmdparams) => HandleScriptsAction(cmdparams, HandleResumeScript)); 417 (module, cmdparams) => HandleScriptsAction(cmdparams, HandleResumeScript));
396 418
397 MainConsole.Instance.Commands.AddCommand( 419 MainConsole.Instance.Commands.AddCommand(
398 "Scripts", false, "scripts stop", "scripts stop [<script-item-uuid>]", "Stops all running scripts", 420 "Scripts", false, "scripts stop", "scripts stop [<script-item-uuid>]", "Stops all running scripts",
399 "Stops all running scripts." 421 "Stops all running scripts.\n"
400 + "If a <script-item-uuid> is given then only that script will be stopped. Otherwise, all suitable scripts are stopped.", 422 + "If a <script-item-uuid> is given then only that script will be stopped. Otherwise, all suitable scripts are stopped.",
401 (module, cmdparams) => HandleScriptsAction(cmdparams, HandleStopScript)); 423 (module, cmdparams) => HandleScriptsAction(cmdparams, HandleStopScript));
402 424
403 MainConsole.Instance.Commands.AddCommand( 425 MainConsole.Instance.Commands.AddCommand(
404 "Scripts", false, "scripts start", "scripts start [<script-item-uuid>]", "Starts all stopped scripts", 426 "Scripts", false, "scripts start", "scripts start [<script-item-uuid>]", "Starts all stopped scripts",
405 "Starts all stopped scripts." 427 "Starts all stopped scripts.\n"
406 + "If a <script-item-uuid> is given then only that script will be started. Otherwise, all suitable scripts are started.", 428 + "If a <script-item-uuid> is given then only that script will be started. Otherwise, all suitable scripts are started.",
407 (module, cmdparams) => HandleScriptsAction(cmdparams, HandleStartScript)); 429 (module, cmdparams) => HandleScriptsAction(cmdparams, HandleStartScript));
408 430
431 MainConsole.Instance.Commands.AddCommand(
432 "Scripts", false, "debug script log", "debug scripts log <item-id> <log-level>", "Extra debug logging for a script",
433 "Activates or deactivates extra debug logging for the given script.\n"
434 + "Level == 0, deactivate extra debug logging.\n"
435 + "Level >= 1, log state changes.\n"
436 + "Level >= 2, log event invocations.\n",
437 HandleDebugScriptLogCommand);
438
409// MainConsole.Instance.Commands.AddCommand( 439// MainConsole.Instance.Commands.AddCommand(
410// "Debug", false, "debug xengine", "debug xengine [<level>]", 440// "Debug", false, "debug xengine", "debug xengine [<level>]",
411// "Turn on detailed xengine debugging.", 441// "Turn on detailed xengine debugging.",
@@ -414,6 +444,41 @@ namespace OpenSim.Region.ScriptEngine.XEngine
414// HandleDebugLevelCommand); 444// HandleDebugLevelCommand);
415 } 445 }
416 446
447 private void HandleDebugScriptLogCommand(string module, string[] args)
448 {
449 if (!(MainConsole.Instance.ConsoleScene == null || MainConsole.Instance.ConsoleScene == m_Scene))
450 return;
451
452 if (args.Length != 5)
453 {
454 MainConsole.Instance.Output("Usage: debug script log <item-id> <log-level>");
455 return;
456 }
457
458 UUID itemId;
459
460 if (!ConsoleUtil.TryParseConsoleUuid(MainConsole.Instance, args[3], out itemId))
461 return;
462
463 int newLevel;
464
465 if (!ConsoleUtil.TryParseConsoleInt(MainConsole.Instance, args[4], out newLevel))
466 return;
467
468 IScriptInstance si;
469
470 lock (m_Scripts)
471 {
472 // XXX: We can't give the user feedback on a bad item id because this may apply to a different script
473 // engine
474 if (!m_Scripts.TryGetValue(itemId, out si))
475 return;
476 }
477
478 si.DebugLevel = newLevel;
479 MainConsole.Instance.OutputFormat("Set debug level of {0} {1} to {2}", si.ScriptName, si.ItemID, newLevel);
480 }
481
417 /// <summary> 482 /// <summary>
418 /// Change debug level 483 /// Change debug level
419 /// </summary> 484 /// </summary>
@@ -445,9 +510,21 @@ namespace OpenSim.Region.ScriptEngine.XEngine
445 /// </summary> 510 /// </summary>
446 /// <param name="cmdparams"></param> 511 /// <param name="cmdparams"></param>
447 /// <param name="instance"></param> 512 /// <param name="instance"></param>
448 /// <returns>true if we're okay to proceed, false if not.</returns> 513 /// <param name="comparer">Basis on which to sort output. Can be null if no sort needs to take place</param>
449 private void HandleScriptsAction(string[] cmdparams, Action<IScriptInstance> action) 514 private void HandleScriptsAction(string[] cmdparams, Action<IScriptInstance> action)
450 { 515 {
516 HandleScriptsAction<object>(cmdparams, action, null);
517 }
518
519 /// <summary>
520 /// Parse the raw item id into a script instance from the command params if it's present.
521 /// </summary>
522 /// <param name="cmdparams"></param>
523 /// <param name="instance"></param>
524 /// <param name="keySelector">Basis on which to sort output. Can be null if no sort needs to take place</param>
525 private void HandleScriptsAction<TKey>(
526 string[] cmdparams, Action<IScriptInstance> action, Func<IScriptInstance, TKey> keySelector)
527 {
451 if (!(MainConsole.Instance.ConsoleScene == null || MainConsole.Instance.ConsoleScene == m_Scene)) 528 if (!(MainConsole.Instance.ConsoleScene == null || MainConsole.Instance.ConsoleScene == m_Scene))
452 return; 529 return;
453 530
@@ -458,7 +535,12 @@ namespace OpenSim.Region.ScriptEngine.XEngine
458 535
459 if (cmdparams.Length == 2) 536 if (cmdparams.Length == 2)
460 { 537 {
461 foreach (IScriptInstance instance in m_Scripts.Values) 538 IEnumerable<IScriptInstance> scripts = m_Scripts.Values;
539
540 if (keySelector != null)
541 scripts = scripts.OrderBy<IScriptInstance, TKey>(keySelector);
542
543 foreach (IScriptInstance instance in scripts)
462 action(instance); 544 action(instance);
463 545
464 return; 546 return;
@@ -468,7 +550,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine
468 550
469 if (!UUID.TryParse(rawItemId, out itemId)) 551 if (!UUID.TryParse(rawItemId, out itemId))
470 { 552 {
471 MainConsole.Instance.OutputFormat("Error - {0} is not a valid UUID", rawItemId); 553 MainConsole.Instance.OutputFormat("ERROR: {0} is not a valid UUID", rawItemId);
472 return; 554 return;
473 } 555 }
474 556
@@ -505,9 +587,20 @@ namespace OpenSim.Region.ScriptEngine.XEngine
505 StringBuilder sb = new StringBuilder(); 587 StringBuilder sb = new StringBuilder();
506 sb.AppendFormat("Status of XEngine instance for {0}\n", m_Scene.RegionInfo.RegionName); 588 sb.AppendFormat("Status of XEngine instance for {0}\n", m_Scene.RegionInfo.RegionName);
507 589
590 long scriptsLoaded, eventsQueued = 0, eventsProcessed = 0;
591
508 lock (m_Scripts) 592 lock (m_Scripts)
509 sb.AppendFormat("Scripts loaded : {0}\n", m_Scripts.Count); 593 {
594 scriptsLoaded = m_Scripts.Count;
595
596 foreach (IScriptInstance si in m_Scripts.Values)
597 {
598 eventsQueued += si.EventsQueued;
599 eventsProcessed += si.EventsProcessed;
600 }
601 }
510 602
603 sb.AppendFormat("Scripts loaded : {0}\n", scriptsLoaded);
511 sb.AppendFormat("Unique scripts : {0}\n", m_uniqueScripts.Count); 604 sb.AppendFormat("Unique scripts : {0}\n", m_uniqueScripts.Count);
512 sb.AppendFormat("Scripts waiting for load : {0}\n", m_CompileQueue.Count); 605 sb.AppendFormat("Scripts waiting for load : {0}\n", m_CompileQueue.Count);
513 sb.AppendFormat("Max threads : {0}\n", m_ThreadPool.MaxThreads); 606 sb.AppendFormat("Max threads : {0}\n", m_ThreadPool.MaxThreads);
@@ -516,6 +609,8 @@ namespace OpenSim.Region.ScriptEngine.XEngine
516 sb.AppendFormat("In use threads : {0}\n", m_ThreadPool.InUseThreads); 609 sb.AppendFormat("In use threads : {0}\n", m_ThreadPool.InUseThreads);
517 sb.AppendFormat("Work items waiting : {0}\n", m_ThreadPool.WaitingCallbacks); 610 sb.AppendFormat("Work items waiting : {0}\n", m_ThreadPool.WaitingCallbacks);
518// sb.AppendFormat("Assemblies loaded : {0}\n", m_Assemblies.Count); 611// sb.AppendFormat("Assemblies loaded : {0}\n", m_Assemblies.Count);
612 sb.AppendFormat("Events queued : {0}\n", eventsQueued);
613 sb.AppendFormat("Events processed : {0}\n", eventsProcessed);
519 614
520 SensorRepeat sr = AsyncCommandManager.GetSensorRepeatPlugin(this); 615 SensorRepeat sr = AsyncCommandManager.GetSensorRepeatPlugin(this);
521 sb.AppendFormat("Sensors : {0}\n", sr != null ? sr.SensorsCount : 0); 616 sb.AppendFormat("Sensors : {0}\n", sr != null ? sr.SensorsCount : 0);
@@ -546,7 +641,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine
546 } 641 }
547 } 642 }
548 643
549 HandleScriptsAction(cmdparams, HandleShowScript); 644 HandleScriptsAction<long>(cmdparams, HandleShowScript, si => si.EventsProcessed);
550 } 645 }
551 646
552 private void HandleShowScript(IScriptInstance instance) 647 private void HandleShowScript(IScriptInstance instance)
@@ -576,11 +671,10 @@ namespace OpenSim.Region.ScriptEngine.XEngine
576 671
577 sb.AppendFormat("Script name : {0}\n", instance.ScriptName); 672 sb.AppendFormat("Script name : {0}\n", instance.ScriptName);
578 sb.AppendFormat("Status : {0}\n", status); 673 sb.AppendFormat("Status : {0}\n", status);
579 674 sb.AppendFormat("Queued events : {0}\n", instance.EventsQueued);
580 lock (eq) 675 sb.AppendFormat("Processed events : {0}\n", instance.EventsProcessed);
581 sb.AppendFormat("Queued events : {0}\n", eq.Count);
582
583 sb.AppendFormat("Item UUID : {0}\n", instance.ItemID); 676 sb.AppendFormat("Item UUID : {0}\n", instance.ItemID);
677 sb.AppendFormat("Asset UUID : {0}\n", instance.AssetID);
584 sb.AppendFormat("Containing part name: {0}\n", instance.PrimName); 678 sb.AppendFormat("Containing part name: {0}\n", instance.PrimName);
585 sb.AppendFormat("Containing part UUID: {0}\n", instance.ObjectID); 679 sb.AppendFormat("Containing part UUID: {0}\n", instance.ObjectID);
586 sb.AppendFormat("Position : {0}\n", sop.AbsolutePosition); 680 sb.AppendFormat("Position : {0}\n", sop.AbsolutePosition);
@@ -1089,8 +1183,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1089 1183
1090 string assembly = ""; 1184 string assembly = "";
1091 1185
1092 CultureInfo USCulture = new CultureInfo("en-US"); 1186 Culture.SetCurrentCulture();
1093 Thread.CurrentThread.CurrentCulture = USCulture;
1094 1187
1095 Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> linemap; 1188 Dictionary<KeyValuePair<int, int>, KeyValuePair<int, int>> linemap;
1096 1189
@@ -1347,9 +1440,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1347 lockScriptsForWrite(false); 1440 lockScriptsForWrite(false);
1348 instance.ClearQueue(); 1441 instance.ClearQueue();
1349 1442
1350 // Give the script some time to finish processing its last event. Simply aborting the script thread can 1443 instance.Stop(m_WaitForEventCompletionOnScriptStop);
1351 // cause issues on mono 2.6, 2.10 and possibly later where locks are not released properly on abort.
1352 instance.Stop(1000);
1353 1444
1354// bool objectRemoved = false; 1445// bool objectRemoved = false;
1355 1446
@@ -1504,8 +1595,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1504 /// <returns></returns> 1595 /// <returns></returns>
1505 private object ProcessEventHandler(object parms) 1596 private object ProcessEventHandler(object parms)
1506 { 1597 {
1507 CultureInfo USCulture = new CultureInfo("en-US"); 1598 Culture.SetCurrentCulture();
1508 Thread.CurrentThread.CurrentCulture = USCulture;
1509 1599
1510 IScriptInstance instance = (ScriptInstance) parms; 1600 IScriptInstance instance = (ScriptInstance) parms;
1511 1601
@@ -1693,7 +1783,7 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1693 { 1783 {
1694 IScriptInstance instance = GetInstance(itemID); 1784 IScriptInstance instance = GetInstance(itemID);
1695 if (instance != null) 1785 if (instance != null)
1696 instance.ResetScript(); 1786 instance.ResetScript(m_WaitForEventCompletionOnScriptStop);
1697 } 1787 }
1698 1788
1699 public void StartScript(UUID itemID) 1789 public void StartScript(UUID itemID)
@@ -1708,16 +1798,11 @@ namespace OpenSim.Region.ScriptEngine.XEngine
1708 public void StopScript(UUID itemID) 1798 public void StopScript(UUID itemID)
1709 { 1799 {
1710 IScriptInstance instance = GetInstance(itemID); 1800 IScriptInstance instance = GetInstance(itemID);
1801
1711 if (instance != null) 1802 if (instance != null)
1712 { 1803 instance.Stop(m_WaitForEventCompletionOnScriptStop);
1713 // Give the script some time to finish processing its last event. Simply aborting the script thread can
1714 // cause issues on mono 2.6, 2.10 and possibly later where locks are not released properly on abort.
1715 instance.Stop(1000);
1716 }
1717 else 1804 else
1718 {
1719 m_runFlags.AddOrUpdate(itemID, false, 240); 1805 m_runFlags.AddOrUpdate(itemID, false, 240);
1720 }
1721 } 1806 }
1722 1807
1723 public DetectParams GetDetectParams(UUID itemID, int idx) 1808 public DetectParams GetDetectParams(UUID itemID, int idx)
diff --git a/OpenSim/Region/UserStatistics/WebStatsModule.cs b/OpenSim/Region/UserStatistics/WebStatsModule.cs
index b08233c..64cb577 100644
--- a/OpenSim/Region/UserStatistics/WebStatsModule.cs
+++ b/OpenSim/Region/UserStatistics/WebStatsModule.cs
@@ -94,8 +94,6 @@ namespace OpenSim.Region.UserStatistics
94 if (!enabled) 94 if (!enabled)
95 return; 95 return;
96 96
97 AddEventHandlers();
98
99 if (Util.IsWindows()) 97 if (Util.IsWindows())
100 Util.LoadArchSpecificWindowsDll("sqlite3.dll"); 98 Util.LoadArchSpecificWindowsDll("sqlite3.dll");
101 99
@@ -143,10 +141,14 @@ namespace OpenSim.Region.UserStatistics
143 lock (m_scenes) 141 lock (m_scenes)
144 { 142 {
145 m_scenes.Add(scene); 143 m_scenes.Add(scene);
146 if (m_simstatsCounters.ContainsKey(scene.RegionInfo.RegionID)) 144 updateLogMod = m_scenes.Count * 2;
147 m_simstatsCounters.Remove(scene.RegionInfo.RegionID);
148 145
149 m_simstatsCounters.Add(scene.RegionInfo.RegionID, new USimStatsData(scene.RegionInfo.RegionID)); 146 m_simstatsCounters.Add(scene.RegionInfo.RegionID, new USimStatsData(scene.RegionInfo.RegionID));
147
148 scene.EventManager.OnRegisterCaps += OnRegisterCaps;
149 scene.EventManager.OnDeregisterCaps += OnDeRegisterCaps;
150 scene.EventManager.OnClientClosed += OnClientClosed;
151 scene.EventManager.OnMakeRootAgent += OnMakeRootAgent;
150 scene.StatsReporter.OnSendStatsResult += ReceiveClassicSimStatsPacket; 152 scene.StatsReporter.OnSendStatsResult += ReceiveClassicSimStatsPacket;
151 } 153 }
152 } 154 }
@@ -157,6 +159,15 @@ namespace OpenSim.Region.UserStatistics
157 159
158 public void RemoveRegion(Scene scene) 160 public void RemoveRegion(Scene scene)
159 { 161 {
162 if (!enabled)
163 return;
164
165 lock (m_scenes)
166 {
167 m_scenes.Remove(scene);
168 updateLogMod = m_scenes.Count * 2;
169 m_simstatsCounters.Remove(scene.RegionInfo.RegionID);
170 }
160 } 171 }
161 172
162 public virtual void Close() 173 public virtual void Close()
@@ -187,9 +198,7 @@ namespace OpenSim.Region.UserStatistics
187 private void ReceiveClassicSimStatsPacket(SimStats stats) 198 private void ReceiveClassicSimStatsPacket(SimStats stats)
188 { 199 {
189 if (!enabled) 200 if (!enabled)
190 {
191 return; 201 return;
192 }
193 202
194 try 203 try
195 { 204 {
@@ -198,17 +207,25 @@ namespace OpenSim.Region.UserStatistics
198 if (concurrencyCounter > 0 || System.Environment.TickCount - lastHit > 30000) 207 if (concurrencyCounter > 0 || System.Environment.TickCount - lastHit > 30000)
199 return; 208 return;
200 209
201 if ((updateLogCounter++ % updateLogMod) == 0) 210 // We will conduct this under lock so that fields such as updateLogCounter do not potentially get
211 // confused if a scene is removed.
212 // XXX: Possibly the scope of this lock could be reduced though it's not critical.
213 lock (m_scenes)
202 { 214 {
203 m_loglines = readLogLines(10); 215 if (updateLogMod != 0 && updateLogCounter++ % updateLogMod == 0)
204 if (updateLogCounter > 10000) updateLogCounter = 1; 216 {
205 } 217 m_loglines = readLogLines(10);
206 218
207 USimStatsData ss = m_simstatsCounters[stats.RegionUUID]; 219 if (updateLogCounter > 10000)
220 updateLogCounter = 1;
221 }
208 222
209 if ((++ss.StatsCounter % updateStatsMod) == 0) 223 USimStatsData ss = m_simstatsCounters[stats.RegionUUID];
210 { 224
211 ss.ConsumeSimStats(stats); 225 if ((++ss.StatsCounter % updateStatsMod) == 0)
226 {
227 ss.ConsumeSimStats(stats);
228 }
212 } 229 }
213 } 230 }
214 catch (KeyNotFoundException) 231 catch (KeyNotFoundException)