aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
diff options
context:
space:
mode:
authorubit2012-12-03 21:26:36 +0100
committerubit2012-12-03 21:26:36 +0100
commit5d4b7d537d872d27c394402d3483c2a9bd2153f0 (patch)
tree39aae50386555bf024700314fb7bee5121a9d683 /OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
parentMerge branch 'ubitwork' of ssh://3dhosting.de/var/git/careminster into ubitwork (diff)
parentMerge branch 'avination' into ubitwork (diff)
downloadopensim-SC-5d4b7d537d872d27c394402d3483c2a9bd2153f0.zip
opensim-SC-5d4b7d537d872d27c394402d3483c2a9bd2153f0.tar.gz
opensim-SC-5d4b7d537d872d27c394402d3483c2a9bd2153f0.tar.bz2
opensim-SC-5d4b7d537d872d27c394402d3483c2a9bd2153f0.tar.xz
Merge branch 'ubitwork' of ssh://3dhosting.de/var/git/careminster into ubitwork
Diffstat (limited to 'OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs')
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs1392
1 files changed, 731 insertions, 661 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
index 9c20004..2b3fa25 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs
@@ -24,6 +24,7 @@
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */ 26 */
27
27using System; 28using System;
28using System.Reflection; 29using System.Reflection;
29using System.Collections.Generic; 30using System.Collections.Generic;
@@ -36,32 +37,17 @@ using OpenSim.Region.Physics.ConvexDecompositionDotNet;
36 37
37namespace OpenSim.Region.Physics.BulletSPlugin 38namespace OpenSim.Region.Physics.BulletSPlugin
38{ 39{
40
39 [Serializable] 41 [Serializable]
40public sealed class BSPrim : PhysicsActor 42public sealed class BSPrim : BSPhysObject
41{ 43{
42 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); 44 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
43 private static readonly string LogHeader = "[BULLETS PRIM]"; 45 private static readonly string LogHeader = "[BULLETS PRIM]";
44 46
45 private void DebugLog(string mm, params Object[] xx) { if (_scene.ShouldDebugLog) m_log.DebugFormat(mm, xx); } 47 // _size is what the user passed. Scale is what we pass to the physics engine with the mesh.
46 48 // Often Scale is unity because the meshmerizer will apply _size when creating the mesh.
47 private IMesh _mesh;
48 private PrimitiveBaseShape _pbs;
49 private ShapeData.PhysicsShapeType _shapeType;
50 private ulong _meshKey;
51 private ulong _hullKey;
52 private List<ConvexResult> _hulls;
53
54 private BSScene _scene;
55 public BSScene Scene { get { return _scene; } }
56 private String _avName;
57 private uint _localID = 0;
58
59 // _size is what the user passed. _scale is what we pass to the physics engine with the mesh.
60 // Often _scale is unity because the meshmerizer will apply _size when creating the mesh.
61 private OMV.Vector3 _size; // the multiplier for each mesh dimension as passed by the user 49 private OMV.Vector3 _size; // the multiplier for each mesh dimension as passed by the user
62 private OMV.Vector3 _scale; // the multiplier for each mesh dimension for the mesh as created by the meshmerizer
63 50
64 private bool _stopped;
65 private bool _grabbed; 51 private bool _grabbed;
66 private bool _isSelected; 52 private bool _isSelected;
67 private bool _isVolumeDetect; 53 private bool _isVolumeDetect;
@@ -89,25 +75,6 @@ public sealed class BSPrim : PhysicsActor
89 private bool _kinematic; 75 private bool _kinematic;
90 private float _buoyancy; 76 private float _buoyancy;
91 77
92 // Membership in a linkset is controlled by this class.
93 private BSLinkset _linkset;
94 public BSLinkset Linkset
95 {
96 get { return _linkset; }
97 set { _linkset = value; }
98 }
99
100 private int _subscribedEventsMs = 0;
101 private int _nextCollisionOkTime = 0;
102 long _collidingStep;
103 long _collidingGroundStep;
104
105 private BulletBody m_body;
106 public BulletBody Body {
107 get { return m_body; }
108 set { m_body = value; }
109 }
110
111 private BSDynamics _vehicle; 78 private BSDynamics _vehicle;
112 79
113 private OMV.Vector3 _PIDTarget; 80 private OMV.Vector3 _PIDTarget;
@@ -120,110 +87,116 @@ public sealed class BSPrim : PhysicsActor
120 87
121 public BSPrim(uint localID, String primName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size, 88 public BSPrim(uint localID, String primName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size,
122 OMV.Quaternion rotation, PrimitiveBaseShape pbs, bool pisPhysical) 89 OMV.Quaternion rotation, PrimitiveBaseShape pbs, bool pisPhysical)
90 : base(parent_scene, localID, primName, "BSPrim")
123 { 91 {
124 // m_log.DebugFormat("{0}: BSPrim creation of {1}, id={2}", LogHeader, primName, localID); 92 // m_log.DebugFormat("{0}: BSPrim creation of {1}, id={2}", LogHeader, primName, localID);
125 _localID = localID; 93 _physicsActorType = (int)ActorTypes.Prim;
126 _avName = primName;
127 _scene = parent_scene;
128 _position = pos; 94 _position = pos;
129 _size = size; 95 _size = size;
130 _scale = new OMV.Vector3(1f, 1f, 1f); // the scale will be set by CreateGeom depending on object type 96 Scale = size; // the scale will be set by CreateGeom depending on object type
131 _orientation = rotation; 97 _orientation = rotation;
132 _buoyancy = 1f; 98 _buoyancy = 1f;
133 _velocity = OMV.Vector3.Zero; 99 _velocity = OMV.Vector3.Zero;
134 _rotationalVelocity = OMV.Vector3.Zero; 100 _rotationalVelocity = OMV.Vector3.Zero;
135 _hullKey = 0; 101 BaseShape = pbs;
136 _meshKey = 0;
137 _pbs = pbs;
138 _isPhysical = pisPhysical; 102 _isPhysical = pisPhysical;
139 _isVolumeDetect = false; 103 _isVolumeDetect = false;
140 _subscribedEventsMs = 0; 104 _friction = PhysicsScene.Params.defaultFriction; // TODO: compute based on object material
141 _friction = _scene.Params.defaultFriction; // TODO: compute based on object material 105 _density = PhysicsScene.Params.defaultDensity; // TODO: compute based on object material
142 _density = _scene.Params.defaultDensity; // TODO: compute based on object material 106 _restitution = PhysicsScene.Params.defaultRestitution;
143 _restitution = _scene.Params.defaultRestitution; 107 _vehicle = new BSDynamics(PhysicsScene, this); // add vehicleness
144 _linkset = new BSLinkset(_scene, this); // a linkset of one
145 _vehicle = new BSDynamics(this); // add vehicleness
146 _mass = CalculateMass(); 108 _mass = CalculateMass();
147 // do the actual object creation at taint time 109
110 // No body or shape yet
111 PhysBody = new BulletBody(LocalID, IntPtr.Zero);
112 PhysShape = new BulletShape(IntPtr.Zero);
113
148 DetailLog("{0},BSPrim.constructor,call", LocalID); 114 DetailLog("{0},BSPrim.constructor,call", LocalID);
149 _scene.TaintedObject("BSPrim.create", delegate() 115 // do the actual object creation at taint time
116 PhysicsScene.TaintedObject("BSPrim.create", delegate()
150 { 117 {
151 RecreateGeomAndObject(); 118 CreateGeomAndObject(true);
152 119
153 // Get the pointer to the physical body for this object. 120 CurrentCollisionFlags = BulletSimAPI.GetCollisionFlags2(PhysBody.ptr);
154 // At the moment, we're still letting BulletSim manage the creation and destruction
155 // of the object. Someday we'll move that into the C# code.
156 m_body = new BulletBody(LocalID, BulletSimAPI.GetBodyHandle2(_scene.World.Ptr, LocalID));
157 }); 121 });
158 } 122 }
159 123
160 // called when this prim is being destroyed and we should free all the resources 124 // called when this prim is being destroyed and we should free all the resources
161 public void Destroy() 125 public override void Destroy()
162 { 126 {
163 // m_log.DebugFormat("{0}: Destroy, id={1}", LogHeader, LocalID); 127 // m_log.DebugFormat("{0}: Destroy, id={1}", LogHeader, LocalID);
164 128
165 // Undo any links between me and any other object 129 // Undo any links between me and any other object
166 BSPrim parentBefore = _linkset.LinksetRoot; 130 BSPhysObject parentBefore = Linkset.LinksetRoot;
167 int childrenBefore = _linkset.NumberOfChildren; 131 int childrenBefore = Linkset.NumberOfChildren;
168 132
169 _linkset = _linkset.RemoveMeFromLinkset(this); 133 Linkset = Linkset.RemoveMeFromLinkset(this);
170 134
171 DetailLog("{0},BSPrim.Destroy,call,parentBefore={1},childrenBefore={2},parentAfter={3},childrenAfter={4}", 135 DetailLog("{0},BSPrim.Destroy,call,parentBefore={1},childrenBefore={2},parentAfter={3},childrenAfter={4}",
172 LocalID, parentBefore.LocalID, childrenBefore, _linkset.LinksetRoot.LocalID, _linkset.NumberOfChildren); 136 LocalID, parentBefore.LocalID, childrenBefore, Linkset.LinksetRoot.LocalID, Linkset.NumberOfChildren);
173 137
174 // Undo any vehicle properties 138 // Undo any vehicle properties
175 this.VehicleType = (int)Vehicle.TYPE_NONE; 139 this.VehicleType = (int)Vehicle.TYPE_NONE;
176 140
177 _scene.TaintedObject("BSPrim.destroy", delegate() 141 PhysicsScene.TaintedObject("BSPrim.destroy", delegate()
178 { 142 {
179 DetailLog("{0},BSPrim.Destroy,taint,", LocalID); 143 DetailLog("{0},BSPrim.Destroy,taint,", LocalID);
180 // everything in the C# world will get garbage collected. Tell the C++ world to free stuff. 144 // If there are physical body and shape, release my use of same.
181 BulletSimAPI.DestroyObject(_scene.WorldID, LocalID); 145 PhysicsScene.Shapes.DereferenceBody(PhysBody, true, null);
146 PhysicsScene.Shapes.DereferenceShape(PhysShape, true, null);
182 }); 147 });
183 } 148 }
184 149
185 public override bool Stopped { 150 // No one uses this property.
186 get { return _stopped; } 151 public override bool Stopped {
152 get { return false; }
187 } 153 }
188 public override OMV.Vector3 Size { 154 public override OMV.Vector3 Size {
189 get { return _size; } 155 get { return _size; }
190 set { 156 set {
157 // We presume the scale and size are the same. If scale must be changed for
158 // the physical shape, that is done when the geometry is built.
191 _size = value; 159 _size = value;
192 _scene.TaintedObject("BSPrim.setSize", delegate() 160 ForceBodyShapeRebuild(false);
193 { 161 }
194 _mass = CalculateMass(); // changing size changes the mass
195 BulletSimAPI.SetObjectScaleMass(_scene.WorldID, _localID, _scale, (IsPhysical ? _mass : 0f), IsPhysical);
196 // DetailLog("{0}: BSPrim.setSize: size={1}, mass={2}, physical={3}", LocalID, _size, _mass, IsPhysical);
197 RecreateGeomAndObject();
198 });
199 }
200 } 162 }
201 public override PrimitiveBaseShape Shape { 163 // Scale is what we set in the physics engine. It is different than 'size' in that
164 // 'size' can be encorporated into the mesh. In that case, the scale is <1,1,1>.
165 public override OMV.Vector3 Scale { get; set; }
166
167 public override PrimitiveBaseShape Shape {
202 set { 168 set {
203 _pbs = value; 169 BaseShape = value;
204 _scene.TaintedObject("BSPrim.setShape", delegate() 170 ForceBodyShapeRebuild(false);
205 { 171 }
206 _mass = CalculateMass(); // changing the shape changes the mass
207 RecreateGeomAndObject();
208 });
209 }
210 } 172 }
211 public override uint LocalID { 173 // Whatever the linkset wants is what I want.
212 set { _localID = value; } 174 public override BSPhysicsShapeType PreferredPhysicalShape
213 get { return _localID; } 175 { get { return Linkset.PreferredPhysicalShape(this); } }
176
177 public override bool ForceBodyShapeRebuild(bool inTaintTime)
178 {
179 LastAssetBuildFailed = false;
180 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ForceBodyShapeRebuild", delegate()
181 {
182 _mass = CalculateMass(); // changing the shape changes the mass
183 CreateGeomAndObject(true);
184 });
185 return true;
214 } 186 }
215 public override bool Grabbed { 187 public override bool Grabbed {
216 set { _grabbed = value; 188 set { _grabbed = value;
217 } 189 }
218 } 190 }
219 public override bool Selected { 191 public override bool Selected {
220 set { 192 set {
221 _isSelected = value; 193 _isSelected = value;
222 _scene.TaintedObject("BSPrim.setSelected", delegate() 194 PhysicsScene.TaintedObject("BSPrim.setSelected", delegate()
223 { 195 {
224 SetObjectDynamic(); 196 DetailLog("{0},BSPrim.selected,taint,selected={1}", LocalID, _isSelected);
197 SetObjectDynamic(false);
225 }); 198 });
226 } 199 }
227 } 200 }
228 public override void CrossingFailure() { return; } 201 public override void CrossingFailure() { return; }
229 202
@@ -232,158 +205,260 @@ public sealed class BSPrim : PhysicsActor
232 BSPrim parent = obj as BSPrim; 205 BSPrim parent = obj as BSPrim;
233 if (parent != null) 206 if (parent != null)
234 { 207 {
235 DebugLog("{0}: link {1}/{2} to {3}", LogHeader, _avName, _localID, parent.LocalID); 208 BSPhysObject parentBefore = Linkset.LinksetRoot;
236 BSPrim parentBefore = _linkset.LinksetRoot; 209 int childrenBefore = Linkset.NumberOfChildren;
237 int childrenBefore = _linkset.NumberOfChildren;
238 210
239 _linkset = parent.Linkset.AddMeToLinkset(this); 211 Linkset = parent.Linkset.AddMeToLinkset(this);
240 212
241 DetailLog("{0},BSPrim.link,call,parentBefore={1}, childrenBefore=={2}, parentAfter={3}, childrenAfter={4}", 213 DetailLog("{0},BSPrim.link,call,parentBefore={1}, childrenBefore=={2}, parentAfter={3}, childrenAfter={4}",
242 LocalID, parentBefore.LocalID, childrenBefore, _linkset.LinksetRoot.LocalID, _linkset.NumberOfChildren); 214 LocalID, parentBefore.LocalID, childrenBefore, Linkset.LinksetRoot.LocalID, Linkset.NumberOfChildren);
243 } 215 }
244 return; 216 return;
245 } 217 }
246 218
247 // delink me from my linkset 219 // delink me from my linkset
248 public override void delink() { 220 public override void delink() {
249 // TODO: decide if this parent checking needs to happen at taint time 221 // TODO: decide if this parent checking needs to happen at taint time
250 // Race condition here: if link() and delink() in same simulation tick, the delink will not happen 222 // Race condition here: if link() and delink() in same simulation tick, the delink will not happen
251 DebugLog("{0}: delink {1}/{2}. Parent={3}", LogHeader, _avName, _localID,
252 _linkset.LinksetRoot._avName+"/"+_linkset.LinksetRoot.LocalID.ToString());
253 223
254 BSPrim parentBefore = _linkset.LinksetRoot; 224 BSPhysObject parentBefore = Linkset.LinksetRoot;
255 int childrenBefore = _linkset.NumberOfChildren; 225 int childrenBefore = Linkset.NumberOfChildren;
256 226
257 _linkset = _linkset.RemoveMeFromLinkset(this); 227 Linkset = Linkset.RemoveMeFromLinkset(this);
258 228
259 DetailLog("{0},BSPrim.delink,parentBefore={1},childrenBefore={2},parentAfter={3},childrenAfter={4}, ", 229 DetailLog("{0},BSPrim.delink,parentBefore={1},childrenBefore={2},parentAfter={3},childrenAfter={4}, ",
260 LocalID, parentBefore.LocalID, childrenBefore, _linkset.LinksetRoot.LocalID, _linkset.NumberOfChildren); 230 LocalID, parentBefore.LocalID, childrenBefore, Linkset.LinksetRoot.LocalID, Linkset.NumberOfChildren);
261 return; 231 return;
262 } 232 }
263 233
264 // Set motion values to zero. 234 // Set motion values to zero.
265 // Do it to the properties so the values get set in the physics engine. 235 // Do it to the properties so the values get set in the physics engine.
266 // Push the setting of the values to the viewer. 236 // Push the setting of the values to the viewer.
267 // Called at taint time! 237 // Called at taint time!
268 public void ZeroMotion() 238 public override void ZeroMotion(bool inTaintTime)
269 { 239 {
270 _velocity = OMV.Vector3.Zero; 240 _velocity = OMV.Vector3.Zero;
271 _acceleration = OMV.Vector3.Zero; 241 _acceleration = OMV.Vector3.Zero;
272 _rotationalVelocity = OMV.Vector3.Zero; 242 _rotationalVelocity = OMV.Vector3.Zero;
273 243
274 // Zero some other properties directly into the physics engine 244 // Zero some other properties in the physics engine
275 BulletSimAPI.SetVelocity2(Body.Ptr, OMV.Vector3.Zero); 245 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate()
276 BulletSimAPI.SetAngularVelocity2(Body.Ptr, OMV.Vector3.Zero); 246 {
277 BulletSimAPI.SetInterpolation2(Body.Ptr, OMV.Vector3.Zero, OMV.Vector3.Zero); 247 BulletSimAPI.ClearAllForces2(PhysBody.ptr);
278 BulletSimAPI.ClearForces2(Body.Ptr); 248 });
249 }
250 public override void ZeroAngularMotion(bool inTaintTime)
251 {
252 _rotationalVelocity = OMV.Vector3.Zero;
253 // Zero some other properties in the physics engine
254 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate()
255 {
256 BulletSimAPI.SetInterpolationAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero);
257 BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero);
258 });
279 } 259 }
280 260
281 public override void LockAngularMotion(OMV.Vector3 axis) 261 public override void LockAngularMotion(OMV.Vector3 axis)
282 { 262 {
283 // DetailLog("{0},BSPrim.LockAngularMotion,call,axis={1}", LocalID, axis); 263 DetailLog("{0},BSPrim.LockAngularMotion,call,axis={1}", LocalID, axis);
284 return; 264 return;
285 } 265 }
286 266
287 public override OMV.Vector3 Position { 267 public override OMV.Vector3 RawPosition
288 get { 268 {
289 if (!_linkset.IsRoot(this)) 269 get { return _position; }
290 // child prims move around based on their parent. Need to get the latest location 270 set { _position = value; }
291 _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID); 271 }
272 public override OMV.Vector3 Position {
273 get {
274 // child prims move around based on their parent. Need to get the latest location
275 if (!Linkset.IsRoot(this))
276 _position = Linkset.Position(this);
292 277
293 // don't do the GetObjectPosition for root elements because this function is called a zillion times 278 // don't do the GetObjectPosition for root elements because this function is called a zillion times.
294 // _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID); 279 // _position = BulletSimAPI.GetObjectPosition2(PhysicsScene.World.ptr, BSBody.ptr);
295 return _position; 280 return _position;
296 } 281 }
297 set { 282 set {
283 // If the position must be forced into the physics engine, use ForcePosition.
284 if (_position == value)
285 {
286 return;
287 }
298 _position = value; 288 _position = value;
299 // TODO: what does it mean to set the position of a child prim?? Rebuild the constraint? 289 // TODO: what does it mean to set the position of a child prim?? Rebuild the constraint?
300 _scene.TaintedObject("BSPrim.setPosition", delegate() 290 PositionSanityCheck(false);
291 PhysicsScene.TaintedObject("BSPrim.setPosition", delegate()
301 { 292 {
302 // DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation); 293 // DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation);
303 BulletSimAPI.SetObjectTranslation(_scene.WorldID, _localID, _position, _orientation); 294 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
295 ActivateIfPhysical(false);
304 }); 296 });
305 } 297 }
298 }
299 public override OMV.Vector3 ForcePosition {
300 get {
301 _position = BulletSimAPI.GetPosition2(PhysBody.ptr);
302 return _position;
303 }
304 set {
305 _position = value;
306 // PositionSanityCheck(); // Don't do this! Causes a loop and caller should know better.
307 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
308 ActivateIfPhysical(false);
309 }
310 }
311
312 // Check that the current position is sane and, if not, modify the position to make it so.
313 // Check for being below terrain and being out of bounds.
314 // Returns 'true' of the position was made sane by some action.
315 private bool PositionSanityCheck(bool inTaintTime)
316 {
317 bool ret = false;
318
319 float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position);
320 OMV.Vector3 upForce = OMV.Vector3.Zero;
321 if (Position.Z < terrainHeight)
322 {
323 DetailLog("{0},BSPrim.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight);
324 float targetHeight = terrainHeight + (Size.Z / 2f);
325 // Upforce proportional to the distance away from the terrain. Correct the error in 1 sec.
326 upForce.Z = (terrainHeight - Position.Z) * 1f;
327 ret = true;
328 }
329
330 if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0)
331 {
332 float waterHeight = PhysicsScene.GetWaterLevelAtXYZ(_position);
333 // TODO: a floating motor so object will bob in the water
334 if (Math.Abs(Position.Z - waterHeight) > 0.1f)
335 {
336 // Upforce proportional to the distance away from the water. Correct the error in 1 sec.
337 upForce.Z = (waterHeight - Position.Z) * 1f;
338 ret = true;
339 }
340 }
341
342 // TODO: check for out of bounds
343
344 // The above code computes a force to apply to correct any out-of-bounds problems. Apply same.
345 if (ret)
346 {
347 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.PositionSanityCheck:belowTerrain", delegate()
348 {
349 // Apply upforce and overcome gravity.
350 ForceVelocity = ForceVelocity + upForce - PhysicsScene.DefaultGravity;
351 });
352 }
353 return ret;
306 } 354 }
307 355
308 // Return the effective mass of the object. 356 // Return the effective mass of the object.
309 // If there are multiple items in the linkset, add them together for the root 357 // If there are multiple items in the linkset, add them together for the root
310 public override float Mass 358 public override float Mass
311 { 359 {
312 get 360 get
313 { 361 {
314 return _linkset.LinksetMass; 362 return Linkset.LinksetMass;
363 // return _mass;
315 } 364 }
316 } 365 }
317 366
318 // used when we only want this prim's mass and not the linkset thing 367 // used when we only want this prim's mass and not the linkset thing
319 public float MassRaw { get { return _mass; } } 368 public override float RawMass {
369 get { return _mass; }
370 }
371 // Set the physical mass to the passed mass.
372 // Note that this does not change _mass!
373 public override void UpdatePhysicalMassProperties(float physMass)
374 {
375 if (IsStatic)
376 {
377 Inertia = OMV.Vector3.Zero;
378 BulletSimAPI.SetMassProps2(PhysBody.ptr, 0f, Inertia);
379 BulletSimAPI.UpdateInertiaTensor2(PhysBody.ptr);
380 }
381 else
382 {
383 Inertia = BulletSimAPI.CalculateLocalInertia2(PhysShape.ptr, physMass);
384 BulletSimAPI.SetMassProps2(PhysBody.ptr, physMass, Inertia);
385 BulletSimAPI.UpdateInertiaTensor2(PhysBody.ptr);
386 // center of mass is at the zero of the object
387 // DEBUG DEBUG BulletSimAPI.SetCenterOfMassByPosRot2(PhysBody.ptr, ForcePosition, ForceOrientation);
388 DetailLog("{0},BSPrim.UpdateMassProperties,mass={1},localInertia={2}", LocalID, physMass, Inertia);
389 }
390 }
320 391
321 // Is this used? 392 // Is this used?
322 public override OMV.Vector3 CenterOfMass 393 public override OMV.Vector3 CenterOfMass
323 { 394 {
324 get { return _linkset.CenterOfMass; } 395 get { return Linkset.CenterOfMass; }
325 } 396 }
326 397
327 // Is this used? 398 // Is this used?
328 public override OMV.Vector3 GeometricCenter 399 public override OMV.Vector3 GeometricCenter
329 { 400 {
330 get { return _linkset.GeometricCenter; } 401 get { return Linkset.GeometricCenter; }
331 } 402 }
332 403
333 public override OMV.Vector3 Force { 404 public override OMV.Vector3 Force {
334 get { return _force; } 405 get { return _force; }
335 set { 406 set {
336 _force = value; 407 _force = value;
337 _scene.TaintedObject("BSPrim.setForce", delegate() 408 PhysicsScene.TaintedObject("BSPrim.setForce", delegate()
338 { 409 {
339 // DetailLog("{0},BSPrim.setForce,taint,force={1}", LocalID, _force); 410 // DetailLog("{0},BSPrim.setForce,taint,force={1}", LocalID, _force);
340 // BulletSimAPI.SetObjectForce(_scene.WorldID, _localID, _force); 411 BulletSimAPI.SetObjectForce2(PhysBody.ptr, _force);
341 BulletSimAPI.SetObjectForce2(Body.Ptr, _force);
342 }); 412 });
343 } 413 }
344 } 414 }
345 415
346 public override int VehicleType { 416 public override int VehicleType {
347 get { 417 get {
348 return (int)_vehicle.Type; // if we are a vehicle, return that type 418 return (int)_vehicle.Type; // if we are a vehicle, return that type
349 } 419 }
350 set { 420 set {
351 Vehicle type = (Vehicle)value; 421 Vehicle type = (Vehicle)value;
352 BSPrim vehiclePrim = this; 422
353 _scene.TaintedObject("setVehicleType", delegate() 423 // Tell the scene about the vehicle so it will get processing each frame.
424 PhysicsScene.VehicleInSceneTypeChanged(this, type);
425
426 PhysicsScene.TaintedObject("setVehicleType", delegate()
354 { 427 {
355 // Done at taint time so we're sure the physics engine is not using the variables 428 // Done at taint time so we're sure the physics engine is not using the variables
356 // Vehicle code changes the parameters for this vehicle type. 429 // Vehicle code changes the parameters for this vehicle type.
357 _vehicle.ProcessTypeChange(type); 430 _vehicle.ProcessTypeChange(type);
358 // Tell the scene about the vehicle so it will get processing each frame. 431 ActivateIfPhysical(false);
359 _scene.VehicleInSceneTypeChanged(this, type);
360 }); 432 });
361 } 433 }
362 } 434 }
363 public override void VehicleFloatParam(int param, float value) 435 public override void VehicleFloatParam(int param, float value)
364 { 436 {
365 _scene.TaintedObject("BSPrim.VehicleFloatParam", delegate() 437 PhysicsScene.TaintedObject("BSPrim.VehicleFloatParam", delegate()
366 { 438 {
367 _vehicle.ProcessFloatVehicleParam((Vehicle)param, value, _scene.LastSimulatedTimestep); 439 _vehicle.ProcessFloatVehicleParam((Vehicle)param, value);
440 ActivateIfPhysical(false);
368 }); 441 });
369 } 442 }
370 public override void VehicleVectorParam(int param, OMV.Vector3 value) 443 public override void VehicleVectorParam(int param, OMV.Vector3 value)
371 { 444 {
372 _scene.TaintedObject("BSPrim.VehicleVectorParam", delegate() 445 PhysicsScene.TaintedObject("BSPrim.VehicleVectorParam", delegate()
373 { 446 {
374 _vehicle.ProcessVectorVehicleParam((Vehicle)param, value, _scene.LastSimulatedTimestep); 447 _vehicle.ProcessVectorVehicleParam((Vehicle)param, value);
448 ActivateIfPhysical(false);
375 }); 449 });
376 } 450 }
377 public override void VehicleRotationParam(int param, OMV.Quaternion rotation) 451 public override void VehicleRotationParam(int param, OMV.Quaternion rotation)
378 { 452 {
379 _scene.TaintedObject("BSPrim.VehicleRotationParam", delegate() 453 PhysicsScene.TaintedObject("BSPrim.VehicleRotationParam", delegate()
380 { 454 {
381 _vehicle.ProcessRotationVehicleParam((Vehicle)param, rotation); 455 _vehicle.ProcessRotationVehicleParam((Vehicle)param, rotation);
456 ActivateIfPhysical(false);
382 }); 457 });
383 } 458 }
384 public override void VehicleFlags(int param, bool remove) 459 public override void VehicleFlags(int param, bool remove)
385 { 460 {
386 _scene.TaintedObject("BSPrim.VehicleFlags", delegate() 461 PhysicsScene.TaintedObject("BSPrim.VehicleFlags", delegate()
387 { 462 {
388 _vehicle.ProcessVehicleFlags(param, remove); 463 _vehicle.ProcessVehicleFlags(param, remove);
389 }); 464 });
@@ -391,143 +466,356 @@ public sealed class BSPrim : PhysicsActor
391 466
392 // Called each simulation step to advance vehicle characteristics. 467 // Called each simulation step to advance vehicle characteristics.
393 // Called from Scene when doing simulation step so we're in taint processing time. 468 // Called from Scene when doing simulation step so we're in taint processing time.
394 public void StepVehicle(float timeStep) 469 public override void StepVehicle(float timeStep)
395 { 470 {
396 if (IsPhysical) 471 if (IsPhysical && _vehicle.IsActive)
472 {
397 _vehicle.Step(timeStep); 473 _vehicle.Step(timeStep);
474 /* // TEST TEST DEBUG DEBUG -- trying to reduce the extra action of Bullet simulation step
475 PhysicsScene.PostTaintObject("BSPrim.StepVehicles", LocalID, delegate()
476 {
477 // This resets the interpolation values and recomputes the tensor variables
478 BulletSimAPI.SetCenterOfMassByPosRot2(BSBody.ptr, ForcePosition, ForceOrientation);
479 });
480 */
481 }
398 } 482 }
399 483
400 // Allows the detection of collisions with inherently non-physical prims. see llVolumeDetect for more 484 // Allows the detection of collisions with inherently non-physical prims. see llVolumeDetect for more
401 public override void SetVolumeDetect(int param) { 485 public override void SetVolumeDetect(int param) {
402 bool newValue = (param != 0); 486 bool newValue = (param != 0);
403 _isVolumeDetect = newValue; 487 if (_isVolumeDetect != newValue)
404 _scene.TaintedObject("BSPrim.SetVolumeDetect", delegate()
405 { 488 {
406 SetObjectDynamic(); 489 _isVolumeDetect = newValue;
407 }); 490 PhysicsScene.TaintedObject("BSPrim.SetVolumeDetect", delegate()
408 return; 491 {
492 // DetailLog("{0},setVolumeDetect,taint,volDetect={1}", LocalID, _isVolumeDetect);
493 SetObjectDynamic(true);
494 });
495 }
496 return;
409 } 497 }
410 498 public override OMV.Vector3 Velocity {
411 public override OMV.Vector3 Velocity { 499 get { return _velocity; }
412 get { return _velocity; }
413 set { 500 set {
414 _velocity = value; 501 _velocity = value;
415 _scene.TaintedObject("BSPrim.setVelocity", delegate() 502 PhysicsScene.TaintedObject("BSPrim.setVelocity", delegate()
416 { 503 {
417 // DetailLog("{0},BSPrim.SetVelocity,taint,vel={1}", LocalID, _velocity); 504 // DetailLog("{0},BSPrim.SetVelocity,taint,vel={1}", LocalID, _velocity);
418 BulletSimAPI.SetObjectVelocity(_scene.WorldID, LocalID, _velocity); 505 BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity);
419 }); 506 });
420 } 507 }
508 }
509 public override OMV.Vector3 ForceVelocity {
510 get { return _velocity; }
511 set {
512 _velocity = value;
513 BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity);
514 }
421 } 515 }
422 public override OMV.Vector3 Torque { 516 public override OMV.Vector3 Torque {
423 get { return _torque; } 517 get { return _torque; }
424 set { _torque = value; 518 set {
519 _torque = value;
520 AddAngularForce(_torque, false, false);
425 // DetailLog("{0},BSPrim.SetTorque,call,torque={1}", LocalID, _torque); 521 // DetailLog("{0},BSPrim.SetTorque,call,torque={1}", LocalID, _torque);
426 } 522 }
427 } 523 }
428 public override float CollisionScore { 524 public override float CollisionScore {
429 get { return _collisionScore; } 525 get { return _collisionScore; }
430 set { _collisionScore = value; 526 set { _collisionScore = value;
431 } 527 }
432 } 528 }
433 public override OMV.Vector3 Acceleration { 529 public override OMV.Vector3 Acceleration {
434 get { return _acceleration; } 530 get { return _acceleration; }
435 set { _acceleration = value; } 531 set { _acceleration = value; }
436 } 532 }
437 public override OMV.Quaternion Orientation { 533 public override OMV.Quaternion RawOrientation
534 {
535 get { return _orientation; }
536 set { _orientation = value; }
537 }
538 public override OMV.Quaternion Orientation {
438 get { 539 get {
439 if (!_linkset.IsRoot(this)) 540 // Children move around because tied to parent. Get a fresh value.
541 if (!Linkset.IsRoot(this))
440 { 542 {
441 // Children move around because tied to parent. Get a fresh value. 543 _orientation = Linkset.Orientation(this);
442 _orientation = BulletSimAPI.GetObjectOrientation(_scene.WorldID, LocalID);
443 } 544 }
444 return _orientation; 545 return _orientation;
445 } 546 }
446 set { 547 set {
548 if (_orientation == value)
549 return;
447 _orientation = value; 550 _orientation = value;
448 // TODO: what does it mean if a child in a linkset changes its orientation? Rebuild the constraint? 551 // TODO: what does it mean if a child in a linkset changes its orientation? Rebuild the constraint?
449 _scene.TaintedObject("BSPrim.setOrientation", delegate() 552 PhysicsScene.TaintedObject("BSPrim.setOrientation", delegate()
450 { 553 {
451 // _position = BulletSimAPI.GetObjectPosition(_scene.WorldID, _localID); 554 // _position = BulletSimAPI.GetObjectPosition2(PhysicsScene.World.ptr, BSBody.ptr);
452 // DetailLog("{0},BSPrim.setOrientation,taint,pos={1},orient={2}", LocalID, _position, _orientation); 555 // DetailLog("{0},BSPrim.setOrientation,taint,pos={1},orient={2}", LocalID, _position, _orientation);
453 BulletSimAPI.SetObjectTranslation(_scene.WorldID, _localID, _position, _orientation); 556 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
454 }); 557 });
455 } 558 }
559 }
560 // Go directly to Bullet to get/set the value.
561 public override OMV.Quaternion ForceOrientation
562 {
563 get
564 {
565 _orientation = BulletSimAPI.GetOrientation2(PhysBody.ptr);
566 return _orientation;
567 }
568 set
569 {
570 _orientation = value;
571 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
572 }
456 } 573 }
457 public override int PhysicsActorType { 574 public override int PhysicsActorType {
458 get { return _physicsActorType; } 575 get { return _physicsActorType; }
459 set { _physicsActorType = value; 576 set { _physicsActorType = value; }
460 }
461 } 577 }
462 public override bool IsPhysical { 578 public override bool IsPhysical {
463 get { return _isPhysical; } 579 get { return _isPhysical; }
464 set { 580 set {
465 _isPhysical = value; 581 if (_isPhysical != value)
466 _scene.TaintedObject("BSPrim.setIsPhysical", delegate()
467 { 582 {
468 SetObjectDynamic(); 583 _isPhysical = value;
469 }); 584 PhysicsScene.TaintedObject("BSPrim.setIsPhysical", delegate()
470 } 585 {
586 // DetailLog("{0},setIsPhysical,taint,isPhys={1}", LocalID, _isPhysical);
587 SetObjectDynamic(true);
588 // whether phys-to-static or static-to-phys, the object is not moving.
589 ZeroMotion(true);
590 });
591 }
592 }
471 } 593 }
472 594
473 // An object is static (does not move) if selected or not physical 595 // An object is static (does not move) if selected or not physical
474 private bool IsStatic 596 public override bool IsStatic
475 { 597 {
476 get { return _isSelected || !IsPhysical; } 598 get { return _isSelected || !IsPhysical; }
477 } 599 }
478 600
479 // An object is solid if it's not phantom and if it's not doing VolumeDetect 601 // An object is solid if it's not phantom and if it's not doing VolumeDetect
480 private bool IsSolid 602 public override bool IsSolid
481 { 603 {
482 get { return !IsPhantom && !_isVolumeDetect; } 604 get { return !IsPhantom && !_isVolumeDetect; }
483 } 605 }
484 606
485 // Make gravity work if the object is physical and not selected 607 // Make gravity work if the object is physical and not selected
486 // No locking here because only called when it is safe 608 // Called at taint-time!!
487 private void SetObjectDynamic() 609 private void SetObjectDynamic(bool forceRebuild)
610 {
611 // Recreate the physical object if necessary
612 CreateGeomAndObject(forceRebuild);
613 }
614
615 // Convert the simulator's physical properties into settings on BulletSim objects.
616 // There are four flags we're interested in:
617 // IsStatic: Object does not move, otherwise the object has mass and moves
618 // isSolid: other objects bounce off of this object
619 // isVolumeDetect: other objects pass through but can generate collisions
620 // collisionEvents: whether this object returns collision events
621 private void UpdatePhysicalParameters()
488 { 622 {
489 // RA: remove this for the moment. 623 // DetailLog("{0},BSPrim.UpdatePhysicalParameters,entry,body={1},shape={2}", LocalID, BSBody, BSShape);
490 // The problem is that dynamic objects are hulls so if we are becoming physical 624
491 // the shape has to be checked and possibly built. 625 // Mangling all the physical properties requires the object not be in the physical world.
492 // Maybe a VerifyCorrectPhysicalShape() routine? 626 // This is a NOOP if the object is not in the world (BulletSim and Bullet ignore objects not found).
493 // RecreateGeomAndObject(); 627 BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, PhysBody.ptr);
628
629 // Set up the object physicalness (does gravity and collisions move this object)
630 MakeDynamic(IsStatic);
631
632 // Update vehicle specific parameters (after MakeDynamic() so can change physical parameters)
633 _vehicle.Refresh();
494 634
495 // Bullet wants static objects to have a mass of zero 635 // Arrange for collision events if the simulator wants them
496 float mass = IsStatic ? 0f : _mass; 636 EnableCollisions(SubscribedEvents());
497 637
498 BulletSimAPI.SetObjectProperties(_scene.WorldID, LocalID, IsStatic, IsSolid, SubscribedEvents(), mass); 638 // Make solid or not (do things bounce off or pass through this object).
639 MakeSolid(IsSolid);
499 640
500 // recompute any linkset parameters 641 BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, PhysBody.ptr);
501 _linkset.Refresh(this);
502 642
503 CollisionFlags cf = BulletSimAPI.GetCollisionFlags2(Body.Ptr); 643 // Rebuild its shape
504 // DetailLog("{0},BSPrim.SetObjectDynamic,taint,static={1},solid={2},mass={3}, cf={4}", LocalID, IsStatic, IsSolid, mass, cf); 644 BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, PhysBody.ptr);
645
646 // Collision filter can be set only when the object is in the world
647 if (PhysBody.collisionFilter != 0 || PhysBody.collisionMask != 0)
648 {
649 BulletSimAPI.SetCollisionFilterMask2(PhysBody.ptr, (uint)PhysBody.collisionFilter, (uint)PhysBody.collisionMask);
650 }
651
652 // Recompute any linkset parameters.
653 // When going from non-physical to physical, this re-enables the constraints that
654 // had been automatically disabled when the mass was set to zero.
655 // For compound based linksets, this enables and disables interactions of the children.
656 Linkset.Refresh(this);
657
658 DetailLog("{0},BSPrim.UpdatePhysicalParameters,taintExit,static={1},solid={2},mass={3},collide={4},cf={5:X},body={6},shape={7}",
659 LocalID, IsStatic, IsSolid, _mass, SubscribedEvents(), CurrentCollisionFlags, PhysBody, PhysShape);
660 }
661
662 // "Making dynamic" means changing to and from static.
663 // When static, gravity does not effect the object and it is fixed in space.
664 // When dynamic, the object can fall and be pushed by others.
665 // This is independent of its 'solidness' which controls what passes through
666 // this object and what interacts with it.
667 private void MakeDynamic(bool makeStatic)
668 {
669 if (makeStatic)
670 {
671 // Become a Bullet 'static' object type
672 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
673 // Stop all movement
674 ZeroMotion(true);
675 // Center of mass is at the center of the object
676 // DEBUG DEBUG BulletSimAPI.SetCenterOfMassByPosRot2(Linkset.LinksetRoot.PhysBody.ptr, _position, _orientation);
677 // Mass is zero which disables a bunch of physics stuff in Bullet
678 UpdatePhysicalMassProperties(0f);
679 // Set collision detection parameters
680 if (PhysicsScene.Params.ccdMotionThreshold > 0f)
681 {
682 BulletSimAPI.SetCcdMotionThreshold2(PhysBody.ptr, PhysicsScene.Params.ccdMotionThreshold);
683 BulletSimAPI.SetCcdSweptSphereRadius2(PhysBody.ptr, PhysicsScene.Params.ccdSweptSphereRadius);
684 }
685 // There can be special things needed for implementing linksets
686 Linkset.MakeStatic(this);
687 // The activation state is 'disabled' so Bullet will not try to act on it.
688 BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.DISABLE_SIMULATION);
689 // Start it out sleeping and physical actions could wake it up.
690 // BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.ISLAND_SLEEPING);
691
692 PhysBody.collisionFilter = CollisionFilterGroups.StaticObjectFilter;
693 PhysBody.collisionMask = CollisionFilterGroups.StaticObjectMask;
694 }
695 else
696 {
697 // Not a Bullet static object
698 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_STATIC_OBJECT);
699
700 // Set various physical properties so internal dynamic properties will get computed correctly as they are set
701 BulletSimAPI.SetFriction2(PhysBody.ptr, PhysicsScene.Params.defaultFriction);
702 BulletSimAPI.SetRestitution2(PhysBody.ptr, PhysicsScene.Params.defaultRestitution);
703
704 // per http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=3382
705 // Since this can be called multiple times, only zero forces when becoming physical
706 // BulletSimAPI.ClearAllForces2(BSBody.ptr);
707
708 // For good measure, make sure the transform is set through to the motion state
709 BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation);
710
711 // Center of mass is at the center of the object
712 // DEBUG DEBUG BulletSimAPI.SetCenterOfMassByPosRot2(Linkset.LinksetRoot.PhysBody.ptr, _position, _orientation);
713
714 // A dynamic object has mass
715 UpdatePhysicalMassProperties(RawMass);
716
717 // Set collision detection parameters
718 if (PhysicsScene.Params.ccdMotionThreshold > 0f)
719 {
720 BulletSimAPI.SetCcdMotionThreshold2(PhysBody.ptr, PhysicsScene.Params.ccdMotionThreshold);
721 BulletSimAPI.SetCcdSweptSphereRadius2(PhysBody.ptr, PhysicsScene.Params.ccdSweptSphereRadius);
722 }
723
724 // Various values for simulation limits
725 BulletSimAPI.SetDamping2(PhysBody.ptr, PhysicsScene.Params.linearDamping, PhysicsScene.Params.angularDamping);
726 BulletSimAPI.SetDeactivationTime2(PhysBody.ptr, PhysicsScene.Params.deactivationTime);
727 BulletSimAPI.SetSleepingThresholds2(PhysBody.ptr, PhysicsScene.Params.linearSleepingThreshold, PhysicsScene.Params.angularSleepingThreshold);
728 BulletSimAPI.SetContactProcessingThreshold2(PhysBody.ptr, PhysicsScene.Params.contactProcessingThreshold);
729
730 // There might be special things needed for implementing linksets.
731 Linkset.MakeDynamic(this);
732
733 // Force activation of the object so Bullet will act on it.
734 // Must do the ForceActivationState2() to overcome the DISABLE_SIMULATION from static objects.
735 BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.ACTIVE_TAG);
736 // BulletSimAPI.Activate2(BSBody.ptr, true);
737
738 PhysBody.collisionFilter = CollisionFilterGroups.ObjectFilter;
739 PhysBody.collisionMask = CollisionFilterGroups.ObjectMask;
740 }
741 }
742
743 // "Making solid" means that other object will not pass through this object.
744 // To make transparent, we create a Bullet ghost object.
745 // Note: This expects to be called from the UpdatePhysicalParameters() routine as
746 // the functions after this one set up the state of a possibly newly created collision body.
747 private void MakeSolid(bool makeSolid)
748 {
749 CollisionObjectTypes bodyType = (CollisionObjectTypes)BulletSimAPI.GetBodyType2(PhysBody.ptr);
750 if (makeSolid)
751 {
752 // Verify the previous code created the correct shape for this type of thing.
753 if ((bodyType & CollisionObjectTypes.CO_RIGID_BODY) == 0)
754 {
755 m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for solidity. id={1}, type={2}", LogHeader, LocalID, bodyType);
756 }
757 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE);
758 }
759 else
760 {
761 if ((bodyType & CollisionObjectTypes.CO_GHOST_OBJECT) == 0)
762 {
763 m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for non-solidness. id={1}, type={2}", LogHeader, LocalID, bodyType);
764 }
765 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_NO_CONTACT_RESPONSE);
766 PhysBody.collisionFilter = CollisionFilterGroups.VolumeDetectFilter;
767 PhysBody.collisionMask = CollisionFilterGroups.VolumeDetectMask;
768 }
769 }
770
771 // Enable physical actions. Bullet will keep sleeping non-moving physical objects so
772 // they need waking up when parameters are changed.
773 // Called in taint-time!!
774 private void ActivateIfPhysical(bool forceIt)
775 {
776 if (IsPhysical)
777 BulletSimAPI.Activate2(PhysBody.ptr, forceIt);
778 }
779
780 // Turn on or off the flag controlling whether collision events are returned to the simulator.
781 private void EnableCollisions(bool wantsCollisionEvents)
782 {
783 if (wantsCollisionEvents)
784 {
785 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
786 }
787 else
788 {
789 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
790 }
505 } 791 }
506 792
507 // prims don't fly 793 // prims don't fly
508 public override bool Flying { 794 public override bool Flying {
509 get { return _flying; } 795 get { return _flying; }
510 set { _flying = value; } 796 set {
797 _flying = value;
798 }
511 } 799 }
512 public override bool SetAlwaysRun { 800 public override bool SetAlwaysRun {
513 get { return _setAlwaysRun; } 801 get { return _setAlwaysRun; }
514 set { _setAlwaysRun = value; } 802 set { _setAlwaysRun = value; }
515 } 803 }
516 public override bool ThrottleUpdates { 804 public override bool ThrottleUpdates {
517 get { return _throttleUpdates; } 805 get { return _throttleUpdates; }
518 set { _throttleUpdates = value; } 806 set { _throttleUpdates = value; }
519 } 807 }
520 public override bool IsColliding { 808 public override bool IsColliding {
521 get { return (_collidingStep == _scene.SimulationStep); } 809 get { return (CollidingStep == PhysicsScene.SimulationStep); }
522 set { _isColliding = value; } 810 set { _isColliding = value; }
523 } 811 }
524 public override bool CollidingGround { 812 public override bool CollidingGround {
525 get { return (_collidingGroundStep == _scene.SimulationStep); } 813 get { return (CollidingGroundStep == PhysicsScene.SimulationStep); }
526 set { _collidingGround = value; } 814 set { _collidingGround = value; }
527 } 815 }
528 public override bool CollidingObj { 816 public override bool CollidingObj {
529 get { return _collidingObj; } 817 get { return _collidingObj; }
530 set { _collidingObj = value; } 818 set { _collidingObj = value; }
531 } 819 }
532 public bool IsPhantom { 820 public bool IsPhantom {
533 get { 821 get {
@@ -537,10 +825,19 @@ public sealed class BSPrim : PhysicsActor
537 return false; 825 return false;
538 } 826 }
539 } 827 }
540 public override bool FloatOnWater { 828 public override bool FloatOnWater {
541 set { _floatOnWater = value; } 829 set {
830 _floatOnWater = value;
831 PhysicsScene.TaintedObject("BSPrim.setFloatOnWater", delegate()
832 {
833 if (_floatOnWater)
834 CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER);
835 else
836 CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER);
837 });
838 }
542 } 839 }
543 public override OMV.Vector3 RotationalVelocity { 840 public override OMV.Vector3 RotationalVelocity {
544 get { 841 get {
545 /* 842 /*
546 OMV.Vector3 pv = OMV.Vector3.Zero; 843 OMV.Vector3 pv = OMV.Vector3.Zero;
@@ -552,58 +849,76 @@ public sealed class BSPrim : PhysicsActor
552 */ 849 */
553 850
554 return _rotationalVelocity; 851 return _rotationalVelocity;
555 } 852 }
556 set { 853 set {
557 _rotationalVelocity = value; 854 _rotationalVelocity = value;
558 // m_log.DebugFormat("{0}: RotationalVelocity={1}", LogHeader, _rotationalVelocity); 855 // m_log.DebugFormat("{0}: RotationalVelocity={1}", LogHeader, _rotationalVelocity);
559 _scene.TaintedObject("BSPrim.setRotationalVelocity", delegate() 856 PhysicsScene.TaintedObject("BSPrim.setRotationalVelocity", delegate()
560 { 857 {
561 // DetailLog("{0},BSPrim.SetRotationalVel,taint,rotvel={1}", LocalID, _rotationalVelocity); 858 DetailLog("{0},BSPrim.SetRotationalVel,taint,rotvel={1}", LocalID, _rotationalVelocity);
562 BulletSimAPI.SetObjectAngularVelocity(_scene.WorldID, LocalID, _rotationalVelocity); 859 BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, _rotationalVelocity);
563 }); 860 });
564 } 861 }
565 } 862 }
566 public override bool Kinematic { 863 public override OMV.Vector3 ForceRotationalVelocity {
567 get { return _kinematic; } 864 get {
568 set { _kinematic = value; 865 return _rotationalVelocity;
866 }
867 set {
868 _rotationalVelocity = value;
869 BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, _rotationalVelocity);
870 }
871 }
872 public override bool Kinematic {
873 get { return _kinematic; }
874 set { _kinematic = value;
569 // m_log.DebugFormat("{0}: Kinematic={1}", LogHeader, _kinematic); 875 // m_log.DebugFormat("{0}: Kinematic={1}", LogHeader, _kinematic);
570 } 876 }
571 } 877 }
572 public override float Buoyancy { 878 public override float Buoyancy {
573 get { return _buoyancy; } 879 get { return _buoyancy; }
574 set { 880 set {
575 _buoyancy = value; 881 _buoyancy = value;
576 _scene.TaintedObject("BSPrim.setBuoyancy", delegate() 882 PhysicsScene.TaintedObject("BSPrim.setBuoyancy", delegate()
577 { 883 {
578 // DetailLog("{0},BSPrim.SetBuoyancy,taint,buoy={1}", LocalID, _buoyancy); 884 ForceBuoyancy = _buoyancy;
579 BulletSimAPI.SetObjectBuoyancy(_scene.WorldID, _localID, _buoyancy);
580 }); 885 });
581 } 886 }
887 }
888 public override float ForceBuoyancy {
889 get { return _buoyancy; }
890 set {
891 _buoyancy = value;
892 // DetailLog("{0},BSPrim.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
893 // Buoyancy is faked by changing the gravity applied to the object
894 float grav = PhysicsScene.Params.gravity * (1f - _buoyancy);
895 BulletSimAPI.SetGravity2(PhysBody.ptr, new OMV.Vector3(0f, 0f, grav));
896 }
582 } 897 }
583 898
584 // Used for MoveTo 899 // Used for MoveTo
585 public override OMV.Vector3 PIDTarget { 900 public override OMV.Vector3 PIDTarget {
586 set { _PIDTarget = value; } 901 set { _PIDTarget = value; }
587 } 902 }
588 public override bool PIDActive { 903 public override bool PIDActive {
589 set { _usePID = value; } 904 set { _usePID = value; }
590 } 905 }
591 public override float PIDTau { 906 public override float PIDTau {
592 set { _PIDTau = value; } 907 set { _PIDTau = value; }
593 } 908 }
594 909
595 // Used for llSetHoverHeight and maybe vehicle height 910 // Used for llSetHoverHeight and maybe vehicle height
596 // Hover Height will override MoveTo target's Z 911 // Hover Height will override MoveTo target's Z
597 public override bool PIDHoverActive { 912 public override bool PIDHoverActive {
598 set { _useHoverPID = value; } 913 set { _useHoverPID = value; }
599 } 914 }
600 public override float PIDHoverHeight { 915 public override float PIDHoverHeight {
601 set { _PIDHoverHeight = value; } 916 set { _PIDHoverHeight = value; }
602 } 917 }
603 public override PIDHoverType PIDHoverType { 918 public override PIDHoverType PIDHoverType {
604 set { _PIDHoverType = value; } 919 set { _PIDHoverType = value; }
605 } 920 }
606 public override float PIDHoverTau { 921 public override float PIDHoverTau {
607 set { _PIDHoverTao = value; } 922 set { _PIDHoverTao = value; }
608 } 923 }
609 924
@@ -615,6 +930,10 @@ public sealed class BSPrim : PhysicsActor
615 930
616 private List<OMV.Vector3> m_accumulatedForces = new List<OMV.Vector3>(); 931 private List<OMV.Vector3> m_accumulatedForces = new List<OMV.Vector3>();
617 public override void AddForce(OMV.Vector3 force, bool pushforce) { 932 public override void AddForce(OMV.Vector3 force, bool pushforce) {
933 AddForce(force, pushforce, false);
934 }
935 // Applying a force just adds this to the total force on the object.
936 public void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) {
618 // for an object, doesn't matter if force is a pushforce or not 937 // for an object, doesn't matter if force is a pushforce or not
619 if (force.IsFinite()) 938 if (force.IsFinite())
620 { 939 {
@@ -624,56 +943,88 @@ public sealed class BSPrim : PhysicsActor
624 } 943 }
625 else 944 else
626 { 945 {
627 m_log.WarnFormat("{0}: Got a NaN force applied to a Character", LogHeader); 946 m_log.WarnFormat("{0}: Got a NaN force applied to a prim. LocalID={1}", LogHeader, LocalID);
628 return; 947 return;
629 } 948 }
630 _scene.TaintedObject("BSPrim.AddForce", delegate() 949 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.AddForce", delegate()
631 { 950 {
632 OMV.Vector3 fSum = OMV.Vector3.Zero; 951 OMV.Vector3 fSum = OMV.Vector3.Zero;
633 lock (m_accumulatedForces) 952 lock (m_accumulatedForces)
634 { 953 {
954 // Sum the accumulated additional forces for one big force to apply once.
635 foreach (OMV.Vector3 v in m_accumulatedForces) 955 foreach (OMV.Vector3 v in m_accumulatedForces)
636 { 956 {
637 fSum += v; 957 fSum += v;
638 } 958 }
639 m_accumulatedForces.Clear(); 959 m_accumulatedForces.Clear();
640 } 960 }
641 // DetailLog("{0},BSPrim.AddObjectForce,taint,force={1}", LocalID, _force); 961 DetailLog("{0},BSPrim.AddForce,taint,force={1}", LocalID, fSum);
642 BulletSimAPI.AddObjectForce2(Body.Ptr, fSum); 962 if (fSum != OMV.Vector3.Zero)
963 BulletSimAPI.ApplyCentralForce2(PhysBody.ptr, fSum);
643 }); 964 });
644 } 965 }
645 966
646 public override void AddAngularForce(OMV.Vector3 force, bool pushforce) { 967 // An impulse force is scaled by the mass of the object.
647 // DetailLog("{0},BSPrim.AddAngularForce,call,angForce={1},push={2}", LocalID, force, pushforce); 968 public void ApplyForceImpulse(OMV.Vector3 impulse, bool inTaintTime)
648 // m_log.DebugFormat("{0}: AddAngularForce. f={1}, push={2}", LogHeader, force, pushforce); 969 {
970 OMV.Vector3 applyImpulse = impulse;
971 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ApplyForceImpulse", delegate()
972 {
973 DetailLog("{0},BSPrim.ApplyForceImpulse,taint,tImpulse={1}", LocalID, applyImpulse);
974 BulletSimAPI.ApplyCentralImpulse2(PhysBody.ptr, applyImpulse);
975 });
649 } 976 }
650 public override void SetMomentum(OMV.Vector3 momentum) { 977
651 // DetailLog("{0},BSPrim.SetMomentum,call,mom={1}", LocalID, momentum); 978 private List<OMV.Vector3> m_accumulatedAngularForces = new List<OMV.Vector3>();
979 public override void AddAngularForce(OMV.Vector3 force, bool pushforce) {
980 AddAngularForce(force, pushforce, false);
652 } 981 }
653 public override void SubscribeEvents(int ms) { 982 public void AddAngularForce(OMV.Vector3 force, bool pushforce, bool inTaintTime)
654 _subscribedEventsMs = ms; 983 {
655 if (ms > 0) 984 if (force.IsFinite())
656 { 985 {
657 // make sure first collision happens 986 // _force += force;
658 _nextCollisionOkTime = Util.EnvironmentTickCount() - _subscribedEventsMs; 987 lock (m_accumulatedAngularForces)
659 988 m_accumulatedAngularForces.Add(new OMV.Vector3(force));
660 Scene.TaintedObject("BSPrim.SubscribeEvents", delegate()
661 {
662 BulletSimAPI.AddToCollisionFlags2(Body.Ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS);
663 });
664 } 989 }
665 } 990 else
666 public override void UnSubscribeEvents() { 991 {
667 _subscribedEventsMs = 0; 992 m_log.WarnFormat("{0}: Got a NaN force applied to a prim. LocalID={1}", LogHeader, LocalID);
668 Scene.TaintedObject("BSPrim.UnSubscribeEvents", delegate() 993 return;
994 }
995 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.AddAngularForce", delegate()
669 { 996 {
670 BulletSimAPI.RemoveFromCollisionFlags2(Body.Ptr, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); 997 OMV.Vector3 fSum = OMV.Vector3.Zero;
998 lock (m_accumulatedAngularForces)
999 {
1000 // Sum the accumulated additional forces for one big force to apply once.
1001 foreach (OMV.Vector3 v in m_accumulatedAngularForces)
1002 {
1003 fSum += v;
1004 }
1005 m_accumulatedAngularForces.Clear();
1006 }
1007 DetailLog("{0},BSPrim.AddAngularForce,taint,aForce={1}", LocalID, fSum);
1008 if (fSum != OMV.Vector3.Zero)
1009 {
1010 BulletSimAPI.ApplyTorque2(PhysBody.ptr, fSum);
1011 _torque = fSum;
1012 }
671 }); 1013 });
672 } 1014 }
673 public override bool SubscribedEvents() { 1015 // A torque impulse.
674 return (_subscribedEventsMs > 0); 1016 public void ApplyTorqueImpulse(OMV.Vector3 impulse, bool inTaintTime)
1017 {
1018 OMV.Vector3 applyImpulse = impulse;
1019 PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ApplyTorqueImpulse", delegate()
1020 {
1021 BulletSimAPI.ApplyTorqueImpulse2(PhysBody.ptr, applyImpulse);
1022 });
675 } 1023 }
676 1024
1025 public override void SetMomentum(OMV.Vector3 momentum) {
1026 // DetailLog("{0},BSPrim.SetMomentum,call,mom={1}", LocalID, momentum);
1027 }
677 #region Mass Calculation 1028 #region Mass Calculation
678 1029
679 private float CalculateMass() 1030 private float CalculateMass()
@@ -682,19 +1033,19 @@ public sealed class BSPrim : PhysicsActor
682 float tmp; 1033 float tmp;
683 1034
684 float returnMass = 0; 1035 float returnMass = 0;
685 float hollowAmount = (float)_pbs.ProfileHollow * 2.0e-5f; 1036 float hollowAmount = (float)BaseShape.ProfileHollow * 2.0e-5f;
686 float hollowVolume = hollowAmount * hollowAmount; 1037 float hollowVolume = hollowAmount * hollowAmount;
687 1038
688 switch (_pbs.ProfileShape) 1039 switch (BaseShape.ProfileShape)
689 { 1040 {
690 case ProfileShape.Square: 1041 case ProfileShape.Square:
691 // default box 1042 // default box
692 1043
693 if (_pbs.PathCurve == (byte)Extrusion.Straight) 1044 if (BaseShape.PathCurve == (byte)Extrusion.Straight)
694 { 1045 {
695 if (hollowAmount > 0.0) 1046 if (hollowAmount > 0.0)
696 { 1047 {
697 switch (_pbs.HollowShape) 1048 switch (BaseShape.HollowShape)
698 { 1049 {
699 case HollowShape.Square: 1050 case HollowShape.Square:
700 case HollowShape.Same: 1051 case HollowShape.Same:
@@ -718,19 +1069,19 @@ public sealed class BSPrim : PhysicsActor
718 } 1069 }
719 } 1070 }
720 1071
721 else if (_pbs.PathCurve == (byte)Extrusion.Curve1) 1072 else if (BaseShape.PathCurve == (byte)Extrusion.Curve1)
722 { 1073 {
723 //a tube 1074 //a tube
724 1075
725 volume *= 0.78539816339e-2f * (float)(200 - _pbs.PathScaleX); 1076 volume *= 0.78539816339e-2f * (float)(200 - BaseShape.PathScaleX);
726 tmp= 1.0f -2.0e-2f * (float)(200 - _pbs.PathScaleY); 1077 tmp= 1.0f -2.0e-2f * (float)(200 - BaseShape.PathScaleY);
727 volume -= volume*tmp*tmp; 1078 volume -= volume*tmp*tmp;
728 1079
729 if (hollowAmount > 0.0) 1080 if (hollowAmount > 0.0)
730 { 1081 {
731 hollowVolume *= hollowAmount; 1082 hollowVolume *= hollowAmount;
732 1083
733 switch (_pbs.HollowShape) 1084 switch (BaseShape.HollowShape)
734 { 1085 {
735 case HollowShape.Square: 1086 case HollowShape.Square:
736 case HollowShape.Same: 1087 case HollowShape.Same:
@@ -755,13 +1106,13 @@ public sealed class BSPrim : PhysicsActor
755 1106
756 case ProfileShape.Circle: 1107 case ProfileShape.Circle:
757 1108
758 if (_pbs.PathCurve == (byte)Extrusion.Straight) 1109 if (BaseShape.PathCurve == (byte)Extrusion.Straight)
759 { 1110 {
760 volume *= 0.78539816339f; // elipse base 1111 volume *= 0.78539816339f; // elipse base
761 1112
762 if (hollowAmount > 0.0) 1113 if (hollowAmount > 0.0)
763 { 1114 {
764 switch (_pbs.HollowShape) 1115 switch (BaseShape.HollowShape)
765 { 1116 {
766 case HollowShape.Same: 1117 case HollowShape.Same:
767 case HollowShape.Circle: 1118 case HollowShape.Circle:
@@ -783,19 +1134,19 @@ public sealed class BSPrim : PhysicsActor
783 } 1134 }
784 } 1135 }
785 1136
786 else if (_pbs.PathCurve == (byte)Extrusion.Curve1) 1137 else if (BaseShape.PathCurve == (byte)Extrusion.Curve1)
787 { 1138 {
788 volume *= 0.61685027506808491367715568749226e-2f * (float)(200 - _pbs.PathScaleX); 1139 volume *= 0.61685027506808491367715568749226e-2f * (float)(200 - BaseShape.PathScaleX);
789 tmp = 1.0f - .02f * (float)(200 - _pbs.PathScaleY); 1140 tmp = 1.0f - .02f * (float)(200 - BaseShape.PathScaleY);
790 volume *= (1.0f - tmp * tmp); 1141 volume *= (1.0f - tmp * tmp);
791 1142
792 if (hollowAmount > 0.0) 1143 if (hollowAmount > 0.0)
793 { 1144 {
794 1145
795 // calculate the hollow volume by it's shape compared to the prim shape 1146 // calculate the hollow volume by it's shape compared to the prim shape
796 hollowVolume *= hollowAmount; 1147 hollowVolume *= hollowAmount;
797 1148
798 switch (_pbs.HollowShape) 1149 switch (BaseShape.HollowShape)
799 { 1150 {
800 case HollowShape.Same: 1151 case HollowShape.Same:
801 case HollowShape.Circle: 1152 case HollowShape.Circle:
@@ -819,7 +1170,7 @@ public sealed class BSPrim : PhysicsActor
819 break; 1170 break;
820 1171
821 case ProfileShape.HalfCircle: 1172 case ProfileShape.HalfCircle:
822 if (_pbs.PathCurve == (byte)Extrusion.Curve1) 1173 if (BaseShape.PathCurve == (byte)Extrusion.Curve1)
823 { 1174 {
824 volume *= 0.52359877559829887307710723054658f; 1175 volume *= 0.52359877559829887307710723054658f;
825 } 1176 }
@@ -827,7 +1178,7 @@ public sealed class BSPrim : PhysicsActor
827 1178
828 case ProfileShape.EquilateralTriangle: 1179 case ProfileShape.EquilateralTriangle:
829 1180
830 if (_pbs.PathCurve == (byte)Extrusion.Straight) 1181 if (BaseShape.PathCurve == (byte)Extrusion.Straight)
831 { 1182 {
832 volume *= 0.32475953f; 1183 volume *= 0.32475953f;
833 1184
@@ -835,7 +1186,7 @@ public sealed class BSPrim : PhysicsActor
835 { 1186 {
836 1187
837 // calculate the hollow volume by it's shape compared to the prim shape 1188 // calculate the hollow volume by it's shape compared to the prim shape
838 switch (_pbs.HollowShape) 1189 switch (BaseShape.HollowShape)
839 { 1190 {
840 case HollowShape.Same: 1191 case HollowShape.Same:
841 case HollowShape.Triangle: 1192 case HollowShape.Triangle:
@@ -860,11 +1211,11 @@ public sealed class BSPrim : PhysicsActor
860 volume *= (1.0f - hollowVolume); 1211 volume *= (1.0f - hollowVolume);
861 } 1212 }
862 } 1213 }
863 else if (_pbs.PathCurve == (byte)Extrusion.Curve1) 1214 else if (BaseShape.PathCurve == (byte)Extrusion.Curve1)
864 { 1215 {
865 volume *= 0.32475953f; 1216 volume *= 0.32475953f;
866 volume *= 0.01f * (float)(200 - _pbs.PathScaleX); 1217 volume *= 0.01f * (float)(200 - BaseShape.PathScaleX);
867 tmp = 1.0f - .02f * (float)(200 - _pbs.PathScaleY); 1218 tmp = 1.0f - .02f * (float)(200 - BaseShape.PathScaleY);
868 volume *= (1.0f - tmp * tmp); 1219 volume *= (1.0f - tmp * tmp);
869 1220
870 if (hollowAmount > 0.0) 1221 if (hollowAmount > 0.0)
@@ -872,7 +1223,7 @@ public sealed class BSPrim : PhysicsActor
872 1223
873 hollowVolume *= hollowAmount; 1224 hollowVolume *= hollowAmount;
874 1225
875 switch (_pbs.HollowShape) 1226 switch (BaseShape.HollowShape)
876 { 1227 {
877 case HollowShape.Same: 1228 case HollowShape.Same:
878 case HollowShape.Triangle: 1229 case HollowShape.Triangle:
@@ -912,26 +1263,26 @@ public sealed class BSPrim : PhysicsActor
912 float profileBegin; 1263 float profileBegin;
913 float profileEnd; 1264 float profileEnd;
914 1265
915 if (_pbs.PathCurve == (byte)Extrusion.Straight || _pbs.PathCurve == (byte)Extrusion.Flexible) 1266 if (BaseShape.PathCurve == (byte)Extrusion.Straight || BaseShape.PathCurve == (byte)Extrusion.Flexible)
916 { 1267 {
917 taperX1 = _pbs.PathScaleX * 0.01f; 1268 taperX1 = BaseShape.PathScaleX * 0.01f;
918 if (taperX1 > 1.0f) 1269 if (taperX1 > 1.0f)
919 taperX1 = 2.0f - taperX1; 1270 taperX1 = 2.0f - taperX1;
920 taperX = 1.0f - taperX1; 1271 taperX = 1.0f - taperX1;
921 1272
922 taperY1 = _pbs.PathScaleY * 0.01f; 1273 taperY1 = BaseShape.PathScaleY * 0.01f;
923 if (taperY1 > 1.0f) 1274 if (taperY1 > 1.0f)
924 taperY1 = 2.0f - taperY1; 1275 taperY1 = 2.0f - taperY1;
925 taperY = 1.0f - taperY1; 1276 taperY = 1.0f - taperY1;
926 } 1277 }
927 else 1278 else
928 { 1279 {
929 taperX = _pbs.PathTaperX * 0.01f; 1280 taperX = BaseShape.PathTaperX * 0.01f;
930 if (taperX < 0.0f) 1281 if (taperX < 0.0f)
931 taperX = -taperX; 1282 taperX = -taperX;
932 taperX1 = 1.0f - taperX; 1283 taperX1 = 1.0f - taperX;
933 1284
934 taperY = _pbs.PathTaperY * 0.01f; 1285 taperY = BaseShape.PathTaperY * 0.01f;
935 if (taperY < 0.0f) 1286 if (taperY < 0.0f)
936 taperY = -taperY; 1287 taperY = -taperY;
937 taperY1 = 1.0f - taperY; 1288 taperY1 = 1.0f - taperY;
@@ -941,20 +1292,18 @@ public sealed class BSPrim : PhysicsActor
941 1292
942 volume *= (taperX1 * taperY1 + 0.5f * (taperX1 * taperY + taperX * taperY1) + 0.3333333333f * taperX * taperY); 1293 volume *= (taperX1 * taperY1 + 0.5f * (taperX1 * taperY + taperX * taperY1) + 0.3333333333f * taperX * taperY);
943 1294
944 pathBegin = (float)_pbs.PathBegin * 2.0e-5f; 1295 pathBegin = (float)BaseShape.PathBegin * 2.0e-5f;
945 pathEnd = 1.0f - (float)_pbs.PathEnd * 2.0e-5f; 1296 pathEnd = 1.0f - (float)BaseShape.PathEnd * 2.0e-5f;
946 volume *= (pathEnd - pathBegin); 1297 volume *= (pathEnd - pathBegin);
947 1298
948 // this is crude aproximation 1299 // this is crude aproximation
949 profileBegin = (float)_pbs.ProfileBegin * 2.0e-5f; 1300 profileBegin = (float)BaseShape.ProfileBegin * 2.0e-5f;
950 profileEnd = 1.0f - (float)_pbs.ProfileEnd * 2.0e-5f; 1301 profileEnd = 1.0f - (float)BaseShape.ProfileEnd * 2.0e-5f;
951 volume *= (profileEnd - profileBegin); 1302 volume *= (profileEnd - profileBegin);
952 1303
953 returnMass = _density * volume; 1304 returnMass = _density * volume;
954 1305
955 /* 1306 /* Comment out code that computes the mass of the linkset. That is done in the Linkset class.
956 * This change means each object keeps its own mass and the Mass property
957 * will return the sum if we're part of a linkset.
958 if (IsRootOfLinkset) 1307 if (IsRootOfLinkset)
959 { 1308 {
960 foreach (BSPrim prim in _childrenPrims) 1309 foreach (BSPrim prim in _childrenPrims)
@@ -967,296 +1316,49 @@ public sealed class BSPrim : PhysicsActor
967 if (returnMass <= 0) 1316 if (returnMass <= 0)
968 returnMass = 0.0001f; 1317 returnMass = 0.0001f;
969 1318
970 if (returnMass > _scene.MaximumObjectMass) 1319 if (returnMass > PhysicsScene.MaximumObjectMass)
971 returnMass = _scene.MaximumObjectMass; 1320 returnMass = PhysicsScene.MaximumObjectMass;
972 1321
973 return returnMass; 1322 return returnMass;
974 }// end CalculateMass 1323 }// end CalculateMass
975 #endregion Mass Calculation 1324 #endregion Mass Calculation
976 1325
977 // Create the geometry information in Bullet for later use 1326 // Rebuild the geometry and object.
978 // The objects needs a hull if it's physical otherwise a mesh is enough 1327 // This is called when the shape changes so we need to recreate the mesh/hull.
979 // No locking here because this is done when we know physics is not simulating 1328 // Called at taint-time!!!
980 // if 'forceRebuild' is true, the geometry is rebuilt. Otherwise a previously built version is used 1329 private void CreateGeomAndObject(bool forceRebuild)
981 // Returns 'true' if the geometry was rebuilt
982 private bool CreateGeom(bool forceRebuild)
983 {
984 // the mesher thought this was too simple to mesh. Use a native Bullet collision shape.
985 bool ret = false;
986 if (!_scene.NeedsMeshing(_pbs))
987 {
988 if (_pbs.ProfileShape == ProfileShape.HalfCircle && _pbs.PathCurve == (byte)Extrusion.Curve1)
989 {
990 // if (_size.X == _size.Y && _size.Y == _size.Z && _size.X == _size.Z)
991 // {
992 // m_log.DebugFormat("{0}: CreateGeom: Defaulting to sphere of size {1}", LogHeader, _size);
993 if (forceRebuild || (_shapeType != ShapeData.PhysicsShapeType.SHAPE_SPHERE))
994 {
995 // DetailLog("{0},BSPrim.CreateGeom,sphere (force={1}", LocalID, forceRebuild);
996 _shapeType = ShapeData.PhysicsShapeType.SHAPE_SPHERE;
997 // Bullet native objects are scaled by the Bullet engine so pass the size in
998 _scale = _size;
999 // TODO: do we need to check for and destroy a mesh or hull that might have been left from before?
1000 ret = true;
1001 }
1002 // }
1003 }
1004 else
1005 {
1006 // m_log.DebugFormat("{0}: CreateGeom: Defaulting to box. lid={1}, type={2}, size={3}", LogHeader, LocalID, _shapeType, _size);
1007 if (forceRebuild || (_shapeType != ShapeData.PhysicsShapeType.SHAPE_BOX))
1008 {
1009 // DetailLog("{0},BSPrim.CreateGeom,box (force={1})", LocalID, forceRebuild);
1010 _shapeType = ShapeData.PhysicsShapeType.SHAPE_BOX;
1011 _scale = _size;
1012 // TODO: do we need to check for and destroy a mesh or hull that might have been left from before?
1013 ret = true;
1014 }
1015 }
1016 }
1017 else
1018 {
1019 if (IsPhysical)
1020 {
1021 if (forceRebuild || _hullKey == 0)
1022 {
1023 // physical objects require a hull for interaction.
1024 // This will create the mesh if it doesn't already exist
1025 CreateGeomHull();
1026 ret = true;
1027 }
1028 }
1029 else
1030 {
1031 if (forceRebuild || _meshKey == 0)
1032 {
1033 // Static (non-physical) objects only need a mesh for bumping into
1034 CreateGeomMesh();
1035 ret = true;
1036 }
1037 }
1038 }
1039 return ret;
1040 }
1041
1042 // No locking here because this is done when we know physics is not simulating
1043 private void CreateGeomMesh()
1044 {
1045 float lod = _pbs.SculptEntry ? _scene.SculptLOD : _scene.MeshLOD;
1046 ulong newMeshKey = (ulong)_pbs.GetMeshKey(_size, lod);
1047 // m_log.DebugFormat("{0}: CreateGeomMesh: lID={1}, oldKey={2}, newKey={3}", LogHeader, _localID, _meshKey, newMeshKey);
1048
1049 // if this new shape is the same as last time, don't recreate the mesh
1050 if (_meshKey == newMeshKey) return;
1051
1052 // DetailLog("{0},BSPrim.CreateGeomMesh,create,key={1}", LocalID, newMeshKey);
1053 // Since we're recreating new, get rid of any previously generated shape
1054 if (_meshKey != 0)
1055 {
1056 // m_log.DebugFormat("{0}: CreateGeom: deleting old mesh. lID={1}, Key={2}", LogHeader, _localID, _meshKey);
1057 // DetailLog("{0},BSPrim.CreateGeomMesh,deleteOld,key={1}", LocalID, _meshKey);
1058 BulletSimAPI.DestroyMesh(_scene.WorldID, _meshKey);
1059 _mesh = null;
1060 _meshKey = 0;
1061 }
1062
1063 _meshKey = newMeshKey;
1064 // always pass false for physicalness as this creates some sort of bounding box which we don't need
1065 _mesh = _scene.mesher.CreateMesh(_avName, _pbs, _size, lod, false);
1066
1067 int[] indices = _mesh.getIndexListAsInt();
1068 List<OMV.Vector3> vertices = _mesh.getVertexList();
1069
1070 float[] verticesAsFloats = new float[vertices.Count * 3];
1071 int vi = 0;
1072 foreach (OMV.Vector3 vv in vertices)
1073 {
1074 verticesAsFloats[vi++] = vv.X;
1075 verticesAsFloats[vi++] = vv.Y;
1076 verticesAsFloats[vi++] = vv.Z;
1077 }
1078
1079 // m_log.DebugFormat("{0}: CreateGeomMesh: calling CreateMesh. lid={1}, key={2}, indices={3}, vertices={4}",
1080 // LogHeader, _localID, _meshKey, indices.Length, vertices.Count);
1081 BulletSimAPI.CreateMesh(_scene.WorldID, _meshKey, indices.GetLength(0), indices,
1082 vertices.Count, verticesAsFloats);
1083
1084 _shapeType = ShapeData.PhysicsShapeType.SHAPE_MESH;
1085 // meshes are already scaled by the meshmerizer
1086 _scale = new OMV.Vector3(1f, 1f, 1f);
1087 // DetailLog("{0},BSPrim.CreateGeomMesh,done", LocalID);
1088 return;
1089 }
1090
1091 // No locking here because this is done when we know physics is not simulating
1092 private void CreateGeomHull()
1093 { 1330 {
1094 float lod = _pbs.SculptEntry ? _scene.SculptLOD : _scene.MeshLOD; 1331 // If this prim is part of a linkset, we must remove and restore the physical
1095 ulong newHullKey = (ulong)_pbs.GetMeshKey(_size, lod); 1332 // links if the body is rebuilt.
1096 // m_log.DebugFormat("{0}: CreateGeomHull: lID={1}, oldKey={2}, newKey={3}", LogHeader, _localID, _hullKey, newHullKey); 1333 bool needToRestoreLinkset = false;
1097 1334 bool needToRestoreVehicle = false;
1098 // if the hull hasn't changed, don't rebuild it 1335
1099 if (newHullKey == _hullKey) return; 1336 // Create the correct physical representation for this type of object.
1100 1337 // Updates PhysBody and PhysShape with the new information.
1101 // DetailLog("{0},BSPrim.CreateGeomHull,create,oldKey={1},newKey={2}", LocalID, _hullKey, newHullKey); 1338 // Ignore 'forceRebuild'. This routine makes the right choices and changes of necessary.
1102 1339 // Returns 'true' if either the body or the shape was changed.
1103 // Since we're recreating new, get rid of any previously generated shape 1340 PhysicsScene.Shapes.GetBodyAndShape(false, PhysicsScene.World, this, null, delegate(BulletBody dBody)
1104 if (_hullKey != 0)
1105 {
1106 // m_log.DebugFormat("{0}: CreateGeom: deleting old hull. Key={1}", LogHeader, _hullKey);
1107 // DetailLog("{0},BSPrim.CreateGeomHull,deleteOldHull,key={1}", LocalID, _hullKey);
1108 BulletSimAPI.DestroyHull(_scene.WorldID, _hullKey);
1109 _hullKey = 0;
1110 }
1111
1112 _hullKey = newHullKey;
1113
1114 // Make sure the underlying mesh exists and is correct
1115 CreateGeomMesh();
1116
1117 int[] indices = _mesh.getIndexListAsInt();
1118 List<OMV.Vector3> vertices = _mesh.getVertexList();
1119
1120 //format conversion from IMesh format to DecompDesc format
1121 List<int> convIndices = new List<int>();
1122 List<float3> convVertices = new List<float3>();
1123 for (int ii = 0; ii < indices.GetLength(0); ii++)
1124 {
1125 convIndices.Add(indices[ii]);
1126 }
1127 foreach (OMV.Vector3 vv in vertices)
1128 { 1341 {
1129 convVertices.Add(new float3(vv.X, vv.Y, vv.Z)); 1342 // Called if the current prim body is about to be destroyed.
1130 } 1343 // Remove all the physical dependencies on the old body.
1344 // (Maybe someday make the changing of BSShape an event handled by BSLinkset.)
1345 needToRestoreLinkset = Linkset.RemoveBodyDependencies(this);
1346 needToRestoreVehicle = _vehicle.RemoveBodyDependencies(this);
1347 });
1131 1348
1132 // setup and do convex hull conversion 1349 if (needToRestoreLinkset)
1133 _hulls = new List<ConvexResult>();
1134 DecompDesc dcomp = new DecompDesc();
1135 dcomp.mIndices = convIndices;
1136 dcomp.mVertices = convVertices;
1137 ConvexBuilder convexBuilder = new ConvexBuilder(HullReturn);
1138 // create the hull into the _hulls variable
1139 convexBuilder.process(dcomp);
1140
1141 // Convert the vertices and indices for passing to unmanaged.
1142 // The hull information is passed as a large floating point array.
1143 // The format is:
1144 // convHulls[0] = number of hulls
1145 // convHulls[1] = number of vertices in first hull
1146 // convHulls[2] = hull centroid X coordinate
1147 // convHulls[3] = hull centroid Y coordinate
1148 // convHulls[4] = hull centroid Z coordinate
1149 // convHulls[5] = first hull vertex X
1150 // convHulls[6] = first hull vertex Y
1151 // convHulls[7] = first hull vertex Z
1152 // convHulls[8] = second hull vertex X
1153 // ...
1154 // convHulls[n] = number of vertices in second hull
1155 // convHulls[n+1] = second hull centroid X coordinate
1156 // ...
1157 //
1158 // TODO: is is very inefficient. Someday change the convex hull generator to return
1159 // data structures that do not need to be converted in order to pass to Bullet.
1160 // And maybe put the values directly into pinned memory rather than marshaling.
1161 int hullCount = _hulls.Count;
1162 int totalVertices = 1; // include one for the count of the hulls
1163 foreach (ConvexResult cr in _hulls)
1164 { 1350 {
1165 totalVertices += 4; // add four for the vertex count and centroid 1351 // If physical body dependencies were removed, restore them
1166 totalVertices += cr.HullIndices.Count * 3; // we pass just triangles 1352 Linkset.RestoreBodyDependencies(this);
1167 } 1353 }
1168 float[] convHulls = new float[totalVertices]; 1354 if (needToRestoreVehicle)
1169
1170 convHulls[0] = (float)hullCount;
1171 int jj = 1;
1172 foreach (ConvexResult cr in _hulls)
1173 { 1355 {
1174 // copy vertices for index access 1356 // If physical body dependencies were removed, restore them
1175 float3[] verts = new float3[cr.HullVertices.Count]; 1357 _vehicle.RestoreBodyDependencies(this);
1176 int kk = 0;
1177 foreach (float3 ff in cr.HullVertices)
1178 {
1179 verts[kk++] = ff;
1180 }
1181
1182 // add to the array one hull's worth of data
1183 convHulls[jj++] = cr.HullIndices.Count;
1184 convHulls[jj++] = 0f; // centroid x,y,z
1185 convHulls[jj++] = 0f;
1186 convHulls[jj++] = 0f;
1187 foreach (int ind in cr.HullIndices)
1188 {
1189 convHulls[jj++] = verts[ind].x;
1190 convHulls[jj++] = verts[ind].y;
1191 convHulls[jj++] = verts[ind].z;
1192 }
1193 } 1358 }
1194 1359
1195 // create the hull definition in Bullet 1360 // Make sure the properties are set on the new object
1196 // m_log.DebugFormat("{0}: CreateGeom: calling CreateHull. lid={1}, key={2}, hulls={3}", LogHeader, _localID, _hullKey, hullCount); 1361 UpdatePhysicalParameters();
1197 BulletSimAPI.CreateHull(_scene.WorldID, _hullKey, hullCount, convHulls);
1198 _shapeType = ShapeData.PhysicsShapeType.SHAPE_HULL;
1199 // meshes are already scaled by the meshmerizer
1200 _scale = new OMV.Vector3(1f, 1f, 1f);
1201 // DetailLog("{0},BSPrim.CreateGeomHull,done", LocalID);
1202 return;
1203 }
1204
1205 // Callback from convex hull creater with a newly created hull.
1206 // Just add it to the collection of hulls for this shape.
1207 private void HullReturn(ConvexResult result)
1208 {
1209 _hulls.Add(result);
1210 return;
1211 }
1212
1213 // Create an object in Bullet if it has not already been created
1214 // No locking here because this is done when the physics engine is not simulating
1215 // Returns 'true' if an object was actually created.
1216 private bool CreateObject()
1217 {
1218 // this routine is called when objects are rebuilt.
1219
1220 // the mesh or hull must have already been created in Bullet
1221 ShapeData shape;
1222 FillShapeInfo(out shape);
1223 // m_log.DebugFormat("{0}: CreateObject: lID={1}, shape={2}", LogHeader, _localID, shape.Type);
1224 bool ret = BulletSimAPI.CreateObject(_scene.WorldID, shape);
1225
1226 // the CreateObject() may have recreated the rigid body. Make sure we have the latest.
1227 Body = new BulletBody(LocalID, BulletSimAPI.GetBodyHandle2(_scene.World.Ptr, LocalID));
1228
1229 return ret;
1230 }
1231
1232 // Copy prim's info into the BulletSim shape description structure
1233 public void FillShapeInfo(out ShapeData shape)
1234 {
1235 shape.ID = _localID;
1236 shape.Type = _shapeType;
1237 shape.Position = _position;
1238 shape.Rotation = _orientation;
1239 shape.Velocity = _velocity;
1240 shape.Scale = _scale;
1241 shape.Mass = _isPhysical ? _mass : 0f;
1242 shape.Buoyancy = _buoyancy;
1243 shape.HullKey = _hullKey;
1244 shape.MeshKey = _meshKey;
1245 shape.Friction = _friction;
1246 shape.Restitution = _restitution;
1247 shape.Collidable = (!IsPhantom) ? ShapeData.numericTrue : ShapeData.numericFalse;
1248 shape.Static = _isPhysical ? ShapeData.numericFalse : ShapeData.numericTrue;
1249 }
1250
1251
1252 // Rebuild the geometry and object.
1253 // This is called when the shape changes so we need to recreate the mesh/hull.
1254 // No locking here because this is done when the physics engine is not simulating
1255 private void RecreateGeomAndObject()
1256 {
1257 // m_log.DebugFormat("{0}: RecreateGeomAndObject. lID={1}", LogHeader, _localID);
1258 if (CreateGeom(true))
1259 CreateObject();
1260 return; 1362 return;
1261 } 1363 }
1262 1364
@@ -1277,7 +1379,7 @@ public sealed class BSPrim : PhysicsActor
1277 const float ACCELERATION_TOLERANCE = 0.01f; 1379 const float ACCELERATION_TOLERANCE = 0.01f;
1278 const float ROTATIONAL_VELOCITY_TOLERANCE = 0.01f; 1380 const float ROTATIONAL_VELOCITY_TOLERANCE = 0.01f;
1279 1381
1280 public void UpdateProperties(EntityProperties entprop) 1382 public override void UpdateProperties(EntityProperties entprop)
1281 { 1383 {
1282 /* 1384 /*
1283 UpdatedProperties changed = 0; 1385 UpdatedProperties changed = 0;
@@ -1315,7 +1417,7 @@ public sealed class BSPrim : PhysicsActor
1315 if (changed != 0) 1417 if (changed != 0)
1316 { 1418 {
1317 // Only update the position of single objects and linkset roots 1419 // Only update the position of single objects and linkset roots
1318 if (this._parentPrim == null) 1420 if (Linkset.IsRoot(this))
1319 { 1421 {
1320 base.RequestPhysicsterseUpdate(); 1422 base.RequestPhysicsterseUpdate();
1321 } 1423 }
@@ -1325,78 +1427,46 @@ public sealed class BSPrim : PhysicsActor
1325 // Don't check for damping here -- it's done in BulletSim and SceneObjectPart. 1427 // Don't check for damping here -- it's done in BulletSim and SceneObjectPart.
1326 1428
1327 // Updates only for individual prims and for the root object of a linkset. 1429 // Updates only for individual prims and for the root object of a linkset.
1328 if (_linkset.IsRoot(this)) 1430 if (Linkset.IsRoot(this))
1329 { 1431 {
1330 // Assign to the local variables so the normal set action does not happen 1432 // Assign directly to the local variables so the normal set action does not happen
1331 _position = entprop.Position; 1433 _position = entprop.Position;
1332 _orientation = entprop.Rotation; 1434 _orientation = entprop.Rotation;
1333 _velocity = entprop.Velocity; 1435 _velocity = entprop.Velocity;
1334 _acceleration = entprop.Acceleration; 1436 _acceleration = entprop.Acceleration;
1335 _rotationalVelocity = entprop.RotationalVelocity; 1437 _rotationalVelocity = entprop.RotationalVelocity;
1336 1438
1337 // m_log.DebugFormat("{0}: RequestTerseUpdate. id={1}, ch={2}, pos={3}, rot={4}, vel={5}, acc={6}, rvel={7}", 1439 // The sanity check can change the velocity and/or position.
1338 // LogHeader, LocalID, changed, _position, _orientation, _velocity, _acceleration, _rotationalVelocity); 1440 if (PositionSanityCheck(true))
1339 // DetailLog("{0},BSPrim.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5}", 1441 {
1340 // LocalID, _position, _orientation, _velocity, _acceleration, _rotationalVelocity); 1442 entprop.Position = _position;
1443 entprop.Velocity = _velocity;
1444 }
1445
1446 // remember the current and last set values
1447 LastEntityProperties = CurrentEntityProperties;
1448 CurrentEntityProperties = entprop;
1449
1450 OMV.Vector3 direction = OMV.Vector3.UnitX * _orientation;
1451 DetailLog("{0},BSPrim.UpdateProperties,call,pos={1},orient={2},dir={3},vel={4},rotVel={5}",
1452 LocalID, _position, _orientation, direction, _velocity, _rotationalVelocity);
1453
1454 // BulletSimAPI.DumpRigidBody2(PhysicsScene.World.ptr, BSBody.ptr); // DEBUG DEBUG DEBUG
1341 1455
1342 base.RequestPhysicsterseUpdate(); 1456 base.RequestPhysicsterseUpdate();
1343 } 1457 }
1344 /* 1458 /*
1345 else 1459 else
1346 { 1460 {
1347 // For debugging, we also report the movement of children 1461 // For debugging, report the movement of children
1348 DetailLog("{0},BSPrim.UpdateProperties,child,pos={1},orient={2},vel={3},accel={4},rotVel={5}", 1462 DetailLog("{0},BSPrim.UpdateProperties,child,pos={1},orient={2},vel={3},accel={4},rotVel={5}",
1349 LocalID, entprop.Position, entprop.Rotation, entprop.Velocity, 1463 LocalID, entprop.Position, entprop.Rotation, entprop.Velocity,
1350 entprop.Acceleration, entprop.RotationalVelocity); 1464 entprop.Acceleration, entprop.RotationalVelocity);
1351 } 1465 }
1352 */ 1466 */
1353 }
1354
1355 // I've collided with something
1356 CollisionEventUpdate collisionCollection;
1357 public void Collide(uint collidingWith, ActorTypes type, OMV.Vector3 contactPoint, OMV.Vector3 contactNormal, float pentrationDepth)
1358 {
1359 // m_log.DebugFormat("{0}: Collide: ms={1}, id={2}, with={3}", LogHeader, _subscribedEventsMs, LocalID, collidingWith);
1360
1361 // The following lines make IsColliding() and IsCollidingGround() work
1362 _collidingStep = _scene.SimulationStep;
1363 if (collidingWith == BSScene.TERRAIN_ID || collidingWith == BSScene.GROUNDPLANE_ID)
1364 {
1365 _collidingGroundStep = _scene.SimulationStep;
1366 }
1367
1368 // DetailLog("{0},BSPrim.Collison,call,with={1}", LocalID, collidingWith);
1369
1370 // if someone is subscribed to collision events....
1371 if (_subscribedEventsMs != 0) {
1372 // throttle the collisions to the number of milliseconds specified in the subscription
1373 int nowTime = _scene.SimulationNowTime;
1374 if (nowTime >= _nextCollisionOkTime) {
1375 _nextCollisionOkTime = nowTime + _subscribedEventsMs;
1376 1467
1377 if (collisionCollection == null) 1468 // The linkset implimentation might want to know about this.
1378 collisionCollection = new CollisionEventUpdate(); 1469 Linkset.UpdateProperties(this);
1379 collisionCollection.AddCollider(collidingWith, new ContactPoint(contactPoint, contactNormal, pentrationDepth));
1380 }
1381 }
1382 }
1383
1384 // The scene is telling us it's time to pass our collected collisions into the simulator
1385 public void SendCollisions()
1386 {
1387 if (collisionCollection != null && collisionCollection.Count > 0)
1388 {
1389 base.SendCollisionUpdate(collisionCollection);
1390 // The collisionCollection structure is passed around in the simulator.
1391 // Make sure we don't have a handle to that one and that a new one is used next time.
1392 collisionCollection = null;
1393 }
1394 }
1395
1396 // Invoke the detailed logger and output something if it's enabled.
1397 private void DetailLog(string msg, params Object[] args)
1398 {
1399 Scene.PhysicsLogging.Write(msg, args);
1400 } 1470 }
1401} 1471}
1402} 1472}