aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region
diff options
context:
space:
mode:
authorMelanie2013-02-13 01:54:06 +0000
committerMelanie2013-02-13 01:54:06 +0000
commitd0c5e0990bbfe3104afa78c8c6dbe500a451396d (patch)
tree082e368b1d2b5c7cb7cef27e50523c1406aa3e3e /OpenSim/Region
parentMerge branch 'master' into careminster (diff)
parentMerge branch 'master' of melanie@opensimulator.org:/var/git/opensim (diff)
downloadopensim-SC_OLD-d0c5e0990bbfe3104afa78c8c6dbe500a451396d.zip
opensim-SC_OLD-d0c5e0990bbfe3104afa78c8c6dbe500a451396d.tar.gz
opensim-SC_OLD-d0c5e0990bbfe3104afa78c8c6dbe500a451396d.tar.bz2
opensim-SC_OLD-d0c5e0990bbfe3104afa78c8c6dbe500a451396d.tar.xz
Merge branch 'master' into careminster
Diffstat (limited to 'OpenSim/Region')
-rw-r--r--OpenSim/Region/OptionalModules/Scripting/JsonStore/Tests/JsonStoreScriptModuleTests.cs233
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSApiTemplate.cs7
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs15
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs3
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs155
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSParam.cs17
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs97
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs59
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSPrimDisplaced.cs85
-rwxr-xr-xOpenSim/Region/Physics/BulletSPlugin/BSPrimLinkable.cs7
10 files changed, 522 insertions, 156 deletions
diff --git a/OpenSim/Region/OptionalModules/Scripting/JsonStore/Tests/JsonStoreScriptModuleTests.cs b/OpenSim/Region/OptionalModules/Scripting/JsonStore/Tests/JsonStoreScriptModuleTests.cs
index eb4bc22..f25f290 100644
--- a/OpenSim/Region/OptionalModules/Scripting/JsonStore/Tests/JsonStoreScriptModuleTests.cs
+++ b/OpenSim/Region/OptionalModules/Scripting/JsonStore/Tests/JsonStoreScriptModuleTests.cs
@@ -135,6 +135,15 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore.Tests
135 string value = (string)InvokeOp("JsonGetValue", storeId, "Hello"); 135 string value = (string)InvokeOp("JsonGetValue", storeId, "Hello");
136 Assert.That(value, Is.EqualTo("42.15")); 136 Assert.That(value, Is.EqualTo("42.15"));
137 } 137 }
138
139 // Test with an array as the root node
140 {
141 UUID storeId = (UUID)InvokeOp("JsonCreateStore", "[ 'one', 'two', 'three' ]");
142 Assert.That(storeId, Is.Not.EqualTo(UUID.Zero));
143
144 string value = (string)InvokeOp("JsonGetValue", storeId, "[1]");
145 Assert.That(value, Is.EqualTo("two"));
146 }
138 } 147 }
139 148
140 [Test] 149 [Test]
@@ -260,25 +269,69 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore.Tests
260 TestHelpers.InMethod(); 269 TestHelpers.InMethod();
261// TestHelpers.EnableLogging(); 270// TestHelpers.EnableLogging();
262 271
263 UUID storeId = (UUID)InvokeOp("JsonCreateStore", "{ 'Hello' : 'World' }"); 272 // Test remove of node in object pointing to a string
273 {
274 UUID storeId = (UUID)InvokeOp("JsonCreateStore", "{ 'Hello' : 'World' }");
275
276 int returnValue = (int)InvokeOp( "JsonRemoveValue", storeId, "Hello");
277 Assert.That(returnValue, Is.EqualTo(1));
278
279 int result = (int)InvokeOp("JsonTestPath", storeId, "Hello");
280 Assert.That(result, Is.EqualTo(0));
281
282 string returnValue2 = (string)InvokeOp("JsonGetValue", storeId, "Hello");
283 Assert.That(returnValue2, Is.EqualTo(""));
284 }
285
286 // Test remove of node in object pointing to another object
287 {
288 UUID storeId = (UUID)InvokeOp("JsonCreateStore", "{ 'Hello' : { 'World' : 'Wally' } }");
289
290 int returnValue = (int)InvokeOp( "JsonRemoveValue", storeId, "Hello");
291 Assert.That(returnValue, Is.EqualTo(1));
264 292
265 int returnValue = (int)InvokeOp( "JsonRemoveValue", storeId, "Hello"); 293 int result = (int)InvokeOp("JsonTestPath", storeId, "Hello");
266 Assert.That(returnValue, Is.EqualTo(1)); 294 Assert.That(result, Is.EqualTo(0));
295
296 string returnValue2 = (string)InvokeOp("JsonGetValueJson", storeId, "Hello");
297 Assert.That(returnValue2, Is.EqualTo(""));
298 }
267 299
268 int result = (int)InvokeOp("JsonTestPath", storeId, "Hello"); 300 // Test remove of node in an array
269 Assert.That(result, Is.EqualTo(0)); 301 {
302 UUID storeId
303 = (UUID)InvokeOp("JsonCreateStore", "{ 'Hello' : [ 'value1', 'value2' ] }");
304
305 int returnValue = (int)InvokeOp( "JsonRemoveValue", storeId, "Hello[0]");
306 Assert.That(returnValue, Is.EqualTo(1));
270 307
271 string returnValue2 = (string)InvokeOp("JsonGetValue", storeId, "Hello"); 308 int result = (int)InvokeOp("JsonTestPath", storeId, "Hello[0]");
272 Assert.That(returnValue2, Is.EqualTo("")); 309 Assert.That(result, Is.EqualTo(1));
310
311 result = (int)InvokeOp("JsonTestPath", storeId, "Hello[1]");
312 Assert.That(result, Is.EqualTo(0));
313
314 string stringReturnValue = (string)InvokeOp("JsonGetValue", storeId, "Hello[0]");
315 Assert.That(stringReturnValue, Is.EqualTo("value2"));
316
317 stringReturnValue = (string)InvokeOp("JsonGetValueJson", storeId, "Hello[1]");
318 Assert.That(stringReturnValue, Is.EqualTo(""));
319 }
273 320
274 // Test remove of non-existing value 321 // Test remove of non-existing value
275 int fakeValueRemove = (int)InvokeOp("JsonRemoveValue", storeId, "Hello"); 322 {
276 Assert.That(fakeValueRemove, Is.EqualTo(0)); 323 UUID storeId = (UUID)InvokeOp("JsonCreateStore", "{ 'Hello' : 'World' }");
277 324
278 // Test get from non-existing store 325 int fakeValueRemove = (int)InvokeOp("JsonRemoveValue", storeId, "Cheese");
279 UUID fakeStoreId = TestHelpers.ParseTail(0x500); 326 Assert.That(fakeValueRemove, Is.EqualTo(0));
280 int fakeStoreValueRemove = (int)InvokeOp("JsonRemoveValue", fakeStoreId, "Hello"); 327 }
281 Assert.That(fakeStoreValueRemove, Is.EqualTo(0)); 328
329 {
330 // Test get from non-existing store
331 UUID fakeStoreId = TestHelpers.ParseTail(0x500);
332 int fakeStoreValueRemove = (int)InvokeOp("JsonRemoveValue", fakeStoreId, "Hello");
333 Assert.That(fakeStoreValueRemove, Is.EqualTo(0));
334 }
282 } 335 }
283 336
284 [Test] 337 [Test]
@@ -352,7 +405,7 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore.Tests
352// TestHelpers.EnableLogging(); 405// TestHelpers.EnableLogging();
353 406
354 { 407 {
355 UUID storeId = (UUID)InvokeOp("JsonCreateStore", "{ }"); 408 UUID storeId = (UUID)InvokeOp("JsonCreateStore", "{}");
356 409
357 int result = (int)InvokeOp("JsonSetValue", storeId, "Fun", "Times"); 410 int result = (int)InvokeOp("JsonSetValue", storeId, "Fun", "Times");
358 Assert.That(result, Is.EqualTo(1)); 411 Assert.That(result, Is.EqualTo(1));
@@ -361,9 +414,159 @@ namespace OpenSim.Region.OptionalModules.Scripting.JsonStore.Tests
361 Assert.That(value, Is.EqualTo("Times")); 414 Assert.That(value, Is.EqualTo("Times"));
362 } 415 }
363 416
417 // Commented out as this currently unexpectedly fails.
418 // Test setting a key containing periods with delineation
419// {
420// UUID storeId = (UUID)InvokeOp("JsonCreateStore", "{}");
421//
422// int result = (int)InvokeOp("JsonSetValue", storeId, "{Fun.Circus}", "Times");
423// Assert.That(result, Is.EqualTo(1));
424//
425// string value = (string)InvokeOp("JsonGetValue", storeId, "{Fun.Circus}");
426// Assert.That(value, Is.EqualTo("Times"));
427// }
428
429 // *** Test [] ***
430
431 // Test setting a key containing unbalanced ] without delineation. Expecting failure
432 {
433 UUID storeId = (UUID)InvokeOp("JsonCreateStore", "{}");
434
435 int result = (int)InvokeOp("JsonSetValue", storeId, "Fun]Circus", "Times");
436 Assert.That(result, Is.EqualTo(0));
437
438 string value = (string)InvokeOp("JsonGetValue", storeId, "Fun]Circus");
439 Assert.That(value, Is.EqualTo(""));
440 }
441
442 // Test setting a key containing unbalanced [ without delineation. Expecting failure
443 {
444 UUID storeId = (UUID)InvokeOp("JsonCreateStore", "{}");
445
446 int result = (int)InvokeOp("JsonSetValue", storeId, "Fun[Circus", "Times");
447 Assert.That(result, Is.EqualTo(0));
448
449 string value = (string)InvokeOp("JsonGetValue", storeId, "Fun[Circus");
450 Assert.That(value, Is.EqualTo(""));
451 }
452
453 // Test setting a key containing unbalanced [] without delineation. Expecting failure
454 {
455 UUID storeId = (UUID)InvokeOp("JsonCreateStore", "{}");
456
457 int result = (int)InvokeOp("JsonSetValue", storeId, "Fun[]Circus", "Times");
458 Assert.That(result, Is.EqualTo(0));
459
460 string value = (string)InvokeOp("JsonGetValue", storeId, "Fun[]Circus");
461 Assert.That(value, Is.EqualTo(""));
462 }
463
464 // Test setting a key containing unbalanced ] with delineation
465 {
466 UUID storeId = (UUID)InvokeOp("JsonCreateStore", "{}");
467
468 int result = (int)InvokeOp("JsonSetValue", storeId, "{Fun]Circus}", "Times");
469 Assert.That(result, Is.EqualTo(1));
470
471 string value = (string)InvokeOp("JsonGetValue", storeId, "{Fun]Circus}");
472 Assert.That(value, Is.EqualTo("Times"));
473 }
474
475 // Test setting a key containing unbalanced [ with delineation
476 {
477 UUID storeId = (UUID)InvokeOp("JsonCreateStore", "{}");
478
479 int result = (int)InvokeOp("JsonSetValue", storeId, "{Fun[Circus}", "Times");
480 Assert.That(result, Is.EqualTo(1));
481
482 string value = (string)InvokeOp("JsonGetValue", storeId, "{Fun[Circus}");
483 Assert.That(value, Is.EqualTo("Times"));
484 }
485
486 // Test setting a key containing empty balanced [] with delineation
487 {
488 UUID storeId = (UUID)InvokeOp("JsonCreateStore", "{}");
489
490 int result = (int)InvokeOp("JsonSetValue", storeId, "{Fun[]Circus}", "Times");
491 Assert.That(result, Is.EqualTo(1));
492
493 string value = (string)InvokeOp("JsonGetValue", storeId, "{Fun[]Circus}");
494 Assert.That(value, Is.EqualTo("Times"));
495 }
496
497 // Commented out as this currently unexpectedly fails.
498// // Test setting a key containing brackets around an integer with delineation
499// {
500// UUID storeId = (UUID)InvokeOp("JsonCreateStore", "{}");
501//
502// int result = (int)InvokeOp("JsonSetValue", storeId, "{Fun[0]Circus}", "Times");
503// Assert.That(result, Is.EqualTo(1));
504//
505// string value = (string)InvokeOp("JsonGetValue", storeId, "{Fun[]Circus}");
506// Assert.That(value, Is.EqualTo("Times"));
507// }
508
509 // *** Test {} ***
510
511 // Test setting a key containing unbalanced } without delineation. Expecting failure (?)
512 {
513 UUID storeId = (UUID)InvokeOp("JsonCreateStore", "{}");
514
515 int result = (int)InvokeOp("JsonSetValue", storeId, "Fun}Circus", "Times");
516 Assert.That(result, Is.EqualTo(0));
517
518 string value = (string)InvokeOp("JsonGetValue", storeId, "Fun}Circus");
519 Assert.That(value, Is.EqualTo(""));
520 }
521
522 // Test setting a key containing unbalanced { without delineation. Expecting failure (?)
523 {
524 UUID storeId = (UUID)InvokeOp("JsonCreateStore", "{}");
525
526 int result = (int)InvokeOp("JsonSetValue", storeId, "Fun{Circus", "Times");
527 Assert.That(result, Is.EqualTo(0));
528
529 string value = (string)InvokeOp("JsonGetValue", storeId, "Fun}Circus");
530 Assert.That(value, Is.EqualTo(""));
531 }
532
533 // Commented out as this currently unexpectedly fails.
534// // Test setting a key containing unbalanced }
535// {
536// UUID storeId = (UUID)InvokeOp("JsonCreateStore", "{}");
537//
538// int result = (int)InvokeOp("JsonSetValue", storeId, "{Fun}Circus}", "Times");
539// Assert.That(result, Is.EqualTo(1));
540//
541// string value = (string)InvokeOp("JsonGetValue", storeId, "{Fun}Circus}");
542// Assert.That(value, Is.EqualTo("Times"));
543// }
544
545 // Test setting a key containing unbalanced { with delineation
546 {
547 UUID storeId = (UUID)InvokeOp("JsonCreateStore", "{}");
548
549 int result = (int)InvokeOp("JsonSetValue", storeId, "{Fun{Circus}", "Times");
550 Assert.That(result, Is.EqualTo(1));
551
552 string value = (string)InvokeOp("JsonGetValue", storeId, "{Fun{Circus}");
553 Assert.That(value, Is.EqualTo("Times"));
554 }
555
556 // Test setting a key containing balanced {} with delineation. This should fail.
557 {
558 UUID storeId = (UUID)InvokeOp("JsonCreateStore", "{}");
559
560 int result = (int)InvokeOp("JsonSetValue", storeId, "{Fun{Filled}Circus}", "Times");
561 Assert.That(result, Is.EqualTo(0));
562
563 string value = (string)InvokeOp("JsonGetValue", storeId, "{Fun{Filled}Circus}");
564 Assert.That(value, Is.EqualTo(""));
565 }
566
364 // Test setting to location that does not exist. This should fail. 567 // Test setting to location that does not exist. This should fail.
365 { 568 {
366 UUID storeId = (UUID)InvokeOp("JsonCreateStore", "{ }"); 569 UUID storeId = (UUID)InvokeOp("JsonCreateStore", "{}");
367 570
368 int result = (int)InvokeOp("JsonSetValue", storeId, "Fun.Circus", "Times"); 571 int result = (int)InvokeOp("JsonSetValue", storeId, "Fun.Circus", "Times");
369 Assert.That(result, Is.EqualTo(0)); 572 Assert.That(result, Is.EqualTo(0));
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSApiTemplate.cs b/OpenSim/Region/Physics/BulletSPlugin/BSApiTemplate.cs
index 7ab86d2..3f83ef0 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSApiTemplate.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSApiTemplate.cs
@@ -225,9 +225,10 @@ public enum CollisionFlags : uint
225 CF_DISABLE_VISUALIZE_OBJECT = 1 << 5, 225 CF_DISABLE_VISUALIZE_OBJECT = 1 << 5,
226 CF_DISABLE_SPU_COLLISION_PROCESS = 1 << 6, 226 CF_DISABLE_SPU_COLLISION_PROCESS = 1 << 6,
227 // Following used by BulletSim to control collisions and updates 227 // Following used by BulletSim to control collisions and updates
228 BS_SUBSCRIBE_COLLISION_EVENTS = 1 << 10, 228 BS_SUBSCRIBE_COLLISION_EVENTS = 1 << 10, // return collision events from unmanaged to managed
229 BS_FLOATS_ON_WATER = 1 << 11, 229 BS_FLOATS_ON_WATER = 1 << 11, // the object should float at water level
230 BS_VEHICLE_COLLISIONS = 1 << 12, 230 BS_VEHICLE_COLLISIONS = 1 << 12, // return collisions for vehicle ground checking
231 BS_RETURN_ROOT_COMPOUND_SHAPE = 1 << 13, // return the pos/rot of the root shape in a compound shape
231 BS_NONE = 0, 232 BS_NONE = 0,
232 BS_ALL = 0xFFFFFFFF 233 BS_ALL = 0xFFFFFFFF
233}; 234};
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs
index f781aea..8dca7c6 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs
@@ -83,7 +83,7 @@ public sealed class BSCharacter : BSPhysObject
83 _velocity = OMV.Vector3.Zero; 83 _velocity = OMV.Vector3.Zero;
84 _buoyancy = ComputeBuoyancyFromFlying(isFlying); 84 _buoyancy = ComputeBuoyancyFromFlying(isFlying);
85 Friction = BSParam.AvatarStandingFriction; 85 Friction = BSParam.AvatarStandingFriction;
86 Density = BSParam.AvatarDensity; 86 Density = BSParam.AvatarDensity / BSParam.DensityScaleFactor;
87 87
88 // Old versions of ScenePresence passed only the height. If width and/or depth are zero, 88 // Old versions of ScenePresence passed only the height. If width and/or depth are zero,
89 // replace with the default values. 89 // replace with the default values.
@@ -231,6 +231,15 @@ public sealed class BSCharacter : BSPhysObject
231 PhysicsScene.PE.SetFriction(PhysBody, Friction); 231 PhysicsScene.PE.SetFriction(PhysBody, Friction);
232 } 232 }
233 } 233 }
234 else
235 {
236 if (Flying)
237 {
238 // Flying and not collising and velocity nearly zero.
239 ZeroMotion(true /* inTaintTime */);
240 }
241 }
242
234 DetailLog("{0},BSCharacter.MoveMotor,taint,stopping,target={1},colliding={2}", LocalID, _velocityMotor.TargetValue, IsColliding); 243 DetailLog("{0},BSCharacter.MoveMotor,taint,stopping,target={1},colliding={2}", LocalID, _velocityMotor.TargetValue, IsColliding);
235 } 244 }
236 else 245 else
@@ -274,7 +283,7 @@ public sealed class BSCharacter : BSPhysObject
274 // This test is done if moving forward, not flying and is colliding with something. 283 // This test is done if moving forward, not flying and is colliding with something.
275 // DetailLog("{0},BSCharacter.WalkUpStairs,IsColliding={1},flying={2},targSpeed={3},collisions={4}", 284 // DetailLog("{0},BSCharacter.WalkUpStairs,IsColliding={1},flying={2},targSpeed={3},collisions={4}",
276 // LocalID, IsColliding, Flying, TargetSpeed, CollisionsLastTick.Count); 285 // LocalID, IsColliding, Flying, TargetSpeed, CollisionsLastTick.Count);
277 if (IsColliding && !Flying && TargetSpeed > 0.1f /* && ForwardSpeed < 0.1f */) 286 if (IsColliding && !Flying && TargetVelocitySpeed > 0.1f /* && ForwardSpeed < 0.1f */)
278 { 287 {
279 // The range near the character's feet where we will consider stairs 288 // The range near the character's feet where we will consider stairs
280 float nearFeetHeightMin = RawPosition.Z - (Size.Z / 2f) + 0.05f; 289 float nearFeetHeightMin = RawPosition.Z - (Size.Z / 2f) + 0.05f;
@@ -869,7 +878,7 @@ public sealed class BSCharacter : BSPhysObject
869 * Math.Min(Size.X, Size.Y) / 2 878 * Math.Min(Size.X, Size.Y) / 2
870 * Size.Y / 2f // plus the volume of the capsule end caps 879 * Size.Y / 2f // plus the volume of the capsule end caps
871 ); 880 );
872 _mass = Density * _avatarVolume; 881 _mass = Density * BSParam.DensityScaleFactor * _avatarVolume;
873 } 882 }
874 883
875 // The physics engine says that properties have updated. Update same and inform 884 // The physics engine says that properties have updated. Update same and inform
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs
index e35311f..4ece1eb 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinkset.cs
@@ -127,6 +127,8 @@ public abstract class BSLinkset
127 m_children = new HashSet<BSPrimLinkable>(); 127 m_children = new HashSet<BSPrimLinkable>();
128 LinksetMass = parent.RawMass; 128 LinksetMass = parent.RawMass;
129 Rebuilding = false; 129 Rebuilding = false;
130
131 parent.ClearDisplacement();
130 } 132 }
131 133
132 // Link to a linkset where the child knows the parent. 134 // Link to a linkset where the child knows the parent.
@@ -280,6 +282,7 @@ public abstract class BSLinkset
280 return mass; 282 return mass;
281 } 283 }
282 284
285 // Computes linkset's center of mass in world coordinates.
283 protected virtual OMV.Vector3 ComputeLinksetCenterOfMass() 286 protected virtual OMV.Vector3 ComputeLinksetCenterOfMass()
284 { 287 {
285 OMV.Vector3 com; 288 OMV.Vector3 com;
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs
index 36bae9b..4ce58c7 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSLinksetCompound.cs
@@ -93,7 +93,8 @@ public sealed class BSLinksetCompound : BSLinkset
93{ 93{
94 private static string LogHeader = "[BULLETSIM LINKSET COMPOUND]"; 94 private static string LogHeader = "[BULLETSIM LINKSET COMPOUND]";
95 95
96 public BSLinksetCompound(BSScene scene, BSPrimLinkable parent) : base(scene, parent) 96 public BSLinksetCompound(BSScene scene, BSPrimLinkable parent)
97 : base(scene, parent)
97 { 98 {
98 } 99 }
99 100
@@ -217,59 +218,45 @@ public sealed class BSLinksetCompound : BSLinkset
217 // and that is caused by us updating the object. 218 // and that is caused by us updating the object.
218 if ((whichUpdated & ~(UpdatedProperties.Position | UpdatedProperties.Orientation)) == 0) 219 if ((whichUpdated & ~(UpdatedProperties.Position | UpdatedProperties.Orientation)) == 0)
219 { 220 {
220 // Gather the child info. It might not be there if the linkset is in transition.
221 BSLinksetCompoundInfo lsi = updated.LinksetInfo as BSLinksetCompoundInfo;
222 if (lsi != null)
223 {
224 // Since the child moved or rotationed, it needs a new relative position within the linkset
225 BSLinksetCompoundInfo newLsi = new BSLinksetCompoundInfo(lsi.Index, LinksetRoot, updated, OMV.Vector3.Zero);
226 updated.LinksetInfo = newLsi;
227
228 // Find the physical instance of the child 221 // Find the physical instance of the child
229 if (LinksetRoot.PhysShape.HasPhysicalShape && PhysicsScene.PE.IsCompound(LinksetRoot.PhysShape)) 222 if (LinksetRoot.PhysShape.HasPhysicalShape && PhysicsScene.PE.IsCompound(LinksetRoot.PhysShape))
223 {
224 // It is possible that the linkset is still under construction and the child is not yet
225 // inserted into the compound shape. A rebuild of the linkset in a pre-step action will
226 // build the whole thing with the new position or rotation.
227 // The index must be checked because Bullet references the child array but does no validity
228 // checking of the child index passed.
229 int numLinksetChildren = PhysicsScene.PE.GetNumberOfCompoundChildren(LinksetRoot.PhysShape);
230 if (updated.LinksetChildIndex < numLinksetChildren)
230 { 231 {
231 // It is possible that the linkset is still under construction and the child is not yet 232 BulletShape linksetChildShape = PhysicsScene.PE.GetChildShapeFromCompoundShapeIndex(LinksetRoot.PhysShape, updated.LinksetChildIndex);
232 // inserted into the compound shape. A rebuild of the linkset in a pre-step action will 233 if (linksetChildShape.HasPhysicalShape)
233 // build the whole thing with the new position or rotation.
234 // The index must be checked because Bullet references the child array but does no validity
235 // checking of the child index passed.
236 int numLinksetChildren = PhysicsScene.PE.GetNumberOfCompoundChildren(LinksetRoot.PhysShape);
237 if (lsi.Index < numLinksetChildren)
238 { 234 {
239 BulletShape linksetChildShape = PhysicsScene.PE.GetChildShapeFromCompoundShapeIndex(LinksetRoot.PhysShape, lsi.Index); 235 // Found the child shape within the compound shape
240 if (linksetChildShape.HasPhysicalShape) 236 PhysicsScene.PE.UpdateChildTransform(LinksetRoot.PhysShape, updated.LinksetChildIndex,
241 { 237 updated.RawPosition - LinksetRoot.RawPosition,
242 // Found the child shape within the compound shape 238 updated.RawOrientation * OMV.Quaternion.Inverse(LinksetRoot.RawOrientation),
243 PhysicsScene.PE.UpdateChildTransform(LinksetRoot.PhysShape, lsi.Index, 239 true /* shouldRecalculateLocalAabb */);
244 newLsi.OffsetFromCenterOfMass, 240 updatedChild = true;
245 newLsi.OffsetRot, 241 DetailLog("{0},BSLinksetCompound.UpdateProperties,changeChildPosRot,whichUpdated={1},pos={2},rot={3}",
246 true /* shouldRecalculateLocalAabb */); 242 updated.LocalID, whichUpdated, updated.RawPosition, updated.RawOrientation);
247 updatedChild = true;
248 DetailLog("{0},BSLinksetCompound.UpdateProperties,changeChildPosRot,whichUpdated={1},newLsi={2}",
249 updated.LocalID, whichUpdated, newLsi);
250 }
251 else // DEBUG DEBUG
252 { // DEBUG DEBUG
253 DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild,noChildShape,shape={1}",
254 updated.LocalID, linksetChildShape);
255 } // DEBUG DEBUG
256 } 243 }
257 else // DEBUG DEBUG 244 else // DEBUG DEBUG
258 { // DEBUG DEBUG 245 { // DEBUG DEBUG
259 // the child is not yet in the compound shape. This is non-fatal. 246 DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild,noChildShape,shape={1}",
260 DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild,childNotInCompoundShape,numChildren={1},index={2}", 247 updated.LocalID, linksetChildShape);
261 updated.LocalID, numLinksetChildren, lsi.Index);
262 } // DEBUG DEBUG 248 } // DEBUG DEBUG
263 } 249 }
264 else // DEBUG DEBUG 250 else // DEBUG DEBUG
265 { // DEBUG DEBUG 251 { // DEBUG DEBUG
266 DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild,noBodyOrNotCompound", updated.LocalID); 252 // the child is not yet in the compound shape. This is non-fatal.
253 DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild,childNotInCompoundShape,numChildren={1},index={2}",
254 updated.LocalID, numLinksetChildren, updated.LinksetChildIndex);
267 } // DEBUG DEBUG 255 } // DEBUG DEBUG
268 } 256 }
269 else // DEBUG DEBUG 257 else // DEBUG DEBUG
270 { // DEBUG DEBUG 258 { // DEBUG DEBUG
271 DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild,noLinkSetInfo,rootPhysShape={1}", 259 DetailLog("{0},BSLinksetCompound.UpdateProperties,couldNotUpdateChild,noBodyOrNotCompound", updated.LocalID);
272 updated.LocalID, LinksetRoot.PhysShape);
273 } // DEBUG DEBUG 260 } // DEBUG DEBUG
274 261
275 if (!updatedChild) 262 if (!updatedChild)
@@ -379,6 +366,8 @@ public sealed class BSLinksetCompound : BSLinkset
379 // Safe to call even if the child is not really in the linkset. 366 // Safe to call even if the child is not really in the linkset.
380 protected override void RemoveChildFromLinkset(BSPrimLinkable child) 367 protected override void RemoveChildFromLinkset(BSPrimLinkable child)
381 { 368 {
369 child.ClearDisplacement();
370
382 if (m_children.Remove(child)) 371 if (m_children.Remove(child))
383 { 372 {
384 DetailLog("{0},BSLinksetCompound.RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}", 373 DetailLog("{0},BSLinksetCompound.RemoveChildFromLinkset,call,rID={1},rBody={2},cID={3},cBody={4}",
@@ -424,30 +413,31 @@ public sealed class BSLinksetCompound : BSLinkset
424 // The center of mass for the linkset is the geometric center of the group. 413 // The center of mass for the linkset is the geometric center of the group.
425 // Compute a displacement for each component so it is relative to the center-of-mass. 414 // Compute a displacement for each component so it is relative to the center-of-mass.
426 // Bullet presumes an object's origin (relative <0,0,0>) is its center-of-mass 415 // Bullet presumes an object's origin (relative <0,0,0>) is its center-of-mass
427 OMV.Vector3 centerOfMass; 416 OMV.Vector3 centerOfMassW = LinksetRoot.RawPosition;
428 OMV.Vector3 centerDisplacement = OMV.Vector3.Zero; 417 if (!disableCOM) // DEBUG DEBUG
429 if (disableCOM) // DEBUG DEBUG
430 { // DEBUG DEBUG
431 centerOfMass = LinksetRoot.RawPosition; // DEBUG DEBUG
432 // LinksetRoot.PositionDisplacement = OMV.Vector3.Zero;
433 } // DEBUG DEBUG
434 else
435 { 418 {
436 centerOfMass = ComputeLinksetCenterOfMass(); 419 // Compute a center-of-mass in world coordinates.
437 // 'centerDisplacement' is the value to *add* to all the shape offsets 420 centerOfMassW = ComputeLinksetCenterOfMass();
438 centerDisplacement = LinksetRoot.RawPosition - centerOfMass;
439
440 // Since we're displacing the center of the shape, we need to move the body in the world
441 // LinksetRoot.PositionDisplacement = centerDisplacement;
442
443 // This causes the root prim position to be set properly based on the new PositionDisplacement
444 LinksetRoot.ForcePosition = LinksetRoot.RawPosition;
445 // Update the local transform for the root child shape so it is offset from the <0,0,0> which is COM
446 PhysicsScene.PE.UpdateChildTransform(LinksetRoot.PhysShape, 0, -centerDisplacement, OMV.Quaternion.Identity, false);
447 DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,COM,com={1},rootPos={2},centerDisp={3}",
448 LinksetRoot.LocalID, centerOfMass, LinksetRoot.RawPosition, centerDisplacement);
449 } 421 }
450 422
423 OMV.Quaternion invRootOrientation = OMV.Quaternion.Inverse(LinksetRoot.RawOrientation);
424
425 // 'centerDisplacement' is the value to subtract from children to give physical offset position
426 OMV.Vector3 centerDisplacement = (centerOfMassW - LinksetRoot.RawPosition) * invRootOrientation;
427 LinksetRoot.SetEffectiveCenterOfMassW(centerDisplacement);
428
429 // This causes the physical position of the root prim to be offset to accomodate for the displacements
430 LinksetRoot.ForcePosition = LinksetRoot.RawPosition;
431
432 // Update the local transform for the root child shape so it is offset from the <0,0,0> which is COM
433 PhysicsScene.PE.UpdateChildTransform(LinksetRoot.PhysShape, 0 /* childIndex */,
434 -centerDisplacement,
435 OMV.Quaternion.Identity, // LinksetRoot.RawOrientation,
436 false /* shouldRecalculateLocalAabb (is done later after linkset built) */);
437
438 DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,COM,com={1},rootPos={2},centerDisp={3}",
439 LinksetRoot.LocalID, centerOfMassW, LinksetRoot.RawPosition, centerDisplacement);
440
451 DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,start,rBody={1},rShape={2},numChildren={3}", 441 DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,start,rBody={1},rShape={2},numChildren={3}",
452 LinksetRoot.LocalID, LinksetRoot.PhysBody, LinksetRoot.PhysShape, NumberOfChildren); 442 LinksetRoot.LocalID, LinksetRoot.PhysBody, LinksetRoot.PhysShape, NumberOfChildren);
453 443
@@ -455,38 +445,33 @@ public sealed class BSLinksetCompound : BSLinkset
455 int memberIndex = 1; 445 int memberIndex = 1;
456 ForEachMember(delegate(BSPrimLinkable cPrim) 446 ForEachMember(delegate(BSPrimLinkable cPrim)
457 { 447 {
458 if (!IsRoot(cPrim)) 448 if (IsRoot(cPrim))
459 { 449 {
460 // Compute the displacement of the child from the root of the linkset. 450 cPrim.LinksetChildIndex = 0;
461 // This info is saved in the child prim so the relationship does not 451 }
462 // change over time and the new child position can be computed 452 else
463 // when the linkset is being disassembled (the linkset may have moved). 453 {
464 BSLinksetCompoundInfo lci = cPrim.LinksetInfo as BSLinksetCompoundInfo; 454 cPrim.LinksetChildIndex = memberIndex;
465 if (lci == null)
466 {
467 lci = new BSLinksetCompoundInfo(memberIndex, LinksetRoot, cPrim, centerDisplacement);
468 cPrim.LinksetInfo = lci;
469 DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,creatingRelPos,lci={1}", cPrim.LocalID, lci);
470 }
471
472 DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addMemberToShape,mID={1},mShape={2},lci={3}",
473 LinksetRoot.LocalID, cPrim.LocalID, cPrim.PhysShape, lci);
474 455
475 if (cPrim.PhysShape.isNativeShape) 456 if (cPrim.PhysShape.isNativeShape)
476 { 457 {
477 // A native shape is turned into a hull collision shape because native 458 // A native shape is turned into a hull collision shape because native
478 // shapes are not shared so we have to hullify it so it will be tracked 459 // shapes are not shared so we have to hullify it so it will be tracked
479 // and freed at the correct time. This also solves the scaling problem 460 // and freed at the correct time. This also solves the scaling problem
480 // (native shapes scaled but hull/meshes are assumed to not be). 461 // (native shapes scale but hull/meshes are assumed to not be).
481 // TODO: decide of the native shape can just be used in the compound shape. 462 // TODO: decide of the native shape can just be used in the compound shape.
482 // Use call to CreateGeomNonSpecial(). 463 // Use call to CreateGeomNonSpecial().
483 BulletShape saveShape = cPrim.PhysShape; 464 BulletShape saveShape = cPrim.PhysShape;
484 cPrim.PhysShape.Clear(); // Don't let the create free the child's shape 465 cPrim.PhysShape.Clear(); // Don't let the create free the child's shape
485 // PhysicsScene.Shapes.CreateGeomNonSpecial(true, cPrim, null);
486 PhysicsScene.Shapes.CreateGeomMeshOrHull(cPrim, null); 466 PhysicsScene.Shapes.CreateGeomMeshOrHull(cPrim, null);
487 BulletShape newShape = cPrim.PhysShape; 467 BulletShape newShape = cPrim.PhysShape;
488 cPrim.PhysShape = saveShape; 468 cPrim.PhysShape = saveShape;
489 PhysicsScene.PE.AddChildShapeToCompoundShape(LinksetRoot.PhysShape, newShape, lci.OffsetFromCenterOfMass, lci.OffsetRot); 469
470 OMV.Vector3 offsetPos = (cPrim.RawPosition - LinksetRoot.RawPosition) * invRootOrientation - centerDisplacement;
471 OMV.Quaternion offsetRot = cPrim.RawOrientation * invRootOrientation;
472 PhysicsScene.PE.AddChildShapeToCompoundShape(LinksetRoot.PhysShape, newShape, offsetPos, offsetRot);
473 DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addNative,indx={1},rShape={2},cShape={3},offPos={4},offRot={5}",
474 LinksetRoot.LocalID, memberIndex, LinksetRoot.PhysShape, newShape, offsetPos, offsetRot);
490 } 475 }
491 else 476 else
492 { 477 {
@@ -498,9 +483,13 @@ public sealed class BSLinksetCompound : BSLinkset
498 PhysicsScene.Logger.ErrorFormat("{0} Rebuilt sharable shape when building linkset! Region={1}, primID={2}, shape={3}", 483 PhysicsScene.Logger.ErrorFormat("{0} Rebuilt sharable shape when building linkset! Region={1}, primID={2}, shape={3}",
499 LogHeader, PhysicsScene.RegionName, cPrim.LocalID, cPrim.PhysShape); 484 LogHeader, PhysicsScene.RegionName, cPrim.LocalID, cPrim.PhysShape);
500 } 485 }
501 PhysicsScene.PE.AddChildShapeToCompoundShape(LinksetRoot.PhysShape, cPrim.PhysShape, lci.OffsetFromCenterOfMass, lci.OffsetRot); 486 OMV.Vector3 offsetPos = (cPrim.RawPosition - LinksetRoot.RawPosition) * invRootOrientation - centerDisplacement;
487 OMV.Quaternion offsetRot = cPrim.RawOrientation * invRootOrientation;
488 PhysicsScene.PE.AddChildShapeToCompoundShape(LinksetRoot.PhysShape, cPrim.PhysShape, offsetPos, offsetRot);
489 DetailLog("{0},BSLinksetCompound.RecomputeLinksetCompound,addNonNative,indx={1},rShape={2},cShape={3},offPos={4},offRot={5}",
490 LinksetRoot.LocalID, memberIndex, LinksetRoot.PhysShape, cPrim.PhysShape, offsetPos, offsetRot);
491
502 } 492 }
503 lci.Index = memberIndex;
504 memberIndex++; 493 memberIndex++;
505 } 494 }
506 return false; // 'false' says to move onto the next child in the list 495 return false; // 'false' says to move onto the next child in the list
@@ -509,12 +498,16 @@ public sealed class BSLinksetCompound : BSLinkset
509 // With all of the linkset packed into the root prim, it has the mass of everyone. 498 // With all of the linkset packed into the root prim, it has the mass of everyone.
510 LinksetMass = ComputeLinksetMass(); 499 LinksetMass = ComputeLinksetMass();
511 LinksetRoot.UpdatePhysicalMassProperties(LinksetMass, true); 500 LinksetRoot.UpdatePhysicalMassProperties(LinksetMass, true);
501
502 // Enable the physical position updator to return the position and rotation of the root shape
503 PhysicsScene.PE.AddToCollisionFlags(LinksetRoot.PhysBody, CollisionFlags.BS_RETURN_ROOT_COMPOUND_SHAPE);
512 } 504 }
513 finally 505 finally
514 { 506 {
515 Rebuilding = false; 507 Rebuilding = false;
516 } 508 }
517 509
510 // See that the Aabb surrounds the new shape
518 PhysicsScene.PE.RecalculateCompoundShapeLocalAabb(LinksetRoot.PhysShape); 511 PhysicsScene.PE.RecalculateCompoundShapeLocalAabb(LinksetRoot.PhysShape);
519 } 512 }
520} 513}
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs b/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs
index 3e0b4bc..329169f 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSParam.cs
@@ -49,6 +49,7 @@ public static class BSParam
49 public static float MaxLinearVelocity { get; private set; } 49 public static float MaxLinearVelocity { get; private set; }
50 public static float MaxAngularVelocity { get; private set; } 50 public static float MaxAngularVelocity { get; private set; }
51 public static float MaxAddForceMagnitude { get; private set; } 51 public static float MaxAddForceMagnitude { get; private set; }
52 public static float DensityScaleFactor { get; private set; }
52 53
53 public static float LinearDamping { get; private set; } 54 public static float LinearDamping { get; private set; }
54 public static float AngularDamping { get; private set; } 55 public static float AngularDamping { get; private set; }
@@ -281,29 +282,35 @@ public static class BSParam
281 new ParameterDefn("MinObjectMass", "Minimum object mass (0.0001)", 282 new ParameterDefn("MinObjectMass", "Minimum object mass (0.0001)",
282 0.0001f, 283 0.0001f,
283 (s,cf,p,v) => { MinimumObjectMass = cf.GetFloat(p, v); }, 284 (s,cf,p,v) => { MinimumObjectMass = cf.GetFloat(p, v); },
284 (s) => { return (float)MinimumObjectMass; }, 285 (s) => { return MinimumObjectMass; },
285 (s,p,l,v) => { MinimumObjectMass = v; } ), 286 (s,p,l,v) => { MinimumObjectMass = v; } ),
286 new ParameterDefn("MaxObjectMass", "Maximum object mass (10000.01)", 287 new ParameterDefn("MaxObjectMass", "Maximum object mass (10000.01)",
287 10000.01f, 288 10000.01f,
288 (s,cf,p,v) => { MaximumObjectMass = cf.GetFloat(p, v); }, 289 (s,cf,p,v) => { MaximumObjectMass = cf.GetFloat(p, v); },
289 (s) => { return (float)MaximumObjectMass; }, 290 (s) => { return MaximumObjectMass; },
290 (s,p,l,v) => { MaximumObjectMass = v; } ), 291 (s,p,l,v) => { MaximumObjectMass = v; } ),
291 new ParameterDefn("MaxLinearVelocity", "Maximum velocity magnitude that can be assigned to an object", 292 new ParameterDefn("MaxLinearVelocity", "Maximum velocity magnitude that can be assigned to an object",
292 1000.0f, 293 1000.0f,
293 (s,cf,p,v) => { MaxLinearVelocity = cf.GetFloat(p, v); }, 294 (s,cf,p,v) => { MaxLinearVelocity = cf.GetFloat(p, v); },
294 (s) => { return (float)MaxLinearVelocity; }, 295 (s) => { return MaxLinearVelocity; },
295 (s,p,l,v) => { MaxLinearVelocity = v; } ), 296 (s,p,l,v) => { MaxLinearVelocity = v; } ),
296 new ParameterDefn("MaxAngularVelocity", "Maximum rotational velocity magnitude that can be assigned to an object", 297 new ParameterDefn("MaxAngularVelocity", "Maximum rotational velocity magnitude that can be assigned to an object",
297 1000.0f, 298 1000.0f,
298 (s,cf,p,v) => { MaxAngularVelocity = cf.GetFloat(p, v); }, 299 (s,cf,p,v) => { MaxAngularVelocity = cf.GetFloat(p, v); },
299 (s) => { return (float)MaxAngularVelocity; }, 300 (s) => { return MaxAngularVelocity; },
300 (s,p,l,v) => { MaxAngularVelocity = v; } ), 301 (s,p,l,v) => { MaxAngularVelocity = v; } ),
301 // LL documentation says thie number should be 20f for llApplyImpulse and 200f for llRezObject 302 // LL documentation says thie number should be 20f for llApplyImpulse and 200f for llRezObject
302 new ParameterDefn("MaxAddForceMagnitude", "Maximum force that can be applied by llApplyImpulse (SL says 20f)", 303 new ParameterDefn("MaxAddForceMagnitude", "Maximum force that can be applied by llApplyImpulse (SL says 20f)",
303 20000.0f, 304 20000.0f,
304 (s,cf,p,v) => { MaxAddForceMagnitude = cf.GetFloat(p, v); }, 305 (s,cf,p,v) => { MaxAddForceMagnitude = cf.GetFloat(p, v); },
305 (s) => { return (float)MaxAddForceMagnitude; }, 306 (s) => { return MaxAddForceMagnitude; },
306 (s,p,l,v) => { MaxAddForceMagnitude = v; } ), 307 (s,p,l,v) => { MaxAddForceMagnitude = v; } ),
308 // Density is passed around as 100kg/m3. This scales that to 1kg/m3.
309 new ParameterDefn("DensityScaleFactor", "Conversion for simulator/viewer density (100kg/m3) to physical density (1kg/m3)",
310 0.01f,
311 (s,cf,p,v) => { DensityScaleFactor = cf.GetFloat(p, v); },
312 (s) => { return DensityScaleFactor; },
313 (s,p,l,v) => { DensityScaleFactor = v; } ),
307 314
308 new ParameterDefn("PID_D", "Derivitive factor for motion smoothing", 315 new ParameterDefn("PID_D", "Derivitive factor for motion smoothing",
309 2200f, 316 2200f,
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs
index de69fa0..f953c1e 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSPhysObject.cs
@@ -99,6 +99,9 @@ public abstract class BSPhysObject : PhysicsActor
99 CollisionAccumulation = 0; 99 CollisionAccumulation = 0;
100 ColliderIsMoving = false; 100 ColliderIsMoving = false;
101 CollisionScore = 0; 101 CollisionScore = 0;
102
103 // All axis free.
104 LockedAxis = LockedAxisFree;
102 } 105 }
103 106
104 // Tell the object to clean up. 107 // Tell the object to clean up.
@@ -136,6 +139,7 @@ public abstract class BSPhysObject : PhysicsActor
136 139
137 // The objects base shape information. Null if not a prim type shape. 140 // The objects base shape information. Null if not a prim type shape.
138 public PrimitiveBaseShape BaseShape { get; protected set; } 141 public PrimitiveBaseShape BaseShape { get; protected set; }
142
139 // Some types of objects have preferred physical representations. 143 // Some types of objects have preferred physical representations.
140 // Returns SHAPE_UNKNOWN if there is no preference. 144 // Returns SHAPE_UNKNOWN if there is no preference.
141 public virtual BSPhysicsShapeType PreferredPhysicalShape 145 public virtual BSPhysicsShapeType PreferredPhysicalShape
@@ -150,15 +154,17 @@ public abstract class BSPhysObject : PhysicsActor
150 public EntityProperties LastEntityProperties { get; set; } 154 public EntityProperties LastEntityProperties { get; set; }
151 155
152 public virtual OMV.Vector3 Scale { get; set; } 156 public virtual OMV.Vector3 Scale { get; set; }
153 public abstract bool IsSolid { get; }
154 public abstract bool IsStatic { get; }
155 public abstract bool IsSelected { get; }
156 157
157 // It can be confusing for an actor to know if it should move or update an object 158 // It can be confusing for an actor to know if it should move or update an object
158 // depeneding on the setting of 'selected', 'physical, ... 159 // depeneding on the setting of 'selected', 'physical, ...
159 // This flag is the true test -- if true, the object is being acted on in the physical world 160 // This flag is the true test -- if true, the object is being acted on in the physical world
160 public abstract bool IsPhysicallyActive { get; } 161 public abstract bool IsPhysicallyActive { get; }
161 162
163 // Detailed state of the object.
164 public abstract bool IsSolid { get; }
165 public abstract bool IsStatic { get; }
166 public abstract bool IsSelected { get; }
167
162 // Materialness 168 // Materialness
163 public MaterialAttributes.Material Material { get; private set; } 169 public MaterialAttributes.Material Material { get; private set; }
164 public override void SetMaterial(int material) 170 public override void SetMaterial(int material)
@@ -169,7 +175,8 @@ public abstract class BSPhysObject : PhysicsActor
169 MaterialAttributes matAttrib = BSMaterials.GetAttributes(Material, false); 175 MaterialAttributes matAttrib = BSMaterials.GetAttributes(Material, false);
170 Friction = matAttrib.friction; 176 Friction = matAttrib.friction;
171 Restitution = matAttrib.restitution; 177 Restitution = matAttrib.restitution;
172 Density = matAttrib.density; 178 Density = matAttrib.density / BSParam.DensityScaleFactor;
179 DetailLog("{0},{1}.SetMaterial,Mat={2},frict={3},rest={4},den={5}", LocalID, TypeName, Material, Friction, Restitution, Density);
173 } 180 }
174 181
175 // Stop all physical motion. 182 // Stop all physical motion.
@@ -185,14 +192,6 @@ public abstract class BSPhysObject : PhysicsActor
185 public abstract OMV.Quaternion RawOrientation { get; set; } 192 public abstract OMV.Quaternion RawOrientation { get; set; }
186 public abstract OMV.Quaternion ForceOrientation { get; set; } 193 public abstract OMV.Quaternion ForceOrientation { get; set; }
187 194
188 public virtual float TargetSpeed
189 {
190 get
191 {
192 OMV.Vector3 characterOrientedVelocity = TargetVelocity * OMV.Quaternion.Inverse(OMV.Quaternion.Normalize(RawOrientation));
193 return characterOrientedVelocity.X;
194 }
195 }
196 public abstract OMV.Vector3 RawVelocity { get; set; } 195 public abstract OMV.Vector3 RawVelocity { get; set; }
197 public abstract OMV.Vector3 ForceVelocity { get; set; } 196 public abstract OMV.Vector3 ForceVelocity { get; set; }
198 197
@@ -202,6 +201,7 @@ public abstract class BSPhysObject : PhysicsActor
202 201
203 public virtual bool ForceBodyShapeRebuild(bool inTaintTime) { return false; } 202 public virtual bool ForceBodyShapeRebuild(bool inTaintTime) { return false; }
204 203
204 // The current velocity forward
205 public virtual float ForwardSpeed 205 public virtual float ForwardSpeed
206 { 206 {
207 get 207 get
@@ -210,6 +210,22 @@ public abstract class BSPhysObject : PhysicsActor
210 return characterOrientedVelocity.X; 210 return characterOrientedVelocity.X;
211 } 211 }
212 } 212 }
213 // The forward speed we are trying to achieve (TargetVelocity)
214 public virtual float TargetVelocitySpeed
215 {
216 get
217 {
218 OMV.Vector3 characterOrientedVelocity = TargetVelocity * OMV.Quaternion.Inverse(OMV.Quaternion.Normalize(RawOrientation));
219 return characterOrientedVelocity.X;
220 }
221 }
222
223 // The user can optionally set the center of mass. The user's setting will override any
224 // computed center-of-mass (like in linksets).
225 public OMV.Vector3? UserSetCenterOfMass { get; set; }
226
227 public OMV.Vector3 LockedAxis { get; set; } // zero means locked. one means free.
228 public readonly OMV.Vector3 LockedAxisFree = new OMV.Vector3(1f, 1f, 1f); // All axis are free
213 229
214 #region Collisions 230 #region Collisions
215 231
@@ -407,9 +423,7 @@ public abstract class BSPhysObject : PhysicsActor
407 { 423 {
408 // Clean out any existing action 424 // Clean out any existing action
409 UnRegisterPreStepAction(op, id); 425 UnRegisterPreStepAction(op, id);
410
411 RegisteredPrestepActions[identifier] = actn; 426 RegisteredPrestepActions[identifier] = actn;
412
413 PhysicsScene.BeforeStep += actn; 427 PhysicsScene.BeforeStep += actn;
414 } 428 }
415 DetailLog("{0},BSPhysObject.RegisterPreStepAction,id={1}", LocalID, identifier); 429 DetailLog("{0},BSPhysObject.RegisterPreStepAction,id={1}", LocalID, identifier);
@@ -455,9 +469,7 @@ public abstract class BSPhysObject : PhysicsActor
455 { 469 {
456 // Clean out any existing action 470 // Clean out any existing action
457 UnRegisterPostStepAction(op, id); 471 UnRegisterPostStepAction(op, id);
458
459 RegisteredPoststepActions[identifier] = actn; 472 RegisteredPoststepActions[identifier] = actn;
460
461 PhysicsScene.AfterStep += actn; 473 PhysicsScene.AfterStep += actn;
462 } 474 }
463 DetailLog("{0},BSPhysObject.RegisterPostStepAction,id={1}", LocalID, identifier); 475 DetailLog("{0},BSPhysObject.RegisterPostStepAction,id={1}", LocalID, identifier);
@@ -494,7 +506,58 @@ public abstract class BSPhysObject : PhysicsActor
494 } 506 }
495 DetailLog("{0},BSPhysObject.UnRegisterAllPostStepActions,", LocalID); 507 DetailLog("{0},BSPhysObject.UnRegisterAllPostStepActions,", LocalID);
496 } 508 }
497 509
510 // When an update to the physical properties happens, this event is fired to let
511 // different actors to modify the update before it is passed around
512 public delegate void PreUpdatePropertyAction(ref EntityProperties entprop);
513 public event PreUpdatePropertyAction OnPreUpdateProperty;
514 protected void TriggerPreUpdatePropertyAction(ref EntityProperties entprop)
515 {
516 PreUpdatePropertyAction actions = OnPreUpdateProperty;
517 if (actions != null)
518 actions(ref entprop);
519 }
520
521 private Dictionary<string, PreUpdatePropertyAction> RegisteredPreUpdatePropertyActions = new Dictionary<string, PreUpdatePropertyAction>();
522 public void RegisterPreUpdatePropertyAction(string identifier, PreUpdatePropertyAction actn)
523 {
524 lock (RegisteredPreUpdatePropertyActions)
525 {
526 // Clean out any existing action
527 UnRegisterPreUpdatePropertyAction(identifier);
528 RegisteredPreUpdatePropertyActions[identifier] = actn;
529 OnPreUpdateProperty += actn;
530 }
531 DetailLog("{0},BSPhysObject.RegisterPreUpdatePropertyAction,id={1}", LocalID, identifier);
532 }
533 public bool UnRegisterPreUpdatePropertyAction(string identifier)
534 {
535 bool removed = false;
536 lock (RegisteredPreUpdatePropertyActions)
537 {
538 if (RegisteredPreUpdatePropertyActions.ContainsKey(identifier))
539 {
540 OnPreUpdateProperty -= RegisteredPreUpdatePropertyActions[identifier];
541 RegisteredPreUpdatePropertyActions.Remove(identifier);
542 removed = true;
543 }
544 }
545 DetailLog("{0},BSPhysObject.UnRegisterPreUpdatePropertyAction,id={1},removed={2}", LocalID, identifier, removed);
546 return removed;
547 }
548 public void UnRegisterAllPreUpdatePropertyActions()
549 {
550 lock (RegisteredPreUpdatePropertyActions)
551 {
552 foreach (KeyValuePair<string, PreUpdatePropertyAction> kvp in RegisteredPreUpdatePropertyActions)
553 {
554 OnPreUpdateProperty -= kvp.Value;
555 }
556 RegisteredPreUpdatePropertyActions.Clear();
557 }
558 DetailLog("{0},BSPhysObject.UnRegisterAllPreUpdatePropertyAction,", LocalID);
559 }
560
498 #endregion // Per Simulation Step actions 561 #endregion // Per Simulation Step actions
499 562
500 // High performance detailed logging routine used by the physical objects. 563 // High performance detailed logging routine used by the physical objects.
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
index cf7aa0f..0323b0d 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
@@ -242,6 +242,45 @@ public class BSPrim : BSPhysObject
242 public override void LockAngularMotion(OMV.Vector3 axis) 242 public override void LockAngularMotion(OMV.Vector3 axis)
243 { 243 {
244 DetailLog("{0},BSPrim.LockAngularMotion,call,axis={1}", LocalID, axis); 244 DetailLog("{0},BSPrim.LockAngularMotion,call,axis={1}", LocalID, axis);
245
246 OMV.Vector3 locking = new OMV.Vector3(1f, 1f, 1f);
247 if (axis.X != 1) locking.X = 0f;
248 if (axis.Y != 1) locking.Y = 0f;
249 if (axis.Z != 1) locking.Z = 0f;
250 LockedAxis = locking;
251
252 /* Not implemented yet
253 if (LockedAxis != LockedAxisFree)
254 {
255 // Something is locked so start the thingy that keeps that axis from changing
256 RegisterPreUpdatePropertyAction("BSPrim.LockAngularMotion", delegate(ref EntityProperties entprop)
257 {
258 if (LockedAxis != LockedAxisFree)
259 {
260 if (IsPhysicallyActive)
261 {
262 // Bullet can lock axis but it only works for global axis.
263 // Check if this prim is aligned on global axis and use Bullet's
264 // system if so.
265
266 ForceOrientation = entprop.Rotation;
267 ForceRotationalVelocity = entprop.RotationalVelocity;
268 }
269 }
270 else
271 {
272 UnRegisterPreUpdatePropertyAction("BSPrim.LockAngularMotion");
273 }
274
275 });
276 }
277 else
278 {
279 // Everything seems unlocked
280 UnRegisterPreUpdatePropertyAction("BSPrim.LockAngularMotion");
281 }
282 */
283
245 return; 284 return;
246 } 285 }
247 286
@@ -311,7 +350,8 @@ public class BSPrim : BSPhysObject
311 350
312 float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(RawPosition); 351 float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(RawPosition);
313 OMV.Vector3 upForce = OMV.Vector3.Zero; 352 OMV.Vector3 upForce = OMV.Vector3.Zero;
314 if (RawPosition.Z < terrainHeight) 353 float approxSize = Math.Max(Size.X, Math.Max(Size.Y, Size.Z));
354 if ((RawPosition.Z + approxSize / 2f) < terrainHeight)
315 { 355 {
316 DetailLog("{0},BSPrim.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, RawPosition, terrainHeight); 356 DetailLog("{0},BSPrim.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, RawPosition, terrainHeight);
317 float targetHeight = terrainHeight + (Size.Z / 2f); 357 float targetHeight = terrainHeight + (Size.Z / 2f);
@@ -442,7 +482,7 @@ public class BSPrim : BSPhysObject
442 RegisterPreStepAction("BSPrim.setForce", LocalID, 482 RegisterPreStepAction("BSPrim.setForce", LocalID,
443 delegate(float timeStep) 483 delegate(float timeStep)
444 { 484 {
445 if (!IsPhysicallyActive) 485 if (!IsPhysicallyActive || _force == OMV.Vector3.Zero)
446 { 486 {
447 UnRegisterPreStepAction("BSPrim.setForce", LocalID); 487 UnRegisterPreStepAction("BSPrim.setForce", LocalID);
448 return; 488 return;
@@ -576,6 +616,8 @@ public class BSPrim : BSPhysObject
576 } 616 }
577 } 617 }
578 } 618 }
619 // The simulator/viewer keep density as 100kg/m3.
620 // Remember to use BSParam.DensityScaleFactor to create the physical density.
579 public override float Density 621 public override float Density
580 { 622 {
581 get { return base.Density; } 623 get { return base.Density; }
@@ -647,7 +689,7 @@ public class BSPrim : BSPhysObject
647 RegisterPreStepAction("BSPrim.setTorque", LocalID, 689 RegisterPreStepAction("BSPrim.setTorque", LocalID,
648 delegate(float timeStep) 690 delegate(float timeStep)
649 { 691 {
650 if (!IsPhysicallyActive) 692 if (!IsPhysicallyActive || _torque == OMV.Vector3.Zero)
651 { 693 {
652 UnRegisterPreStepAction("BSPrim.setTorque", LocalID); 694 UnRegisterPreStepAction("BSPrim.setTorque", LocalID);
653 return; 695 return;
@@ -1569,7 +1611,8 @@ public class BSPrim : BSPhysObject
1569 profileEnd = 1.0f - (float)BaseShape.ProfileEnd * 2.0e-5f; 1611 profileEnd = 1.0f - (float)BaseShape.ProfileEnd * 2.0e-5f;
1570 volume *= (profileEnd - profileBegin); 1612 volume *= (profileEnd - profileBegin);
1571 1613
1572 returnMass = Density * volume; 1614 returnMass = Density * BSParam.DensityScaleFactor * volume;
1615 DetailLog("{0},BSPrim.CalculateMass,den={1},vol={2},mass={3}", LocalID, Density, volume, returnMass);
1573 1616
1574 returnMass = Util.Clamp(returnMass, BSParam.MinimumObjectMass, BSParam.MaximumObjectMass); 1617 returnMass = Util.Clamp(returnMass, BSParam.MinimumObjectMass, BSParam.MaximumObjectMass);
1575 1618
@@ -1607,6 +1650,8 @@ public class BSPrim : BSPhysObject
1607 // the world that things have changed. 1650 // the world that things have changed.
1608 public override void UpdateProperties(EntityProperties entprop) 1651 public override void UpdateProperties(EntityProperties entprop)
1609 { 1652 {
1653 TriggerPreUpdatePropertyAction(ref entprop);
1654
1610 // A temporary kludge to suppress the rotational effects introduced on vehicles by Bullet 1655 // A temporary kludge to suppress the rotational effects introduced on vehicles by Bullet
1611 // TODO: handle physics introduced by Bullet with computed vehicle physics. 1656 // TODO: handle physics introduced by Bullet with computed vehicle physics.
1612 if (VehicleController.IsActive) 1657 if (VehicleController.IsActive)
@@ -1619,7 +1664,11 @@ public class BSPrim : BSPhysObject
1619 // Assign directly to the local variables so the normal set actions do not happen 1664 // Assign directly to the local variables so the normal set actions do not happen
1620 _position = entprop.Position; 1665 _position = entprop.Position;
1621 _orientation = entprop.Rotation; 1666 _orientation = entprop.Rotation;
1622 _velocity = entprop.Velocity; 1667 // _velocity = entprop.Velocity;
1668 // DEBUG DEBUG DEBUG -- smooth velocity changes a bit. The simulator seems to be
1669 // very sensitive to velocity changes.
1670 if (!entprop.Velocity.ApproxEquals(_velocity, 0.1f))
1671 _velocity = entprop.Velocity;
1623 _acceleration = entprop.Acceleration; 1672 _acceleration = entprop.Acceleration;
1624 _rotationalVelocity = entprop.RotationalVelocity; 1673 _rotationalVelocity = entprop.RotationalVelocity;
1625 1674
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrimDisplaced.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrimDisplaced.cs
index 6401308..f1c3b5c 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSPrimDisplaced.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrimDisplaced.cs
@@ -44,72 +44,107 @@ namespace OpenSim.Region.Physics.BulletSPlugin
44{ 44{
45public class BSPrimDisplaced : BSPrim 45public class BSPrimDisplaced : BSPrim
46{ 46{
47 // 'Position' and 'Orientation' is what the simulator thinks the positions of the prim is. 47 // The purpose of this module is to do any mapping between what the simulator thinks
48 // Because Bullet needs the zero coordinate to be the center of mass of the linkset, 48 // the prim position and orientation is and what the physical position/orientation.
49 // sometimes it is necessary to displace the position the physics engine thinks 49 // This difference happens because Bullet assumes the center-of-mass is the <0,0,0>
50 // the position is. PositionDisplacement must be added and removed from the 50 // of the prim/linkset. The simulator tracks the location of the prim/linkset by
51 // position as the simulator position is stored and fetched from the physics 51 // the location of the root prim. So, if center-of-mass is anywhere but the origin
52 // engine. Similar to OrientationDisplacement. 52 // of the root prim, the physical origin is displaced from the simulator origin.
53 //
54 // This routine works by capturing the Force* setting of position/orientation/... and
55 // adjusting the simulator values (being set) into the physical values.
56 // The conversion is also done in the opposite direction (physical origin -> simulator origin).
57 //
58 // The updateParameter call is also captured and the values from the physics engine
59 // are converted into simulator origin values before being passed to the base
60 // class.
61
53 public virtual OMV.Vector3 PositionDisplacement { get; set; } 62 public virtual OMV.Vector3 PositionDisplacement { get; set; }
54 public virtual OMV.Quaternion OrientationDisplacement { get; set; } 63 public virtual OMV.Quaternion OrientationDisplacement { get; set; }
55 public virtual OMV.Vector3 CenterOfMassLocation { get; set; }
56 public virtual OMV.Vector3 GeometricCenterLocation { get; set; }
57 64
58 public BSPrimDisplaced(uint localID, String primName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size, 65 public BSPrimDisplaced(uint localID, String primName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size,
59 OMV.Quaternion rotation, PrimitiveBaseShape pbs, bool pisPhysical) 66 OMV.Quaternion rotation, PrimitiveBaseShape pbs, bool pisPhysical)
60 : base(localID, primName, parent_scene, pos, size, rotation, pbs, pisPhysical) 67 : base(localID, primName, parent_scene, pos, size, rotation, pbs, pisPhysical)
61 { 68 {
62 CenterOfMassLocation = RawPosition; 69 ClearDisplacement();
63 GeometricCenterLocation = RawPosition;
64 } 70 }
65 71
66 public override Vector3 ForcePosition 72 public void ClearDisplacement()
73 {
74 PositionDisplacement = OMV.Vector3.Zero;
75 OrientationDisplacement = OMV.Quaternion.Identity;
76 }
77
78 // Set this sets and computes the displacement from the passed prim to the center-of-mass.
79 // A user set value for center-of-mass overrides whatever might be passed in here.
80 // The displacement is in local coordinates (relative to root prim in linkset oriented coordinates).
81 public virtual void SetEffectiveCenterOfMassW(Vector3 centerOfMassDisplacement)
67 { 82 {
68 get 83 Vector3 comDisp;
84 if (UserSetCenterOfMass.HasValue)
85 comDisp = (OMV.Vector3)UserSetCenterOfMass;
86 else
87 comDisp = centerOfMassDisplacement;
88
89 if (comDisp == Vector3.Zero)
69 { 90 {
70 return base.ForcePosition; 91 // If there is no diplacement. Things get reset.
92 PositionDisplacement = OMV.Vector3.Zero;
93 OrientationDisplacement = OMV.Quaternion.Identity;
71 } 94 }
72 set 95 else
73 { 96 {
74 base.ForcePosition = value; 97 // Remember the displacement from root as well as the origional rotation of the
75 CenterOfMassLocation = RawPosition; 98 // new center-of-mass.
76 GeometricCenterLocation = RawPosition; 99 PositionDisplacement = comDisp;
100 OrientationDisplacement = OMV.Quaternion.Identity;
77 } 101 }
78 } 102 }
79 103
80 public override Quaternion ForceOrientation 104 public override Vector3 ForcePosition
81 { 105 {
82 get 106 get { return base.ForcePosition; }
107 set
83 { 108 {
84 return base.ForceOrientation; 109 if (PositionDisplacement != OMV.Vector3.Zero)
110 base.ForcePosition = value - (PositionDisplacement * RawOrientation);
111 else
112 base.ForcePosition = value;
85 } 113 }
114 }
115
116 public override Quaternion ForceOrientation
117 {
118 get { return base.ForceOrientation; }
86 set 119 set
87 { 120 {
88 base.ForceOrientation = value; 121 base.ForceOrientation = value;
89 } 122 }
90 } 123 }
91 124
125 // TODO: decide if this is the right place for these variables.
126 // Somehow incorporate the optional settability by the user.
92 // Is this used? 127 // Is this used?
93 public override OMV.Vector3 CenterOfMass 128 public override OMV.Vector3 CenterOfMass
94 { 129 {
95 get { return CenterOfMassLocation; } 130 get { return RawPosition; }
96 } 131 }
97 132
98 // Is this used? 133 // Is this used?
99 public override OMV.Vector3 GeometricCenter 134 public override OMV.Vector3 GeometricCenter
100 { 135 {
101 get { return GeometricCenterLocation; } 136 get { return RawPosition; }
102 } 137 }
103 138
104
105 public override void UpdateProperties(EntityProperties entprop) 139 public override void UpdateProperties(EntityProperties entprop)
106 { 140 {
107 // Undo any center-of-mass displacement that might have been done. 141 // Undo any center-of-mass displacement that might have been done.
108 if (PositionDisplacement != OMV.Vector3.Zero) 142 if (PositionDisplacement != OMV.Vector3.Zero || OrientationDisplacement != OMV.Quaternion.Identity)
109 { 143 {
110 // Correct for any rotation around the center-of-mass 144 // Correct for any rotation around the center-of-mass
111 // TODO!!! 145 // TODO!!!
112 entprop.Position -= PositionDisplacement; 146 entprop.Position = entprop.Position + (PositionDisplacement * entprop.Rotation);
147 // entprop.Rotation = something;
113 } 148 }
114 149
115 base.UpdateProperties(entprop); 150 base.UpdateProperties(entprop);
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrimLinkable.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrimLinkable.cs
index 9898562..d65d407 100755
--- a/OpenSim/Region/Physics/BulletSPlugin/BSPrimLinkable.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrimLinkable.cs
@@ -38,6 +38,9 @@ namespace OpenSim.Region.Physics.BulletSPlugin
38public class BSPrimLinkable : BSPrimDisplaced 38public class BSPrimLinkable : BSPrimDisplaced
39{ 39{
40 public BSLinkset Linkset { get; set; } 40 public BSLinkset Linkset { get; set; }
41 // The index of this child prim.
42 public int LinksetChildIndex { get; set; }
43
41 public BSLinksetInfo LinksetInfo { get; set; } 44 public BSLinksetInfo LinksetInfo { get; set; }
42 45
43 public BSPrimLinkable(uint localID, String primName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size, 46 public BSPrimLinkable(uint localID, String primName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size,
@@ -90,7 +93,6 @@ public class BSPrimLinkable : BSPrimDisplaced
90 DetailLog("{0},BSPrimLinkset.delink,parentBefore={1},childrenBefore={2},parentAfter={3},childrenAfter={4}, ", 93 DetailLog("{0},BSPrimLinkset.delink,parentBefore={1},childrenBefore={2},parentAfter={3},childrenAfter={4}, ",
91 LocalID, parentBefore.LocalID, childrenBefore, Linkset.LinksetRoot.LocalID, Linkset.NumberOfChildren); 94 LocalID, parentBefore.LocalID, childrenBefore, Linkset.LinksetRoot.LocalID, Linkset.NumberOfChildren);
92 return; 95 return;
93 base.delink();
94 } 96 }
95 97
96 // When simulator changes position, this might be moving a child of the linkset. 98 // When simulator changes position, this might be moving a child of the linkset.
@@ -133,7 +135,8 @@ public class BSPrimLinkable : BSPrimDisplaced
133 // When going from non-physical to physical, this re-enables the constraints that 135 // When going from non-physical to physical, this re-enables the constraints that
134 // had been automatically disabled when the mass was set to zero. 136 // had been automatically disabled when the mass was set to zero.
135 // For compound based linksets, this enables and disables interactions of the children. 137 // For compound based linksets, this enables and disables interactions of the children.
136 Linkset.Refresh(this); 138 if (Linkset != null) // null can happen during initialization
139 Linkset.Refresh(this);
137 } 140 }
138 141
139 protected override void MakeDynamic(bool makeStatic) 142 protected override void MakeDynamic(bool makeStatic)