aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Physics
diff options
context:
space:
mode:
authorTeravus Ovares2008-12-26 12:58:02 +0000
committerTeravus Ovares2008-12-26 12:58:02 +0000
commitec2dc354b485491d7879686b4a78027971e3ed92 (patch)
tree3ea0771a55524ce1b9dec3c3c66f0dac24a006ef /OpenSim/Region/Physics
parentPrevent exception in terrain module if just the word terrain is entered at th... (diff)
downloadopensim-SC_OLD-ec2dc354b485491d7879686b4a78027971e3ed92.zip
opensim-SC_OLD-ec2dc354b485491d7879686b4a78027971e3ed92.tar.gz
opensim-SC_OLD-ec2dc354b485491d7879686b4a78027971e3ed92.tar.bz2
opensim-SC_OLD-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.
Diffstat (limited to 'OpenSim/Region/Physics')
-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
6 files changed, 769 insertions, 2 deletions
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?