diff options
author | Teravus Ovares | 2008-12-26 12:58:02 +0000 |
---|---|---|
committer | Teravus Ovares | 2008-12-26 12:58:02 +0000 |
commit | ec2dc354b485491d7879686b4a78027971e3ed92 (patch) | |
tree | 3ea0771a55524ce1b9dec3c3c66f0dac24a006ef | |
parent | Prevent exception in terrain module if just the word terrain is entered at th... (diff) | |
download | opensim-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.cs | 167 | ||||
-rw-r--r-- | OpenSim/Region/Environment/Scenes/SceneGraph.cs | 17 | ||||
-rw-r--r-- | OpenSim/Region/Environment/Scenes/SceneObjectPart.cs | 282 | ||||
-rw-r--r-- | OpenSim/Region/Physics/Manager/PhysicsActor.cs | 3 | ||||
-rw-r--r-- | OpenSim/Region/Physics/Manager/PhysicsJoint.cs | 55 | ||||
-rw-r--r-- | OpenSim/Region/Physics/Manager/PhysicsScene.cs | 64 | ||||
-rw-r--r-- | OpenSim/Region/Physics/OdePlugin/ODEPrim.cs | 29 | ||||
-rw-r--r-- | OpenSim/Region/Physics/OdePlugin/OdePhysicsJoint.cs | 49 | ||||
-rw-r--r-- | OpenSim/Region/Physics/OdePlugin/OdePlugin.cs | 571 | ||||
-rw-r--r-- | bin/OpenSim.ini.example | 7 |
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 | |||
28 | using System; | ||
29 | using System.Collections.Generic; | ||
30 | using OpenSim.Framework; | ||
31 | using OpenMetaverse; | ||
32 | |||
33 | namespace 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 | |||
28 | using System; | ||
29 | using OpenMetaverse; | ||
30 | using Ode.NET; | ||
31 | using OpenSim.Framework; | ||
32 | using OpenSim.Region.Physics.Manager; | ||
33 | using OpenSim.Region.Physics.Manager; | ||
34 | using OpenSim.Region.Physics.OdePlugin; | ||
35 | |||
36 | namespace 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; | |||
32 | using System.Runtime.InteropServices; | 32 | using System.Runtime.InteropServices; |
33 | using System.Threading; | 33 | using System.Threading; |
34 | using System.IO; | 34 | using System.IO; |
35 | using System.Diagnostics; | ||
35 | using log4net; | 36 | using log4net; |
36 | using Nini.Config; | 37 | using Nini.Config; |
37 | using Ode.NET; | 38 | using 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 |