aboutsummaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
authorTeravus Ovares2008-12-26 12:58:02 +0000
committerTeravus Ovares2008-12-26 12:58:02 +0000
commitec2dc354b485491d7879686b4a78027971e3ed92 (patch)
tree3ea0771a55524ce1b9dec3c3c66f0dac24a006ef
parentPrevent exception in terrain module if just the word terrain is entered at th... (diff)
downloadopensim-SC-ec2dc354b485491d7879686b4a78027971e3ed92.zip
opensim-SC-ec2dc354b485491d7879686b4a78027971e3ed92.tar.gz
opensim-SC-ec2dc354b485491d7879686b4a78027971e3ed92.tar.bz2
opensim-SC-ec2dc354b485491d7879686b4a78027971e3ed92.tar.xz
* Applying Nlin's NINJA Joint patch. v2. Mantis# 2874
* Thanks nlin! * To try it out, set ninja joints active in the ODEPhysicsSettings and use the example at: * http://forge.opensimulator.org/gf/download/frsrelease/142/304/demo-playground.tgz. * Don't forget to change the .tgz to .oar and load it with load-oar.
-rw-r--r--OpenSim/Region/Environment/Scenes/Scene.cs167
-rw-r--r--OpenSim/Region/Environment/Scenes/SceneGraph.cs17
-rw-r--r--OpenSim/Region/Environment/Scenes/SceneObjectPart.cs282
-rw-r--r--OpenSim/Region/Physics/Manager/PhysicsActor.cs3
-rw-r--r--OpenSim/Region/Physics/Manager/PhysicsJoint.cs55
-rw-r--r--OpenSim/Region/Physics/Manager/PhysicsScene.cs64
-rw-r--r--OpenSim/Region/Physics/OdePlugin/ODEPrim.cs29
-rw-r--r--OpenSim/Region/Physics/OdePlugin/OdePhysicsJoint.cs49
-rw-r--r--OpenSim/Region/Physics/OdePlugin/OdePlugin.cs571
-rw-r--r--bin/OpenSim.ini.example7
10 files changed, 1188 insertions, 56 deletions
diff --git a/OpenSim/Region/Environment/Scenes/Scene.cs b/OpenSim/Region/Environment/Scenes/Scene.cs
index d4d5b90..2d0743c 100644
--- a/OpenSim/Region/Environment/Scenes/Scene.cs
+++ b/OpenSim/Region/Environment/Scenes/Scene.cs
@@ -200,8 +200,30 @@ namespace OpenSim.Region.Environment.Scenes
200 // an instance to the physics plugin's Scene object. 200 // an instance to the physics plugin's Scene object.
201 public PhysicsScene PhysicsScene 201 public PhysicsScene PhysicsScene
202 { 202 {
203 set { m_sceneGraph.PhysicsScene = value; }
204 get { return m_sceneGraph.PhysicsScene; } 203 get { return m_sceneGraph.PhysicsScene; }
204 set
205 {
206 // If we're not doing the initial set
207 // Then we've got to remove the previous
208 // event handler
209 if (PhysicsScene != null && PhysicsScene.SupportsNINJAJoints)
210 {
211 PhysicsScene.OnJointMoved -= jointMoved;
212 PhysicsScene.OnJointDeactivated -= jointDeactivated;
213 PhysicsScene.OnJointErrorMessage -= jointErrorMessage;
214 }
215
216 m_sceneGraph.PhysicsScene = value;
217
218 if (PhysicsScene != null && m_sceneGraph.PhysicsScene.SupportsNINJAJoints)
219 {
220 // register event handlers to respond to joint movement/deactivation
221 PhysicsScene.OnJointMoved += jointMoved;
222 PhysicsScene.OnJointDeactivated += jointDeactivated;
223 PhysicsScene.OnJointErrorMessage += jointErrorMessage;
224 }
225
226 }
205 } 227 }
206 228
207 // This gets locked so things stay thread safe. 229 // This gets locked so things stay thread safe.
@@ -1848,7 +1870,11 @@ namespace OpenSim.Region.Environment.Scenes
1848 1870
1849 foreach (SceneObjectPart part in group.Children.Values) 1871 foreach (SceneObjectPart part in group.Children.Values)
1850 { 1872 {
1851 if (part.PhysActor != null) 1873 if (part.IsJoint() && ((part.ObjectFlags&(uint)PrimFlags.Physics) != 0) )
1874 {
1875 PhysicsScene.RequestJointDeletion(part.Name); // FIXME: what if the name changed?
1876 }
1877 else if (part.PhysActor != null)
1852 { 1878 {
1853 PhysicsScene.RemovePrim(part.PhysActor); 1879 PhysicsScene.RemovePrim(part.PhysActor);
1854 part.PhysActor = null; 1880 part.PhysActor = null;
@@ -4408,5 +4434,142 @@ namespace OpenSim.Region.Environment.Scenes
4408 4434
4409 return 0; 4435 return 0;
4410 } 4436 }
4437
4438 // This callback allows the PhysicsScene to call back to its caller (the SceneGraph) and
4439 // update non-physical objects like the joint proxy objects that represent the position
4440 // of the joints in the scene.
4441
4442 // This routine is normally called from within a lock(OdeLock) from within the OdePhysicsScene
4443 // WARNING: be careful of deadlocks here if you manipulate the scene. Remember you are being called
4444 // from within the OdePhysicsScene.
4445
4446 protected internal void jointMoved(PhysicsJoint joint)
4447 {
4448
4449 // m_parentScene.PhysicsScene.DumpJointInfo(); // non-thread-locked version; we should already be in a lock(OdeLock) when this callback is invoked
4450 // FIXME: this causes a sequential lookup of all objects in the scene; use a dictionary
4451 SceneObjectPart jointProxyObject = GetSceneObjectPart(joint.ObjectNameInScene);
4452 if (jointProxyObject == null)
4453 {
4454 jointErrorMessage(joint, "WARNING, joint proxy not found, name " + joint.ObjectNameInScene);
4455 return;
4456 }
4457
4458 // now update the joint proxy object in the scene to have the position of the joint as returned by the physics engine
4459 SceneObjectPart trackedBody = GetSceneObjectPart(joint.TrackedBodyName); // FIXME: causes a sequential lookup
4460 if (trackedBody == null) return; // the actor may have been deleted but the joint still lingers around a few frames waiting for deletion. during this time, trackedBody is NULL to prevent further motion of the joint proxy.
4461 jointProxyObject.Velocity = trackedBody.Velocity;
4462 jointProxyObject.RotationalVelocity = trackedBody.RotationalVelocity;
4463 switch (joint.Type)
4464 {
4465 case PhysicsJointType.Ball:
4466 {
4467 PhysicsVector jointAnchor = PhysicsScene.GetJointAnchor(joint);
4468 Vector3 proxyPos = new Vector3(jointAnchor.X, jointAnchor.Y, jointAnchor.Z);
4469 jointProxyObject.ParentGroup.UpdateGroupPosition(proxyPos); // schedules the entire group for a terse update
4470 }
4471 break;
4472
4473 case PhysicsJointType.Hinge:
4474 {
4475 PhysicsVector jointAnchor = PhysicsScene.GetJointAnchor(joint);
4476
4477 // Normally, we would just ask the physics scene to return the axis for the joint.
4478 // Unfortunately, ODE sometimes returns <0,0,0> for the joint axis, which should
4479 // never occur. Therefore we cannot rely on ODE to always return a correct joint axis.
4480 // Therefore the following call does not always work:
4481 //PhysicsVector phyJointAxis = _PhyScene.GetJointAxis(joint);
4482
4483 // instead we compute the joint orientation by saving the original joint orientation
4484 // relative to one of the jointed bodies, and applying this transformation
4485 // to the current position of the jointed bodies (the tracked body) to compute the
4486 // current joint orientation.
4487
4488 if (joint.TrackedBodyName == null)
4489 {
4490 jointErrorMessage(joint, "joint.TrackedBodyName is null, joint " + joint.ObjectNameInScene);
4491 }
4492
4493 Vector3 proxyPos = new Vector3(jointAnchor.X, jointAnchor.Y, jointAnchor.Z);
4494 Quaternion q = trackedBody.RotationOffset * joint.LocalRotation;
4495
4496 jointProxyObject.ParentGroup.UpdateGroupPosition(proxyPos); // schedules the entire group for a terse update
4497 jointProxyObject.ParentGroup.UpdateGroupRotation(q); // schedules the entire group for a terse update
4498 }
4499 break;
4500 }
4501 }
4502
4503 // This callback allows the PhysicsScene to call back to its caller (the SceneGraph) and
4504 // update non-physical objects like the joint proxy objects that represent the position
4505 // of the joints in the scene.
4506
4507 // This routine is normally called from within a lock(OdeLock) from within the OdePhysicsScene
4508 // WARNING: be careful of deadlocks here if you manipulate the scene. Remember you are being called
4509 // from within the OdePhysicsScene.
4510 protected internal void jointDeactivated(PhysicsJoint joint)
4511 {
4512 //m_log.Debug("[NINJA] SceneGraph.jointDeactivated, joint:" + joint.ObjectNameInScene);
4513 // FIXME: this causes a sequential lookup of all objects in the scene; use a dictionary
4514 SceneObjectPart jointProxyObject = GetSceneObjectPart(joint.ObjectNameInScene);
4515 if (jointProxyObject == null)
4516 {
4517 jointErrorMessage(joint, "WARNING, trying to deactivate (stop interpolation of) joint proxy, but not found, name " + joint.ObjectNameInScene);
4518 return;
4519 }
4520
4521 // turn the proxy non-physical, which also stops its client-side interpolation
4522 bool wasUsingPhysics = ((jointProxyObject.ObjectFlags & (uint)PrimFlags.Physics) != 0);
4523 if (wasUsingPhysics)
4524 {
4525 jointProxyObject.UpdatePrimFlags(false, false, true, false); // FIXME: possible deadlock here; check to make sure all the scene alterations set into motion here won't deadlock
4526 }
4527 }
4528
4529 // This callback allows the PhysicsScene to call back to its caller (the SceneGraph) and
4530 // alert the user of errors by using the debug channel in the same way that scripts alert
4531 // the user of compile errors.
4532
4533 // This routine is normally called from within a lock(OdeLock) from within the OdePhysicsScene
4534 // WARNING: be careful of deadlocks here if you manipulate the scene. Remember you are being called
4535 // from within the OdePhysicsScene.
4536 public void jointErrorMessage(PhysicsJoint joint, string message)
4537 {
4538 // FIXME: this causes a sequential lookup of all objects in the scene; use a dictionary
4539 if (joint != null)
4540 {
4541 if (joint.ErrorMessageCount > PhysicsJoint.maxErrorMessages)
4542 return;
4543
4544 SceneObjectPart jointProxyObject = GetSceneObjectPart(joint.ObjectNameInScene);
4545 if (jointProxyObject != null)
4546 {
4547 SimChat(Utils.StringToBytes("[NINJA] " + message),
4548 ChatTypeEnum.DebugChannel,
4549 2147483647,
4550 jointProxyObject.AbsolutePosition,
4551 jointProxyObject.Name,
4552 jointProxyObject.UUID,
4553 false);
4554
4555 joint.ErrorMessageCount++;
4556
4557 if (joint.ErrorMessageCount > PhysicsJoint.maxErrorMessages)
4558 {
4559 SimChat(Utils.StringToBytes("[NINJA] Too many messages for this joint, suppressing further messages."),
4560 ChatTypeEnum.DebugChannel,
4561 2147483647,
4562 jointProxyObject.AbsolutePosition,
4563 jointProxyObject.Name,
4564 jointProxyObject.UUID,
4565 false);
4566 }
4567 }
4568 else
4569 {
4570 // couldn't find the joint proxy object; the error message is silently suppressed
4571 }
4572 }
4573 }
4411 } 4574 }
4412} 4575}
diff --git a/OpenSim/Region/Environment/Scenes/SceneGraph.cs b/OpenSim/Region/Environment/Scenes/SceneGraph.cs
index dbb8539..d998dbb 100644
--- a/OpenSim/Region/Environment/Scenes/SceneGraph.cs
+++ b/OpenSim/Region/Environment/Scenes/SceneGraph.cs
@@ -159,6 +159,22 @@ namespace OpenSim.Region.Environment.Scenes
159 { 159 {
160 lock (m_syncRoot) 160 lock (m_syncRoot)
161 { 161 {
162 // Here is where the Scene calls the PhysicsScene. This is a one-way
163 // interaction; the PhysicsScene cannot access the calling Scene directly.
164 // But with joints, we want a PhysicsActor to be able to influence a
165 // non-physics SceneObjectPart. In particular, a PhysicsActor that is connected
166 // with a joint should be able to move the SceneObjectPart which is the visual
167 // representation of that joint (for editing and serialization purposes).
168 // However the PhysicsActor normally cannot directly influence anything outside
169 // of the PhysicsScene, and the non-physical SceneObjectPart which represents
170 // the joint in the Scene does not exist in the PhysicsScene.
171 //
172 // To solve this, we have an event in the PhysicsScene that is fired when a joint
173 // has changed position (because one of its associated PhysicsActors has changed
174 // position).
175 //
176 // Therefore, JointMoved and JointDeactivated events will be fired as a result of the following Simulate().
177
162 return _PhyScene.Simulate((float)elapsed); 178 return _PhyScene.Simulate((float)elapsed);
163 } 179 }
164 } 180 }
@@ -875,6 +891,7 @@ namespace OpenSim.Region.Environment.Scenes
875 { 891 {
876 List<EntityBase> EntityList = GetEntities(); 892 List<EntityBase> EntityList = GetEntities();
877 893
894 // FIXME: use a dictionary here
878 foreach (EntityBase ent in EntityList) 895 foreach (EntityBase ent in EntityList)
879 { 896 {
880 if (ent is SceneObjectGroup) 897 if (ent is SceneObjectGroup)
diff --git a/OpenSim/Region/Environment/Scenes/SceneObjectPart.cs b/OpenSim/Region/Environment/Scenes/SceneObjectPart.cs
index 3491645..0f3e065 100644
--- a/OpenSim/Region/Environment/Scenes/SceneObjectPart.cs
+++ b/OpenSim/Region/Environment/Scenes/SceneObjectPart.cs
@@ -419,7 +419,14 @@ namespace OpenSim.Region.Environment.Scenes
419 public virtual string Name 419 public virtual string Name
420 { 420 {
421 get { return m_name; } 421 get { return m_name; }
422 set { m_name = value; } 422 set
423 {
424 m_name = value;
425 if (PhysActor != null)
426 {
427 PhysActor.SOPName = value;
428 }
429 }
423 } 430 }
424 431
425 public byte Material 432 public byte Material
@@ -681,7 +688,14 @@ namespace OpenSim.Region.Environment.Scenes
681 public string Description 688 public string Description
682 { 689 {
683 get { return m_description; } 690 get { return m_description; }
684 set { m_description = value; } 691 set
692 {
693 m_description = value;
694 if (PhysActor != null)
695 {
696 PhysActor.SOPDescription = value;
697 }
698 }
685 } 699 }
686 700
687 public Color Color 701 public Color Color
@@ -1287,30 +1301,39 @@ if (m_shape != null) {
1287 bool isPhysical = (((rootObjectFlags & (uint) PrimFlags.Physics) != 0) && m_physicalPrim); 1301 bool isPhysical = (((rootObjectFlags & (uint) PrimFlags.Physics) != 0) && m_physicalPrim);
1288 bool isPhantom = ((rootObjectFlags & (uint) PrimFlags.Phantom) != 0); 1302 bool isPhantom = ((rootObjectFlags & (uint) PrimFlags.Phantom) != 0);
1289 1303
1290 // Special case for VolumeDetection: If VolumeDetection is set, the phantom flag is locally ignored 1304 if (IsJoint())
1291 if (VolumeDetectActive) 1305 {
1292 isPhantom = false; 1306 DoPhysicsPropertyUpdate(isPhysical, true);
1293 1307 }
1294 // Added clarification.. since A rigid body is an object that you can kick around, etc. 1308 else
1295 bool RigidBody = isPhysical && !isPhantom;
1296
1297 // The only time the physics scene shouldn't know about the prim is if it's phantom or an attachment, which is phantom by definition
1298 if (!isPhantom && !IsAttachment)
1299 { 1309 {
1300 PhysActor = m_parentGroup.Scene.PhysicsScene.AddPrimShape( 1310 // Special case for VolumeDetection: If VolumeDetection is set, the phantom flag is locally ignored
1301 Name, 1311 if (VolumeDetectActive)
1302 Shape, 1312 isPhantom = false;
1303 new PhysicsVector(AbsolutePosition.X, AbsolutePosition.Y, AbsolutePosition.Z),
1304 new PhysicsVector(Scale.X, Scale.Y, Scale.Z),
1305 RotationOffset,
1306 RigidBody);
1307 1313
1308 // Basic Physics returns null.. joy joy joy. 1314 // Added clarification.. since A rigid body is an object that you can kick around, etc.
1309 if (PhysActor != null) 1315 bool RigidBody = isPhysical && !isPhantom;
1316
1317 // The only time the physics scene shouldn't know about the prim is if it's phantom or an attachment, which is phantom by definition
1318 if (!isPhantom && !IsAttachment)
1310 { 1319 {
1311 PhysActor.LocalID = LocalId; 1320 PhysActor = m_parentGroup.Scene.PhysicsScene.AddPrimShape(
1312 DoPhysicsPropertyUpdate(RigidBody, true); 1321 Name,
1313 PhysActor.SetVolumeDetect(VolumeDetectActive ? 1 : 0); 1322 Shape,
1323 new PhysicsVector(AbsolutePosition.X, AbsolutePosition.Y, AbsolutePosition.Z),
1324 new PhysicsVector(Scale.X, Scale.Y, Scale.Z),
1325 RotationOffset,
1326 RigidBody);
1327
1328 // Basic Physics returns null.. joy joy joy.
1329 if (PhysActor != null)
1330 {
1331 PhysActor.SOPName = this.Name; // save object name and desc into the PhysActor so ODE internals know the joint/body info
1332 PhysActor.SOPDescription = this.Description;
1333 PhysActor.LocalID = LocalId;
1334 DoPhysicsPropertyUpdate(RigidBody, true);
1335 PhysActor.SetVolumeDetect(VolumeDetectActive ? 1 : 0);
1336 }
1314 } 1337 }
1315 } 1338 }
1316 } 1339 }
@@ -1421,57 +1444,160 @@ if (m_shape != null) {
1421 1444
1422 public void DoPhysicsPropertyUpdate(bool UsePhysics, bool isNew) 1445 public void DoPhysicsPropertyUpdate(bool UsePhysics, bool isNew)
1423 { 1446 {
1424 if (PhysActor != null) 1447 if (IsJoint())
1425 { 1448 {
1426 if (UsePhysics != PhysActor.IsPhysical || isNew) 1449 if (UsePhysics)
1427 { 1450 {
1428 if (PhysActor.IsPhysical) 1451 // by turning a joint proxy object physical, we cause creation of a joint in the ODE scene.
1452 // note that, as a special case, joints have no bodies or geoms in the physics scene, even though they are physical.
1453
1454 PhysicsJointType jointType;
1455 if (IsHingeJoint())
1456 {
1457 jointType = PhysicsJointType.Hinge;
1458 }
1459 else if (IsBallJoint())
1460 {
1461 jointType = PhysicsJointType.Ball;
1462 }
1463 else
1464 {
1465 jointType = PhysicsJointType.Ball;
1466 }
1467
1468 List<string> bodyNames = new List<string>();
1469 string RawParams = Description;
1470 string[] jointParams = RawParams.Split(' ');
1471 string trackedBodyName = null;
1472 if (jointParams.Length >= 2)
1473 {
1474 for (int iBodyName = 0; iBodyName < 2; iBodyName++)
1475 {
1476 string bodyName = jointParams[iBodyName];
1477 bodyNames.Add(bodyName);
1478 if (bodyName != "NULL")
1479 {
1480 if (trackedBodyName == null)
1481 {
1482 trackedBodyName = bodyName;
1483 }
1484 }
1485 }
1486 }
1487
1488 SceneObjectPart trackedBody = m_parentGroup.Scene.GetSceneObjectPart(trackedBodyName); // FIXME: causes a sequential lookup
1489 Quaternion localRotation = Quaternion.Identity;
1490 if (trackedBody != null)
1429 { 1491 {
1430 if (!isNew) 1492 localRotation = Quaternion.Inverse(trackedBody.RotationOffset) * this.RotationOffset;
1431 ParentGroup.Scene.RemovePhysicalPrim(1); 1493 }
1494 else
1495 {
1496 // error, output it below
1497 }
1432 1498
1433 PhysActor.OnRequestTerseUpdate -= PhysicsRequestingTerseUpdate; 1499 PhysicsJoint joint;
1434 PhysActor.OnOutOfBounds -= PhysicsOutOfBounds; 1500
1435 PhysActor.delink(); 1501 joint = m_parentGroup.Scene.PhysicsScene.RequestJointCreation(Name, jointType,
1502 new PhysicsVector(AbsolutePosition.X, AbsolutePosition.Y, AbsolutePosition.Z),
1503 this.RotationOffset,
1504 Description,
1505 bodyNames,
1506 trackedBodyName,
1507 localRotation);
1508
1509 if (trackedBody == null)
1510 {
1511 ParentGroup.Scene.jointErrorMessage(joint, "warning: tracked body name not found! joint location will not be updated properly. joint: " + Name);
1436 } 1512 }
1437 1513
1438 if (!UsePhysics && !isNew) 1514 }
1515 else
1516 {
1517 if (isNew)
1439 { 1518 {
1440 // reset velocity to 0 on physics switch-off. Without that, the client thinks the 1519 // if the joint proxy is new, and it is not physical, do nothing. There is no joint in ODE to
1441 // prim still has velocity and continues to interpolate its position along the old 1520 // delete, and if we try to delete it, due to asynchronous processing, the deletion request
1442 // velocity-vector. 1521 // will get processed later at an indeterminate time, which could cancel a later-arriving
1522 // joint creation request.
1523 }
1524 else
1525 {
1526 // here we turn off the joint object, so remove the joint from the physics scene
1527 m_parentGroup.Scene.PhysicsScene.RequestJointDeletion(Name); // FIXME: what if the name changed?
1528
1529 // make sure client isn't interpolating the joint proxy object
1443 Velocity = new Vector3(0, 0, 0); 1530 Velocity = new Vector3(0, 0, 0);
1531 RotationalVelocity = new Vector3(0, 0, 0);
1444 Acceleration = new Vector3(0, 0, 0); 1532 Acceleration = new Vector3(0, 0, 0);
1445 AngularVelocity = new Vector3(0, 0, 0);
1446 } 1533 }
1534 }
1535 }
1536 else
1537 {
1538 if (PhysActor != null)
1539 {
1540 if (UsePhysics != PhysActor.IsPhysical || isNew)
1541 {
1542 if (PhysActor.IsPhysical) // implies UsePhysics==false for this block
1543 {
1544 if (!isNew)
1545 ParentGroup.Scene.RemovePhysicalPrim(1);
1447 1546
1448 PhysActor.IsPhysical = UsePhysics; 1547 PhysActor.OnRequestTerseUpdate -= PhysicsRequestingTerseUpdate;
1548 PhysActor.OnOutOfBounds -= PhysicsOutOfBounds;
1549 PhysActor.delink();
1449 1550
1551 if (ParentGroup.Scene.PhysicsScene.SupportsNINJAJoints && (!isNew))
1552 {
1553 // destroy all joints connected to this now deactivated body
1554 m_parentGroup.Scene.PhysicsScene.RemoveAllJointsConnectedToActorThreadLocked(PhysActor);
1555 }
1450 1556
1451 // If we're not what we're supposed to be in the physics scene, recreate ourselves. 1557 // stop client-side interpolation of all joint proxy objects that have just been deleted
1452 //m_parentGroup.Scene.PhysicsScene.RemovePrim(PhysActor); 1558 // this is done because RemoveAllJointsConnectedToActor invokes the OnJointDeactivated callback,
1453 /// that's not wholesome. Had to make Scene public 1559 // which stops client-side interpolation of deactivated joint proxy objects.
1454 //PhysActor = null; 1560 }
1455 1561
1456 if ((ObjectFlags & (uint) PrimFlags.Phantom) == 0) 1562 if (!UsePhysics && !isNew)
1457 {
1458 if (UsePhysics)
1459 { 1563 {
1460 ParentGroup.Scene.AddPhysicalPrim(1); 1564 // reset velocity to 0 on physics switch-off. Without that, the client thinks the
1565 // prim still has velocity and continues to interpolate its position along the old
1566 // velocity-vector.
1567 Velocity = new Vector3(0, 0, 0);
1568 Acceleration = new Vector3(0, 0, 0);
1569 AngularVelocity = new Vector3(0, 0, 0);
1570 //RotationalVelocity = new Vector3(0, 0, 0);
1571 }
1572
1573 PhysActor.IsPhysical = UsePhysics;
1461 1574
1462 PhysActor.OnRequestTerseUpdate += PhysicsRequestingTerseUpdate; 1575
1463 PhysActor.OnOutOfBounds += PhysicsOutOfBounds; 1576 // If we're not what we're supposed to be in the physics scene, recreate ourselves.
1464 if (_parentID != 0 && _parentID != LocalId) 1577 //m_parentGroup.Scene.PhysicsScene.RemovePrim(PhysActor);
1578 /// that's not wholesome. Had to make Scene public
1579 //PhysActor = null;
1580
1581 if ((ObjectFlags & (uint)PrimFlags.Phantom) == 0)
1582 {
1583 if (UsePhysics)
1465 { 1584 {
1466 if (ParentGroup.RootPart.PhysActor != null) 1585 ParentGroup.Scene.AddPhysicalPrim(1);
1586
1587 PhysActor.OnRequestTerseUpdate += PhysicsRequestingTerseUpdate;
1588 PhysActor.OnOutOfBounds += PhysicsOutOfBounds;
1589 if (_parentID != 0 && _parentID != LocalId)
1467 { 1590 {
1468 PhysActor.link(ParentGroup.RootPart.PhysActor); 1591 if (ParentGroup.RootPart.PhysActor != null)
1592 {
1593 PhysActor.link(ParentGroup.RootPart.PhysActor);
1594 }
1469 } 1595 }
1470 } 1596 }
1471 } 1597 }
1472 } 1598 }
1599 m_parentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(PhysActor);
1473 } 1600 }
1474 m_parentGroup.Scene.PhysicsScene.AddPhysicsActorTaint(PhysActor);
1475 } 1601 }
1476 } 1602 }
1477 1603
@@ -3190,6 +3316,53 @@ if (m_shape != null) {
3190 } 3316 }
3191 } 3317 }
3192 3318
3319 public bool IsHingeJoint()
3320 {
3321 // For now, we use the NINJA naming scheme for identifying joints.
3322 // In the future, we can support other joint specification schemes such as a
3323 // custom checkbox in the viewer GUI.
3324 if (m_parentGroup.Scene.PhysicsScene.SupportsNINJAJoints)
3325 {
3326 string hingeString = "hingejoint";
3327 return (Name.Length >= hingeString.Length && Name.Substring(0, hingeString.Length) == hingeString);
3328 }
3329 else
3330 {
3331 return false;
3332 }
3333 }
3334
3335 public bool IsBallJoint()
3336 {
3337 // For now, we use the NINJA naming scheme for identifying joints.
3338 // In the future, we can support other joint specification schemes such as a
3339 // custom checkbox in the viewer GUI.
3340 if (m_parentGroup.Scene.PhysicsScene.SupportsNINJAJoints)
3341 {
3342 string ballString = "balljoint";
3343 return (Name.Length >= ballString.Length && Name.Substring(0, ballString.Length) == ballString);
3344 }
3345 else
3346 {
3347 return false;
3348 }
3349 }
3350
3351 public bool IsJoint()
3352 {
3353 // For now, we use the NINJA naming scheme for identifying joints.
3354 // In the future, we can support other joint specification schemes such as a
3355 // custom checkbox in the viewer GUI.
3356 if (m_parentGroup.Scene.PhysicsScene.SupportsNINJAJoints)
3357 {
3358 return IsHingeJoint() || IsBallJoint();
3359 }
3360 else
3361 {
3362 return false;
3363 }
3364 }
3365
3193 public void UpdatePrimFlags(bool UsePhysics, bool IsTemporary, bool IsPhantom, bool IsVD) 3366 public void UpdatePrimFlags(bool UsePhysics, bool IsTemporary, bool IsPhantom, bool IsVD)
3194 { 3367 {
3195 bool wasUsingPhysics = ((ObjectFlags & (uint) PrimFlags.Physics) != 0); 3368 bool wasUsingPhysics = ((ObjectFlags & (uint) PrimFlags.Physics) != 0);
@@ -3230,6 +3403,11 @@ if (m_shape != null) {
3230 3403
3231 } 3404 }
3232 3405
3406 if (UsePhysics && IsJoint())
3407 {
3408 IsPhantom = true;
3409 }
3410
3233 if (UsePhysics) 3411 if (UsePhysics)
3234 { 3412 {
3235 AddFlag(PrimFlags.Physics); 3413 AddFlag(PrimFlags.Physics);
@@ -3258,7 +3436,7 @@ if (m_shape != null) {
3258 } 3436 }
3259 3437
3260 3438
3261 if (IsPhantom || IsAttachment) 3439 if (IsPhantom || IsAttachment) // note: this may have been changed above in the case of joints
3262 { 3440 {
3263 AddFlag(PrimFlags.Phantom); 3441 AddFlag(PrimFlags.Phantom);
3264 if (PhysActor != null) 3442 if (PhysActor != null)
diff --git a/OpenSim/Region/Physics/Manager/PhysicsActor.cs b/OpenSim/Region/Physics/Manager/PhysicsActor.cs
index fd02057..3ba5ce5 100644
--- a/OpenSim/Region/Physics/Manager/PhysicsActor.cs
+++ b/OpenSim/Region/Physics/Manager/PhysicsActor.cs
@@ -127,6 +127,9 @@ namespace OpenSim.Region.Physics.Manager
127 127
128 public abstract bool Selected { set; } 128 public abstract bool Selected { set; }
129 129
130 public String SOPName { get; set; }
131 public String SOPDescription { get; set; }
132
130 public abstract void CrossingFailure(); 133 public abstract void CrossingFailure();
131 134
132 public abstract void link(PhysicsActor obj); 135 public abstract void link(PhysicsActor obj);
diff --git a/OpenSim/Region/Physics/Manager/PhysicsJoint.cs b/OpenSim/Region/Physics/Manager/PhysicsJoint.cs
new file mode 100644
index 0000000..52f7e5f
--- /dev/null
+++ b/OpenSim/Region/Physics/Manager/PhysicsJoint.cs
@@ -0,0 +1,55 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSim Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using System.Collections.Generic;
30using OpenSim.Framework;
31using OpenMetaverse;
32
33namespace OpenSim.Region.Physics.Manager
34{
35 public enum PhysicsJointType : int
36 {
37 Ball = 0,
38 Hinge = 1
39 }
40
41 public class PhysicsJoint
42 {
43 public virtual bool IsInPhysicsEngine { get { return false; } } // set internally to indicate if this joint has already been passed to the physics engine or is still pending
44 public PhysicsJointType Type { get; set; }
45 public string RawParams { get; set; }
46 public List<string> BodyNames = new List<string>();
47 public PhysicsVector Position { get; set; } // global coords
48 public Quaternion Rotation { get; set; } // global coords
49 public string ObjectNameInScene { get; set; } // proxy object in scene that represents the joint position/orientation
50 public string TrackedBodyName { get; set; } // body name that this joint is attached to (ObjectNameInScene will follow TrackedBodyName)
51 public Quaternion LocalRotation { get; set; } // joint orientation relative to one of the involved bodies, the tracked body
52 public int ErrorMessageCount { get; set; } // total # of error messages printed for this joint since its creation. if too many, further error messages are suppressed to prevent flooding.
53 public const int maxErrorMessages = 100; // no more than this # of error messages will be printed for each joint
54 }
55}
diff --git a/OpenSim/Region/Physics/Manager/PhysicsScene.cs b/OpenSim/Region/Physics/Manager/PhysicsScene.cs
index f104632..2cf4d5a 100644
--- a/OpenSim/Region/Physics/Manager/PhysicsScene.cs
+++ b/OpenSim/Region/Physics/Manager/PhysicsScene.cs
@@ -73,6 +73,67 @@ namespace OpenSim.Region.Physics.Manager
73 public abstract PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, PhysicsVector position, 73 public abstract PhysicsActor AddPrimShape(string primName, PrimitiveBaseShape pbs, PhysicsVector position,
74 PhysicsVector size, Quaternion rotation, bool isPhysical); 74 PhysicsVector size, Quaternion rotation, bool isPhysical);
75 75
76 public virtual bool SupportsNINJAJoints
77 {
78 get { return false; }
79 }
80
81 public virtual PhysicsJoint RequestJointCreation(string objectNameInScene, PhysicsJointType jointType, PhysicsVector position,
82 Quaternion rotation, string parms, List<string> bodyNames, string trackedBodyName, Quaternion localRotation)
83 { return null; }
84
85 public virtual void RequestJointDeletion(string objectNameInScene)
86 { return; }
87
88 public virtual void RemoveAllJointsConnectedToActorThreadLocked(PhysicsActor actor)
89 { return; }
90
91 public virtual void DumpJointInfo()
92 { return; }
93
94 public event JointMoved OnJointMoved;
95
96 protected virtual void DoJointMoved(PhysicsJoint joint)
97 {
98 // We need this to allow subclasses (but not other classes) to invoke the event; C# does
99 // not allow subclasses to invoke the parent class event.
100 if (OnJointMoved != null)
101 {
102 OnJointMoved(joint);
103 }
104 }
105
106 public event JointDeactivated OnJointDeactivated;
107
108 protected virtual void DoJointDeactivated(PhysicsJoint joint)
109 {
110 // We need this to allow subclasses (but not other classes) to invoke the event; C# does
111 // not allow subclasses to invoke the parent class event.
112 if (OnJointDeactivated != null)
113 {
114 OnJointDeactivated(joint);
115 }
116 }
117
118 public event JointErrorMessage OnJointErrorMessage;
119
120 protected virtual void DoJointErrorMessage(PhysicsJoint joint, string message)
121 {
122 // We need this to allow subclasses (but not other classes) to invoke the event; C# does
123 // not allow subclasses to invoke the parent class event.
124 if (OnJointErrorMessage != null)
125 {
126 OnJointErrorMessage(joint, message);
127 }
128 }
129
130 public virtual PhysicsVector GetJointAnchor(PhysicsJoint joint)
131 { return null; }
132
133 public virtual PhysicsVector GetJointAxis(PhysicsJoint joint)
134 { return null; }
135
136
76 public abstract void AddPhysicsActorTaint(PhysicsActor prim); 137 public abstract void AddPhysicsActorTaint(PhysicsActor prim);
77 138
78 public abstract float Simulate(float timeStep); 139 public abstract float Simulate(float timeStep);
@@ -181,4 +242,7 @@ namespace OpenSim.Region.Physics.Manager
181 } 242 }
182 } 243 }
183 } 244 }
245 public delegate void JointMoved(PhysicsJoint joint);
246 public delegate void JointDeactivated(PhysicsJoint joint);
247 public delegate void JointErrorMessage(PhysicsJoint joint, string message); // this refers to an "error message due to a problem", not "amount of joint constraint violation"
184} 248}
diff --git a/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs b/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs
index b9c0936..8dc8f78 100644
--- a/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs
+++ b/OpenSim/Region/Physics/OdePlugin/ODEPrim.cs
@@ -50,6 +50,7 @@ namespace OpenSim.Region.Physics.OdePlugin
50 private PhysicsVector _torque = new PhysicsVector(0,0,0); 50 private PhysicsVector _torque = new PhysicsVector(0,0,0);
51 private PhysicsVector m_lastVelocity = new PhysicsVector(0.0f, 0.0f, 0.0f); 51 private PhysicsVector m_lastVelocity = new PhysicsVector(0.0f, 0.0f, 0.0f);
52 private PhysicsVector m_lastposition = new PhysicsVector(0.0f, 0.0f, 0.0f); 52 private PhysicsVector m_lastposition = new PhysicsVector(0.0f, 0.0f, 0.0f);
53 private Quaternion m_lastorientation = new Quaternion();
53 private PhysicsVector m_rotationalVelocity; 54 private PhysicsVector m_rotationalVelocity;
54 private PhysicsVector _size; 55 private PhysicsVector _size;
55 private PhysicsVector _acceleration; 56 private PhysicsVector _acceleration;
@@ -1182,6 +1183,23 @@ namespace OpenSim.Region.Physics.OdePlugin
1182 // in between the disabling and the collision properties setting 1183 // in between the disabling and the collision properties setting
1183 // which would wake the physical body up from a soft disabling and potentially cause it to fall 1184 // which would wake the physical body up from a soft disabling and potentially cause it to fall
1184 // through the ground. 1185 // through the ground.
1186
1187 // NOTE FOR JOINTS: this doesn't always work for jointed assemblies because if you select
1188 // just one part of the assembly, the rest of the assembly is non-selected and still simulating,
1189 // so that causes the selected part to wake up and continue moving.
1190
1191 // even if you select all parts of a jointed assembly, it is not guaranteed that the entire
1192 // assembly will stop simulating during the selection, because of the lack of atomicity
1193 // of select operations (their processing could be interrupted by a thread switch, causing
1194 // simulation to continue before all of the selected object notifications trickle down to
1195 // the physics engine).
1196
1197 // e.g. we select 100 prims that are connected by joints. non-atomically, the first 50 are
1198 // selected and disabled. then, due to a thread switch, the selection processing is
1199 // interrupted and the physics engine continues to simulate, so the last 50 items, whose
1200 // selection was not yet processed, continues to simulate. this wakes up ALL of the
1201 // first 50 again. then the last 50 are disabled. then the first 50, which were just woken
1202 // up, start simulating again, which in turn wakes up the last 50.
1185 1203
1186 if (m_isphysical) 1204 if (m_isphysical)
1187 { 1205 {
@@ -2398,7 +2416,7 @@ namespace OpenSim.Region.Physics.OdePlugin
2398 { 2416 {
2399 PhysicsVector pv = new PhysicsVector(0, 0, 0); 2417 PhysicsVector pv = new PhysicsVector(0, 0, 0);
2400 bool lastZeroFlag = _zeroFlag; 2418 bool lastZeroFlag = _zeroFlag;
2401 if (Body != (IntPtr)0) 2419 if (Body != (IntPtr)0) // FIXME -> or if it is a joint
2402 { 2420 {
2403 d.Vector3 vec = d.BodyGetPosition(Body); 2421 d.Vector3 vec = d.BodyGetPosition(Body);
2404 d.Quaternion ori = d.BodyGetQuaternion(Body); 2422 d.Quaternion ori = d.BodyGetQuaternion(Body);
@@ -2407,6 +2425,7 @@ namespace OpenSim.Region.Physics.OdePlugin
2407 d.Vector3 torque = d.BodyGetTorque(Body); 2425 d.Vector3 torque = d.BodyGetTorque(Body);
2408 _torque.setValues(torque.X, torque.Y, torque.Z); 2426 _torque.setValues(torque.X, torque.Y, torque.Z);
2409 PhysicsVector l_position = new PhysicsVector(); 2427 PhysicsVector l_position = new PhysicsVector();
2428 Quaternion l_orientation = new Quaternion();
2410 2429
2411 // kluge to keep things in bounds. ODE lets dead avatars drift away (they should be removed!) 2430 // kluge to keep things in bounds. ODE lets dead avatars drift away (they should be removed!)
2412 //if (vec.X < 0.0f) { vec.X = 0.0f; if (Body != (IntPtr)0) d.BodySetAngularVel(Body, 0, 0, 0); } 2431 //if (vec.X < 0.0f) { vec.X = 0.0f; if (Body != (IntPtr)0) d.BodySetAngularVel(Body, 0, 0, 0); }
@@ -2415,10 +2434,15 @@ namespace OpenSim.Region.Physics.OdePlugin
2415 //if (vec.Y > 255.95f) { vec.Y = 255.95f; if (Body != (IntPtr)0) d.BodySetAngularVel(Body, 0, 0, 0); } 2434 //if (vec.Y > 255.95f) { vec.Y = 255.95f; if (Body != (IntPtr)0) d.BodySetAngularVel(Body, 0, 0, 0); }
2416 2435
2417 m_lastposition = _position; 2436 m_lastposition = _position;
2437 m_lastorientation = _orientation;
2418 2438
2419 l_position.X = vec.X; 2439 l_position.X = vec.X;
2420 l_position.Y = vec.Y; 2440 l_position.Y = vec.Y;
2421 l_position.Z = vec.Z; 2441 l_position.Z = vec.Z;
2442 l_orientation.X = ori.X;
2443 l_orientation.Y = ori.Y;
2444 l_orientation.Z = ori.Z;
2445 l_orientation.W = ori.W;
2422 2446
2423 if (l_position.X > 255.95f || l_position.X < 0f || l_position.Y > 255.95f || l_position.Y < 0f) 2447 if (l_position.X > 255.95f || l_position.X < 0f || l_position.Y > 255.95f || l_position.Y < 0f)
2424 { 2448 {
@@ -2474,7 +2498,8 @@ namespace OpenSim.Region.Physics.OdePlugin
2474 2498
2475 if ((Math.Abs(m_lastposition.X - l_position.X) < 0.02) 2499 if ((Math.Abs(m_lastposition.X - l_position.X) < 0.02)
2476 && (Math.Abs(m_lastposition.Y - l_position.Y) < 0.02) 2500 && (Math.Abs(m_lastposition.Y - l_position.Y) < 0.02)
2477 && (Math.Abs(m_lastposition.Z - l_position.Z) < 0.02)) 2501 && (Math.Abs(m_lastposition.Z - l_position.Z) < 0.02)
2502 && (1.0 - Math.Abs(Quaternion.Dot(m_lastorientation, l_orientation)) < 0.01 ))
2478 { 2503 {
2479 _zeroFlag = true; 2504 _zeroFlag = true;
2480 m_throttleUpdates = false; 2505 m_throttleUpdates = false;
diff --git a/OpenSim/Region/Physics/OdePlugin/OdePhysicsJoint.cs b/OpenSim/Region/Physics/OdePlugin/OdePhysicsJoint.cs
new file mode 100644
index 0000000..754ca2b
--- /dev/null
+++ b/OpenSim/Region/Physics/OdePlugin/OdePhysicsJoint.cs
@@ -0,0 +1,49 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyright
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSim Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28using System;
29using OpenMetaverse;
30using Ode.NET;
31using OpenSim.Framework;
32using OpenSim.Region.Physics.Manager;
33using OpenSim.Region.Physics.Manager;
34using OpenSim.Region.Physics.OdePlugin;
35
36namespace OpenSim.Region.Physics.OdePlugin
37{
38 class OdePhysicsJoint : PhysicsJoint
39 {
40 public override bool IsInPhysicsEngine
41 {
42 get
43 {
44 return (jointID != IntPtr.Zero);
45 }
46 }
47 public IntPtr jointID { get; set; }
48 }
49}
diff --git a/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs b/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs
index c0b4b45..acd2569 100644
--- a/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs
+++ b/OpenSim/Region/Physics/OdePlugin/OdePlugin.cs
@@ -32,6 +32,7 @@ using System.Reflection;
32using System.Runtime.InteropServices; 32using System.Runtime.InteropServices;
33using System.Threading; 33using System.Threading;
34using System.IO; 34using System.IO;
35using System.Diagnostics;
35using log4net; 36using log4net;
36using Nini.Config; 37using Nini.Config;
37using Ode.NET; 38using Ode.NET;
@@ -218,7 +219,17 @@ namespace OpenSim.Region.Physics.OdePlugin
218 private List<PhysicsActor> _collisionEventPrim = new List<PhysicsActor>(); 219 private List<PhysicsActor> _collisionEventPrim = new List<PhysicsActor>();
219 public Dictionary<IntPtr, String> geom_name_map = new Dictionary<IntPtr, String>(); 220 public Dictionary<IntPtr, String> geom_name_map = new Dictionary<IntPtr, String>();
220 public Dictionary<IntPtr, PhysicsActor> actor_name_map = new Dictionary<IntPtr, PhysicsActor>(); 221 public Dictionary<IntPtr, PhysicsActor> actor_name_map = new Dictionary<IntPtr, PhysicsActor>();
222 private bool m_NINJA_physics_joints_enabled = false;
223 //private Dictionary<String, IntPtr> jointpart_name_map = new Dictionary<String,IntPtr>();
224 private Dictionary<String, List<PhysicsJoint>> joints_connecting_actor = new Dictionary<String, List<PhysicsJoint>>();
221 private d.ContactGeom[] contacts = new d.ContactGeom[80]; 225 private d.ContactGeom[] contacts = new d.ContactGeom[80];
226 private List<PhysicsJoint> requestedJointsToBeCreated = new List<PhysicsJoint>(); // lock only briefly. accessed by external code (to request new joints) and by OdeScene.Simulate() to move those joints into pending/active
227 private List<PhysicsJoint> pendingJoints = new List<PhysicsJoint>(); // can lock for longer. accessed only by OdeScene.
228 private List<PhysicsJoint> activeJoints = new List<PhysicsJoint>(); // can lock for longer. accessed only by OdeScene.
229 private List<string> requestedJointsToBeDeleted = new List<string>(); // lock only briefly. accessed by external code (to request deletion of joints) and by OdeScene.Simulate() to move those joints out of pending/active
230 private Object externalJointRequestsLock = new Object();
231 private Dictionary<String, PhysicsJoint> SOPName_to_activeJoint = new Dictionary<String, PhysicsJoint>();
232 private Dictionary<String, PhysicsJoint> SOPName_to_pendingJoint = new Dictionary<String, PhysicsJoint>();
222 233
223 private d.Contact contact; 234 private d.Contact contact;
224 private d.Contact TerrainContact; 235 private d.Contact TerrainContact;
@@ -415,6 +426,9 @@ namespace OpenSim.Region.Physics.OdePlugin
415 physics_logging = physicsconfig.GetBoolean("physics_logging", false); 426 physics_logging = physicsconfig.GetBoolean("physics_logging", false);
416 physics_logging_interval = physicsconfig.GetInt("physics_logging_interval", 0); 427 physics_logging_interval = physicsconfig.GetInt("physics_logging_interval", 0);
417 physics_logging_append_existing_logfile = physicsconfig.GetBoolean("physics_logging_append_existing_logfile", false); 428 physics_logging_append_existing_logfile = physicsconfig.GetBoolean("physics_logging_append_existing_logfile", false);
429
430 m_NINJA_physics_joints_enabled = physicsconfig.GetBoolean("use_NINJA_physics_joints", false);
431
418 } 432 }
419 } 433 }
420 434
@@ -1347,6 +1361,341 @@ namespace OpenSim.Region.Physics.OdePlugin
1347 return result; 1361 return result;
1348 } 1362 }
1349 1363
1364 public override bool SupportsNINJAJoints
1365 {
1366 get { return m_NINJA_physics_joints_enabled; }
1367 }
1368
1369 // internal utility function: must be called within a lock(OdeLock)
1370 private void InternalAddActiveJoint(PhysicsJoint joint)
1371 {
1372 activeJoints.Add(joint);
1373 SOPName_to_activeJoint.Add(joint.ObjectNameInScene, joint);
1374 }
1375
1376 // internal utility function: must be called within a lock(OdeLock)
1377 private void InternalAddPendingJoint(OdePhysicsJoint joint)
1378 {
1379 pendingJoints.Add(joint);
1380 SOPName_to_pendingJoint.Add(joint.ObjectNameInScene, joint);
1381 }
1382
1383 // internal utility function: must be called within a lock(OdeLock)
1384 private void InternalRemovePendingJoint(PhysicsJoint joint)
1385 {
1386 pendingJoints.Remove(joint);
1387 SOPName_to_pendingJoint.Remove(joint.ObjectNameInScene);
1388 }
1389
1390 // internal utility function: must be called within a lock(OdeLock)
1391 private void InternalRemoveActiveJoint(PhysicsJoint joint)
1392 {
1393 activeJoints.Remove(joint);
1394 SOPName_to_activeJoint.Remove(joint.ObjectNameInScene);
1395 }
1396
1397 public override void DumpJointInfo()
1398 {
1399 string hdr = "[NINJA] JOINTINFO: ";
1400 foreach (PhysicsJoint j in pendingJoints)
1401 {
1402 m_log.Debug(hdr + " pending joint, Name: " + j.ObjectNameInScene + " raw parms:" + j.RawParams);
1403 }
1404 m_log.Debug(hdr + pendingJoints.Count + " total pending joints");
1405 foreach (string jointName in SOPName_to_pendingJoint.Keys)
1406 {
1407 m_log.Debug(hdr + " pending joints dict contains Name: " + jointName);
1408 }
1409 m_log.Debug(hdr + SOPName_to_pendingJoint.Keys.Count + " total pending joints dict entries");
1410 foreach (PhysicsJoint j in activeJoints)
1411 {
1412 m_log.Debug(hdr + " active joint, Name: " + j.ObjectNameInScene + " raw parms:" + j.RawParams);
1413 }
1414 m_log.Debug(hdr + activeJoints.Count + " total active joints");
1415 foreach (string jointName in SOPName_to_activeJoint.Keys)
1416 {
1417 m_log.Debug(hdr + " active joints dict contains Name: " + jointName);
1418 }
1419 m_log.Debug(hdr + SOPName_to_activeJoint.Keys.Count + " total active joints dict entries");
1420
1421 m_log.Debug(hdr + " Per-body joint connectivity information follows.");
1422 m_log.Debug(hdr + joints_connecting_actor.Keys.Count + " bodies are connected by joints.");
1423 foreach (string actorName in joints_connecting_actor.Keys)
1424 {
1425 m_log.Debug(hdr + " Actor " + actorName + " has the following joints connecting it");
1426 foreach (PhysicsJoint j in joints_connecting_actor[actorName])
1427 {
1428 m_log.Debug(hdr + " * joint Name: " + j.ObjectNameInScene + " raw parms:" + j.RawParams);
1429 }
1430 m_log.Debug(hdr + joints_connecting_actor[actorName].Count + " connecting joints total for this actor");
1431 }
1432 }
1433
1434 public override void RequestJointDeletion(string ObjectNameInScene)
1435 {
1436 lock (externalJointRequestsLock)
1437 {
1438 if (!requestedJointsToBeDeleted.Contains(ObjectNameInScene)) // forbid same deletion request from entering twice to prevent spurious deletions processed asynchronously
1439 {
1440 requestedJointsToBeDeleted.Add(ObjectNameInScene);
1441 }
1442 }
1443 }
1444
1445 private void DeleteRequestedJoints()
1446 {
1447 List<string> myRequestedJointsToBeDeleted;
1448 lock (externalJointRequestsLock)
1449 {
1450 // make a local copy of the shared list for processing (threading issues)
1451 myRequestedJointsToBeDeleted = new List<string>(requestedJointsToBeDeleted);
1452 }
1453
1454 foreach (string jointName in myRequestedJointsToBeDeleted)
1455 {
1456 lock (OdeLock)
1457 {
1458 //m_log.Debug("[NINJA] trying to deleting requested joint " + jointName);
1459 if (SOPName_to_activeJoint.ContainsKey(jointName) || SOPName_to_pendingJoint.ContainsKey(jointName))
1460 {
1461 OdePhysicsJoint joint = null;
1462 if (SOPName_to_activeJoint.ContainsKey(jointName))
1463 {
1464 joint = SOPName_to_activeJoint[jointName] as OdePhysicsJoint;
1465 InternalRemoveActiveJoint(joint);
1466 }
1467 else if (SOPName_to_pendingJoint.ContainsKey(jointName))
1468 {
1469 joint = SOPName_to_pendingJoint[jointName] as OdePhysicsJoint;
1470 InternalRemovePendingJoint(joint);
1471 }
1472
1473 if (joint != null)
1474 {
1475 //m_log.Debug("joint.BodyNames.Count is " + joint.BodyNames.Count + " and contents " + joint.BodyNames);
1476 for (int iBodyName = 0; iBodyName < 2; iBodyName++)
1477 {
1478 string bodyName = joint.BodyNames[iBodyName];
1479 if (bodyName != "NULL")
1480 {
1481 joints_connecting_actor[bodyName].Remove(joint);
1482 if (joints_connecting_actor[bodyName].Count == 0)
1483 {
1484 joints_connecting_actor.Remove(bodyName);
1485 }
1486 }
1487 }
1488
1489 DoJointDeactivated(joint);
1490 if (joint.jointID != IntPtr.Zero)
1491 {
1492 d.JointDestroy(joint.jointID);
1493 joint.jointID = IntPtr.Zero;
1494 //DoJointErrorMessage(joint, "successfully destroyed joint " + jointName);
1495 }
1496 else
1497 {
1498 //m_log.Warn("[NINJA] Ignoring re-request to destroy joint " + jointName);
1499 }
1500 }
1501 else
1502 {
1503 // DoJointErrorMessage(joint, "coult not find joint to destroy based on name " + jointName);
1504 }
1505 }
1506 else
1507 {
1508 // DoJointErrorMessage(joint, "WARNING - joint removal failed, joint " + jointName);
1509 }
1510 }
1511 }
1512
1513 // remove processed joints from the shared list
1514 lock (externalJointRequestsLock)
1515 {
1516 foreach (string jointName in myRequestedJointsToBeDeleted)
1517 {
1518 requestedJointsToBeDeleted.Remove(jointName);
1519 }
1520 }
1521 }
1522
1523 // for pending joints we don't know if their associated bodies exist yet or not.
1524 // the joint is actually created during processing of the taints
1525 private void CreateRequestedJoints()
1526 {
1527 List<PhysicsJoint> myRequestedJointsToBeCreated;
1528 lock (externalJointRequestsLock)
1529 {
1530 // make a local copy of the shared list for processing (threading issues)
1531 myRequestedJointsToBeCreated = new List<PhysicsJoint>(requestedJointsToBeCreated);
1532 }
1533
1534 foreach (PhysicsJoint joint in myRequestedJointsToBeCreated)
1535 {
1536 lock (OdeLock)
1537 {
1538 if (SOPName_to_pendingJoint.ContainsKey(joint.ObjectNameInScene) && SOPName_to_pendingJoint[joint.ObjectNameInScene] != null)
1539 {
1540 DoJointErrorMessage(joint, "WARNING: ignoring request to re-add already pending joint Name:" + joint.ObjectNameInScene + " type:" + joint.Type + " parms: " + joint.RawParams + " pos: " + joint.Position + " rot:" + joint.Rotation);
1541 continue;
1542 }
1543 if (SOPName_to_activeJoint.ContainsKey(joint.ObjectNameInScene) && SOPName_to_activeJoint[joint.ObjectNameInScene] != null)
1544 {
1545 DoJointErrorMessage(joint, "WARNING: ignoring request to re-add already active joint Name:" + joint.ObjectNameInScene + " type:" + joint.Type + " parms: " + joint.RawParams + " pos: " + joint.Position + " rot:" + joint.Rotation);
1546 continue;
1547 }
1548
1549 InternalAddPendingJoint(joint as OdePhysicsJoint);
1550
1551 if (joint.BodyNames.Count >= 2)
1552 {
1553 for (int iBodyName = 0; iBodyName < 2; iBodyName++)
1554 {
1555 string bodyName = joint.BodyNames[iBodyName];
1556 if (bodyName != "NULL")
1557 {
1558 if (!joints_connecting_actor.ContainsKey(bodyName))
1559 {
1560 joints_connecting_actor.Add(bodyName, new List<PhysicsJoint>());
1561 }
1562 joints_connecting_actor[bodyName].Add(joint);
1563 }
1564 }
1565 }
1566 }
1567 }
1568
1569 // remove processed joints from shared list
1570 lock (externalJointRequestsLock)
1571 {
1572 foreach (PhysicsJoint joint in myRequestedJointsToBeCreated)
1573 {
1574 requestedJointsToBeCreated.Remove(joint);
1575 }
1576 }
1577
1578 }
1579
1580 // public function to add an request for joint creation
1581 // this joint will just be added to a waiting list that is NOT processed during the main
1582 // Simulate() loop (to avoid deadlocks). After Simulate() is finished, we handle unprocessed joint requests.
1583
1584 public override PhysicsJoint RequestJointCreation(string objectNameInScene, PhysicsJointType jointType, PhysicsVector position,
1585 Quaternion rotation, string parms, List<string> bodyNames, string trackedBodyName, Quaternion localRotation)
1586
1587 {
1588
1589 OdePhysicsJoint joint = new OdePhysicsJoint();
1590 joint.ObjectNameInScene = objectNameInScene;
1591 joint.Type = jointType;
1592 joint.Position = new PhysicsVector(position.X, position.Y, position.Z);
1593 joint.Rotation = rotation;
1594 joint.RawParams = parms;
1595 joint.BodyNames = new List<string>(bodyNames);
1596 joint.TrackedBodyName = trackedBodyName;
1597 joint.LocalRotation = localRotation;
1598 joint.jointID = IntPtr.Zero;
1599 joint.ErrorMessageCount = 0;
1600
1601 lock (externalJointRequestsLock)
1602 {
1603 if (!requestedJointsToBeCreated.Contains(joint)) // forbid same creation request from entering twice
1604 {
1605 requestedJointsToBeCreated.Add(joint);
1606 }
1607 }
1608 return joint;
1609 }
1610
1611 private void RemoveAllJointsConnectedToActor(PhysicsActor actor)
1612 {
1613 //m_log.Debug("RemoveAllJointsConnectedToActor: start");
1614 if (actor.SOPName != null && joints_connecting_actor.ContainsKey(actor.SOPName) && joints_connecting_actor[actor.SOPName] != null)
1615 {
1616
1617 List<PhysicsJoint> jointsToRemove = new List<PhysicsJoint>();
1618 //TODO: merge these 2 loops (originally it was needed to avoid altering a list being iterated over, but it is no longer needed due to the joint request queue mechanism)
1619 foreach (PhysicsJoint j in joints_connecting_actor[actor.SOPName])
1620 {
1621 jointsToRemove.Add(j);
1622 }
1623 foreach (PhysicsJoint j in jointsToRemove)
1624 {
1625 //m_log.Debug("RemoveAllJointsConnectedToActor: about to request deletion of " + j.ObjectNameInScene);
1626 RequestJointDeletion(j.ObjectNameInScene);
1627 //m_log.Debug("RemoveAllJointsConnectedToActor: done request deletion of " + j.ObjectNameInScene);
1628 j.TrackedBodyName = null; // *IMMEDIATELY* prevent any further movement of this joint (else a deleted actor might cause spurious tracking motion of the joint for a few frames, leading to the joint proxy object disappearing)
1629 }
1630 }
1631 }
1632
1633 public override void RemoveAllJointsConnectedToActorThreadLocked(PhysicsActor actor)
1634 {
1635 //m_log.Debug("RemoveAllJointsConnectedToActorThreadLocked: start");
1636 lock (OdeLock)
1637 {
1638 //m_log.Debug("RemoveAllJointsConnectedToActorThreadLocked: got lock");
1639 RemoveAllJointsConnectedToActor(actor);
1640 }
1641 }
1642
1643 // normally called from within OnJointMoved, which is called from within a lock(OdeLock)
1644 public override PhysicsVector GetJointAnchor(PhysicsJoint joint)
1645 {
1646 Debug.Assert(joint.IsInPhysicsEngine);
1647 d.Vector3 pos = new d.Vector3();
1648
1649 if (!(joint is OdePhysicsJoint))
1650 {
1651 DoJointErrorMessage(joint, "warning: non-ODE joint requesting anchor: " + joint.ObjectNameInScene);
1652 }
1653 else
1654 {
1655 OdePhysicsJoint odeJoint = (OdePhysicsJoint)joint;
1656 switch (odeJoint.Type)
1657 {
1658 case PhysicsJointType.Ball:
1659 d.JointGetBallAnchor(odeJoint.jointID, out pos);
1660 break;
1661 case PhysicsJointType.Hinge:
1662 d.JointGetHingeAnchor(odeJoint.jointID, out pos);
1663 break;
1664 }
1665 }
1666 return new PhysicsVector(pos.X, pos.Y, pos.Z);
1667 }
1668
1669 // normally called from within OnJointMoved, which is called from within a lock(OdeLock)
1670 // WARNING: ODE sometimes returns <0,0,0> as the joint axis! Therefore this function
1671 // appears to be unreliable. Fortunately we can compute the joint axis ourselves by
1672 // keeping track of the joint's original orientation relative to one of the involved bodies.
1673 public override PhysicsVector GetJointAxis(PhysicsJoint joint)
1674 {
1675 Debug.Assert(joint.IsInPhysicsEngine);
1676 d.Vector3 axis = new d.Vector3();
1677
1678 if (!(joint is OdePhysicsJoint))
1679 {
1680 DoJointErrorMessage(joint, "warning: non-ODE joint requesting anchor: " + joint.ObjectNameInScene);
1681 }
1682 else
1683 {
1684 OdePhysicsJoint odeJoint = (OdePhysicsJoint)joint;
1685 switch (odeJoint.Type)
1686 {
1687 case PhysicsJointType.Ball:
1688 DoJointErrorMessage(joint, "warning - axis requested for ball joint: " + joint.ObjectNameInScene);
1689 break;
1690 case PhysicsJointType.Hinge:
1691 d.JointGetHingeAxis(odeJoint.jointID, out axis);
1692 break;
1693 }
1694 }
1695 return new PhysicsVector(axis.X, axis.Y, axis.Z);
1696 }
1697
1698
1350 public void remActivePrim(OdePrim deactivatePrim) 1699 public void remActivePrim(OdePrim deactivatePrim)
1351 { 1700 {
1352 lock (_activeprims) 1701 lock (_activeprims)
@@ -1468,6 +1817,11 @@ namespace OpenSim.Region.Physics.OdePlugin
1468 //} 1817 //}
1469 //} 1818 //}
1470 //} 1819 //}
1820
1821 if (SupportsNINJAJoints)
1822 {
1823 RemoveAllJointsConnectedToActorThreadLocked(prim);
1824 }
1471 } 1825 }
1472 } 1826 }
1473 } 1827 }
@@ -1873,6 +2227,13 @@ namespace OpenSim.Region.Physics.OdePlugin
1873 { 2227 {
1874 m_physicsiterations = 10; 2228 m_physicsiterations = 10;
1875 } 2229 }
2230
2231 if (SupportsNINJAJoints)
2232 {
2233 DeleteRequestedJoints(); // this must be outside of the lock(OdeLock) to avoid deadlocks
2234 CreateRequestedJoints(); // this must be outside of the lock(OdeLock) to avoid deadlocks
2235 }
2236
1876 lock (OdeLock) 2237 lock (OdeLock)
1877 { 2238 {
1878 // Process 10 frames if the sim is running normal.. 2239 // Process 10 frames if the sim is running normal..
@@ -1944,6 +2305,188 @@ namespace OpenSim.Region.Physics.OdePlugin
1944 prim.m_collisionscore = 0; 2305 prim.m_collisionscore = 0;
1945 } 2306 }
1946 2307
2308 if (SupportsNINJAJoints)
2309 {
2310 // Create pending joints, if possible
2311
2312 // joints can only be processed after ALL bodies are processed (and exist in ODE), since creating
2313 // a joint requires specifying the body id of both involved bodies
2314 if (pendingJoints.Count > 0)
2315 {
2316 List<PhysicsJoint> successfullyProcessedPendingJoints = new List<PhysicsJoint>();
2317 //DoJointErrorMessage(joints_connecting_actor, "taint: " + pendingJoints.Count + " pending joints");
2318 foreach (PhysicsJoint joint in pendingJoints)
2319 {
2320 //DoJointErrorMessage(joint, "taint: time to create joint with parms: " + joint.RawParams);
2321 string[] jointParams = joint.RawParams.Split(' ');
2322 List<IntPtr> jointBodies = new List<IntPtr>();
2323 bool allJointBodiesAreReady = true;
2324 foreach (string jointParam in jointParams)
2325 {
2326 if (jointParam == "NULL")
2327 {
2328 //DoJointErrorMessage(joint, "attaching NULL joint to world");
2329 jointBodies.Add(IntPtr.Zero);
2330 }
2331 else
2332 {
2333 //DoJointErrorMessage(joint, "looking for prim name: " + jointParam);
2334 bool foundPrim = false;
2335 lock (_prims)
2336 {
2337 foreach (OdePrim prim in _prims) // FIXME: inefficient
2338 {
2339 if (prim.SOPName == jointParam)
2340 {
2341 //DoJointErrorMessage(joint, "found for prim name: " + jointParam);
2342 if (prim.IsPhysical && prim.Body != IntPtr.Zero)
2343 {
2344 jointBodies.Add(prim.Body);
2345 foundPrim = true;
2346 break;
2347 }
2348 else
2349 {
2350 DoJointErrorMessage(joint, "prim name " + jointParam +
2351 " exists but is not (yet) physical; deferring joint creation. " +
2352 "IsPhysical property is " + prim.IsPhysical +
2353 " and body is " + prim.Body);
2354 foundPrim = false;
2355 break;
2356 }
2357 }
2358 }
2359 }
2360 if (foundPrim)
2361 {
2362 // all is fine
2363 }
2364 else
2365 {
2366 allJointBodiesAreReady = false;
2367 break;
2368 }
2369 }
2370 }
2371 if (allJointBodiesAreReady)
2372 {
2373 //DoJointErrorMessage(joint, "allJointBodiesAreReady for " + joint.ObjectNameInScene + " with parms " + joint.RawParams);
2374 if (jointBodies[0] == jointBodies[1])
2375 {
2376 DoJointErrorMessage(joint, "ERROR: joint cannot be created; the joint bodies are the same, body1==body2. Raw body is " + jointBodies[0] + ". raw parms: " + joint.RawParams);
2377 }
2378 else
2379 {
2380 switch (joint.Type)
2381 {
2382 case PhysicsJointType.Ball:
2383 {
2384 IntPtr odeJoint;
2385 //DoJointErrorMessage(joint, "ODE creating ball joint ");
2386 odeJoint = d.JointCreateBall(world, IntPtr.Zero);
2387 //DoJointErrorMessage(joint, "ODE attaching ball joint: " + odeJoint + " with b1:" + jointBodies[0] + " b2:" + jointBodies[1]);
2388 d.JointAttach(odeJoint, jointBodies[0], jointBodies[1]);
2389 //DoJointErrorMessage(joint, "ODE setting ball anchor: " + odeJoint + " to vec:" + joint.Position);
2390 d.JointSetBallAnchor(odeJoint,
2391 joint.Position.X,
2392 joint.Position.Y,
2393 joint.Position.Z);
2394 //DoJointErrorMessage(joint, "ODE joint setting OK");
2395 //DoJointErrorMessage(joint, "The ball joint's bodies are here: b0: ");
2396 //DoJointErrorMessage(joint, "" + (jointBodies[0] != IntPtr.Zero ? "" + d.BodyGetPosition(jointBodies[0]) : "fixed environment"));
2397 //DoJointErrorMessage(joint, "The ball joint's bodies are here: b1: ");
2398 //DoJointErrorMessage(joint, "" + (jointBodies[1] != IntPtr.Zero ? "" + d.BodyGetPosition(jointBodies[1]) : "fixed environment"));
2399
2400 if (joint is OdePhysicsJoint)
2401 {
2402 ((OdePhysicsJoint)joint).jointID = odeJoint;
2403 }
2404 else
2405 {
2406 DoJointErrorMessage(joint, "WARNING: non-ode joint in ODE!");
2407 }
2408 }
2409 break;
2410 case PhysicsJointType.Hinge:
2411 {
2412 IntPtr odeJoint;
2413 //DoJointErrorMessage(joint, "ODE creating hinge joint ");
2414 odeJoint = d.JointCreateHinge(world, IntPtr.Zero);
2415 //DoJointErrorMessage(joint, "ODE attaching hinge joint: " + odeJoint + " with b1:" + jointBodies[0] + " b2:" + jointBodies[1]);
2416 d.JointAttach(odeJoint, jointBodies[0], jointBodies[1]);
2417 //DoJointErrorMessage(joint, "ODE setting hinge anchor: " + odeJoint + " to vec:" + joint.Position);
2418 d.JointSetHingeAnchor(odeJoint,
2419 joint.Position.X,
2420 joint.Position.Y,
2421 joint.Position.Z);
2422 // We use the orientation of the x-axis of the joint's coordinate frame
2423 // as the axis for the hinge.
2424
2425 // Therefore, we must get the joint's coordinate frame based on the
2426 // joint.Rotation field, which originates from the orientation of the
2427 // joint's proxy object in the scene.
2428
2429 // The joint's coordinate frame is defined as the transformation matrix
2430 // that converts a vector from joint-local coordinates into world coordinates.
2431 // World coordinates are defined as the XYZ coordinate system of the sim,
2432 // as shown in the top status-bar of the viewer.
2433
2434 // Once we have the joint's coordinate frame, we extract its X axis (AtAxis)
2435 // and use that as the hinge axis.
2436
2437 //joint.Rotation.Normalize();
2438 Matrix4 proxyFrame = Matrix4.CreateFromQuaternion(joint.Rotation);
2439
2440 // Now extract the X axis of the joint's coordinate frame.
2441
2442 // Do not try to use proxyFrame.AtAxis or you will become mired in the
2443 // tar pit of transposed, inverted, and generally messed-up orientations.
2444 // (In other words, Matrix4.AtAxis() is borked.)
2445 // Vector3 jointAxis = proxyFrame.AtAxis; <--- this path leadeth to madness
2446
2447 // Instead, compute the X axis of the coordinate frame by transforming
2448 // the (1,0,0) vector. At least that works.
2449
2450 //m_log.Debug("PHY: making axis: complete matrix is " + proxyFrame);
2451 Vector3 jointAxis = Vector3.Transform(Vector3.UnitX, proxyFrame);
2452 //m_log.Debug("PHY: making axis: hinge joint axis is " + jointAxis);
2453 //DoJointErrorMessage(joint, "ODE setting hinge axis: " + odeJoint + " to vec:" + jointAxis);
2454 d.JointSetHingeAxis(odeJoint,
2455 jointAxis.X,
2456 jointAxis.Y,
2457 jointAxis.Z);
2458 //d.JointSetHingeParam(odeJoint, (int)dParam.CFM, 0.1f);
2459 if (joint is OdePhysicsJoint)
2460 {
2461 ((OdePhysicsJoint)joint).jointID = odeJoint;
2462 }
2463 else
2464 {
2465 DoJointErrorMessage(joint, "WARNING: non-ode joint in ODE!");
2466 }
2467 }
2468 break;
2469 }
2470 successfullyProcessedPendingJoints.Add(joint);
2471 }
2472 }
2473 else
2474 {
2475 DoJointErrorMessage(joint, "joint could not yet be created; still pending");
2476 }
2477 }
2478 foreach (PhysicsJoint successfullyProcessedJoint in successfullyProcessedPendingJoints)
2479 {
2480 //DoJointErrorMessage(successfullyProcessedJoint, "finalizing succesfully procsssed joint " + successfullyProcessedJoint.ObjectNameInScene + " parms " + successfullyProcessedJoint.RawParams);
2481 //DoJointErrorMessage(successfullyProcessedJoint, "removing from pending");
2482 InternalRemovePendingJoint(successfullyProcessedJoint);
2483 //DoJointErrorMessage(successfullyProcessedJoint, "adding to active");
2484 InternalAddActiveJoint(successfullyProcessedJoint);
2485 //DoJointErrorMessage(successfullyProcessedJoint, "done");
2486 }
2487 }
2488 }
2489
1947 if (processedtaints) 2490 if (processedtaints)
1948 _taintedPrim.Clear(); 2491 _taintedPrim.Clear();
1949 } 2492 }
@@ -2032,11 +2575,39 @@ namespace OpenSim.Region.Physics.OdePlugin
2032 if (actor.IsPhysical && (d.BodyIsEnabled(actor.Body) || !actor._zeroFlag)) 2575 if (actor.IsPhysical && (d.BodyIsEnabled(actor.Body) || !actor._zeroFlag))
2033 { 2576 {
2034 actor.UpdatePositionAndVelocity(); 2577 actor.UpdatePositionAndVelocity();
2578
2579 if (SupportsNINJAJoints)
2580 {
2581 // If an actor moved, move its joint proxy objects as well.
2582 // There seems to be an event PhysicsActor.OnPositionUpdate that could be used
2583 // for this purpose but it is never called! So we just do the joint
2584 // movement code here.
2585
2586 if (actor.SOPName != null &&
2587 joints_connecting_actor.ContainsKey(actor.SOPName) &&
2588 joints_connecting_actor[actor.SOPName] != null &&
2589 joints_connecting_actor[actor.SOPName].Count > 0)
2590 {
2591 foreach (PhysicsJoint affectedJoint in joints_connecting_actor[actor.SOPName])
2592 {
2593 if (affectedJoint.IsInPhysicsEngine)
2594 {
2595 DoJointMoved(affectedJoint);
2596 }
2597 else
2598 {
2599 DoJointErrorMessage(affectedJoint, "a body connected to a joint was moved, but the joint doesn't exist yet! this will lead to joint error. joint was: " + affectedJoint.ObjectNameInScene + " parms:" + affectedJoint.RawParams);
2600 }
2601 }
2602 }
2603 }
2035 } 2604 }
2036 } 2605 }
2037 } 2606 }
2038 } 2607 }
2039 2608
2609 //DumpJointInfo();
2610
2040 // Finished with all sim stepping. If requested, dump world state to file for debugging. 2611 // Finished with all sim stepping. If requested, dump world state to file for debugging.
2041 // TODO: This call to the export function is already inside lock (OdeLock) - but is an extra lock needed? 2612 // TODO: This call to the export function is already inside lock (OdeLock) - but is an extra lock needed?
2042 // TODO: This overwrites all dump files in-place. Should this be a growing logfile, or separate snapshots? 2613 // TODO: This overwrites all dump files in-place. Should this be a growing logfile, or separate snapshots?
diff --git a/bin/OpenSim.ini.example b/bin/OpenSim.ini.example
index 0f48fb9..bd48f8c 100644
--- a/bin/OpenSim.ini.example
+++ b/bin/OpenSim.ini.example
@@ -434,6 +434,13 @@
434 ;; append to existing physics logfile, or overwrite existing logfiles? 434 ;; append to existing physics logfile, or overwrite existing logfiles?
435 ;physics_logging_append_existing_logfile = true 435 ;physics_logging_append_existing_logfile = true
436 436
437 ; ##
438 ; ## Joint support
439 ; ##
440
441 ; if you would like physics joints to be enabled through a special naming convention in the client, set this to true. (see NINJA Physics documentation, http://opensimulator.org/wiki/NINJA_Physics)
442 ;use_NINJA_physics_joints = true
443
437 444
438[RemoteAdmin] 445[RemoteAdmin]
439 enabled = false 446 enabled = false