diff options
Diffstat (limited to 'OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs')
-rw-r--r-- | OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs | 889 |
1 files changed, 446 insertions, 443 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs index 826261c..f5b0361 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSPrim.cs | |||
@@ -39,7 +39,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin | |||
39 | { | 39 | { |
40 | 40 | ||
41 | [Serializable] | 41 | [Serializable] |
42 | public sealed class BSPrim : BSPhysObject | 42 | public class BSPrim : BSPhysObject |
43 | { | 43 | { |
44 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); | 44 | private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); |
45 | private static readonly string LogHeader = "[BULLETS PRIM]"; | 45 | private static readonly string LogHeader = "[BULLETS PRIM]"; |
@@ -50,39 +50,38 @@ public sealed class BSPrim : BSPhysObject | |||
50 | private bool _grabbed; | 50 | private bool _grabbed; |
51 | private bool _isSelected; | 51 | private bool _isSelected; |
52 | private bool _isVolumeDetect; | 52 | private bool _isVolumeDetect; |
53 | |||
54 | // _position is what the simulator thinks the positions of the prim is. | ||
53 | private OMV.Vector3 _position; | 55 | private OMV.Vector3 _position; |
56 | |||
54 | private float _mass; // the mass of this object | 57 | private float _mass; // the mass of this object |
55 | private float _density; | ||
56 | private OMV.Vector3 _force; | ||
57 | private OMV.Vector3 _velocity; | ||
58 | private OMV.Vector3 _torque; | ||
59 | private float _collisionScore; | ||
60 | private OMV.Vector3 _acceleration; | 58 | private OMV.Vector3 _acceleration; |
61 | private OMV.Quaternion _orientation; | 59 | private OMV.Quaternion _orientation; |
62 | private int _physicsActorType; | 60 | private int _physicsActorType; |
63 | private bool _isPhysical; | 61 | private bool _isPhysical; |
64 | private bool _flying; | 62 | private bool _flying; |
65 | private float _friction; | ||
66 | private float _restitution; | ||
67 | private bool _setAlwaysRun; | 63 | private bool _setAlwaysRun; |
68 | private bool _throttleUpdates; | 64 | private bool _throttleUpdates; |
69 | private bool _isColliding; | ||
70 | private bool _collidingGround; | ||
71 | private bool _collidingObj; | ||
72 | private bool _floatOnWater; | 65 | private bool _floatOnWater; |
73 | private OMV.Vector3 _rotationalVelocity; | 66 | private OMV.Vector3 _rotationalVelocity; |
74 | private bool _kinematic; | 67 | private bool _kinematic; |
75 | private float _buoyancy; | 68 | private float _buoyancy; |
76 | 69 | ||
77 | private BSDynamics _vehicle; | 70 | private int CrossingFailures { get; set; } |
78 | 71 | ||
79 | private OMV.Vector3 _PIDTarget; | 72 | // Keep a handle to the vehicle actor so it is easy to set parameters on same. |
80 | private bool _usePID; | 73 | public BSDynamics VehicleActor; |
81 | private float _PIDTau; | 74 | public const string VehicleActorName = "BasicVehicle"; |
82 | private bool _useHoverPID; | 75 | |
83 | private float _PIDHoverHeight; | 76 | // Parameters for the hover actor |
84 | private PIDHoverType _PIDHoverType; | 77 | public const string HoverActorName = "HoverActor"; |
85 | private float _PIDHoverTao; | 78 | // Parameters for the axis lock actor |
79 | public const String LockedAxisActorName = "BSPrim.LockedAxis"; | ||
80 | // Parameters for the move to target actor | ||
81 | public const string MoveToTargetActorName = "MoveToTargetActor"; | ||
82 | // Parameters for the setForce and setTorque actors | ||
83 | public const string SetForceActorName = "SetForceActor"; | ||
84 | public const string SetTorqueActorName = "SetTorqueActor"; | ||
86 | 85 | ||
87 | public BSPrim(uint localID, String primName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size, | 86 | public BSPrim(uint localID, String primName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 size, |
88 | OMV.Quaternion rotation, PrimitiveBaseShape pbs, bool pisPhysical) | 87 | OMV.Quaternion rotation, PrimitiveBaseShape pbs, bool pisPhysical) |
@@ -95,32 +94,28 @@ public sealed class BSPrim : BSPhysObject | |||
95 | Scale = size; // prims are the size the user wants them to be (different for BSCharactes). | 94 | Scale = size; // prims are the size the user wants them to be (different for BSCharactes). |
96 | _orientation = rotation; | 95 | _orientation = rotation; |
97 | _buoyancy = 0f; | 96 | _buoyancy = 0f; |
98 | _velocity = OMV.Vector3.Zero; | 97 | RawVelocity = OMV.Vector3.Zero; |
99 | _rotationalVelocity = OMV.Vector3.Zero; | 98 | _rotationalVelocity = OMV.Vector3.Zero; |
100 | BaseShape = pbs; | 99 | BaseShape = pbs; |
101 | _isPhysical = pisPhysical; | 100 | _isPhysical = pisPhysical; |
102 | _isVolumeDetect = false; | 101 | _isVolumeDetect = false; |
103 | 102 | ||
104 | // Someday set default attributes based on the material but, for now, we don't know the prim material yet. | 103 | // We keep a handle to the vehicle actor so we can set vehicle parameters later. |
105 | // MaterialAttributes primMat = BSMaterials.GetAttributes(Material, pisPhysical); | 104 | VehicleActor = new BSDynamics(PhysScene, this, VehicleActorName); |
106 | _density = PhysicsScene.Params.defaultDensity; | 105 | PhysicalActors.Add(VehicleActorName, VehicleActor); |
107 | _friction = PhysicsScene.Params.defaultFriction; | ||
108 | _restitution = PhysicsScene.Params.defaultRestitution; | ||
109 | |||
110 | _vehicle = new BSDynamics(PhysicsScene, this); // add vehicleness | ||
111 | 106 | ||
112 | _mass = CalculateMass(); | 107 | _mass = CalculateMass(); |
113 | 108 | ||
114 | // Cause linkset variables to be initialized (like mass) | 109 | // DetailLog("{0},BSPrim.constructor,call", LocalID); |
115 | Linkset.Refresh(this); | ||
116 | |||
117 | DetailLog("{0},BSPrim.constructor,call", LocalID); | ||
118 | // do the actual object creation at taint time | 110 | // do the actual object creation at taint time |
119 | PhysicsScene.TaintedObject("BSPrim.create", delegate() | 111 | PhysScene.TaintedObject("BSPrim.create", delegate() |
120 | { | 112 | { |
113 | // Make sure the object is being created with some sanity. | ||
114 | ExtremeSanityCheck(true /* inTaintTime */); | ||
115 | |||
121 | CreateGeomAndObject(true); | 116 | CreateGeomAndObject(true); |
122 | 117 | ||
123 | CurrentCollisionFlags = PhysicsScene.PE.GetCollisionFlags(PhysBody); | 118 | CurrentCollisionFlags = PhysScene.PE.GetCollisionFlags(PhysBody); |
124 | }); | 119 | }); |
125 | } | 120 | } |
126 | 121 | ||
@@ -130,26 +125,17 @@ public sealed class BSPrim : BSPhysObject | |||
130 | // m_log.DebugFormat("{0}: Destroy, id={1}", LogHeader, LocalID); | 125 | // m_log.DebugFormat("{0}: Destroy, id={1}", LogHeader, LocalID); |
131 | base.Destroy(); | 126 | base.Destroy(); |
132 | 127 | ||
133 | // Undo any links between me and any other object | ||
134 | BSPhysObject parentBefore = Linkset.LinksetRoot; | ||
135 | int childrenBefore = Linkset.NumberOfChildren; | ||
136 | |||
137 | Linkset = Linkset.RemoveMeFromLinkset(this); | ||
138 | |||
139 | DetailLog("{0},BSPrim.Destroy,call,parentBefore={1},childrenBefore={2},parentAfter={3},childrenAfter={4}", | ||
140 | LocalID, parentBefore.LocalID, childrenBefore, Linkset.LinksetRoot.LocalID, Linkset.NumberOfChildren); | ||
141 | |||
142 | // Undo any vehicle properties | 128 | // Undo any vehicle properties |
143 | this.VehicleType = (int)Vehicle.TYPE_NONE; | 129 | this.VehicleType = (int)Vehicle.TYPE_NONE; |
144 | 130 | ||
145 | PhysicsScene.TaintedObject("BSPrim.destroy", delegate() | 131 | PhysScene.TaintedObject("BSPrim.Destroy", delegate() |
146 | { | 132 | { |
147 | DetailLog("{0},BSPrim.Destroy,taint,", LocalID); | 133 | DetailLog("{0},BSPrim.Destroy,taint,", LocalID); |
148 | // If there are physical body and shape, release my use of same. | 134 | // If there are physical body and shape, release my use of same. |
149 | PhysicsScene.Shapes.DereferenceBody(PhysBody, true, null); | 135 | PhysScene.Shapes.DereferenceBody(PhysBody, null); |
150 | PhysBody.Clear(); | 136 | PhysBody.Clear(); |
151 | PhysicsScene.Shapes.DereferenceShape(PhysShape, true, null); | 137 | PhysShape.Dereference(PhysScene); |
152 | PhysShape.Clear(); | 138 | PhysShape = new BSShapeNull(); |
153 | }); | 139 | }); |
154 | } | 140 | } |
155 | 141 | ||
@@ -171,17 +157,13 @@ public sealed class BSPrim : BSPhysObject | |||
171 | public override PrimitiveBaseShape Shape { | 157 | public override PrimitiveBaseShape Shape { |
172 | set { | 158 | set { |
173 | BaseShape = value; | 159 | BaseShape = value; |
160 | PrimAssetState = PrimAssetCondition.Unknown; | ||
174 | ForceBodyShapeRebuild(false); | 161 | ForceBodyShapeRebuild(false); |
175 | } | 162 | } |
176 | } | 163 | } |
177 | // Whatever the linkset wants is what I want. | ||
178 | public override BSPhysicsShapeType PreferredPhysicalShape | ||
179 | { get { return Linkset.PreferredPhysicalShape(this); } } | ||
180 | |||
181 | public override bool ForceBodyShapeRebuild(bool inTaintTime) | 164 | public override bool ForceBodyShapeRebuild(bool inTaintTime) |
182 | { | 165 | { |
183 | LastAssetBuildFailed = false; | 166 | PhysScene.TaintedObject(inTaintTime, "BSPrim.ForceBodyShapeRebuild", delegate() |
184 | PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ForceBodyShapeRebuild", delegate() | ||
185 | { | 167 | { |
186 | _mass = CalculateMass(); // changing the shape changes the mass | 168 | _mass = CalculateMass(); // changing the shape changes the mass |
187 | CreateGeomAndObject(true); | 169 | CreateGeomAndObject(true); |
@@ -198,7 +180,7 @@ public sealed class BSPrim : BSPhysObject | |||
198 | if (value != _isSelected) | 180 | if (value != _isSelected) |
199 | { | 181 | { |
200 | _isSelected = value; | 182 | _isSelected = value; |
201 | PhysicsScene.TaintedObject("BSPrim.setSelected", delegate() | 183 | PhysScene.TaintedObject("BSPrim.setSelected", delegate() |
202 | { | 184 | { |
203 | DetailLog("{0},BSPrim.selected,taint,selected={1}", LocalID, _isSelected); | 185 | DetailLog("{0},BSPrim.selected,taint,selected={1}", LocalID, _isSelected); |
204 | SetObjectDynamic(false); | 186 | SetObjectDynamic(false); |
@@ -206,37 +188,31 @@ public sealed class BSPrim : BSPhysObject | |||
206 | } | 188 | } |
207 | } | 189 | } |
208 | } | 190 | } |
209 | public override void CrossingFailure() { return; } | 191 | public override bool IsSelected |
192 | { | ||
193 | get { return _isSelected; } | ||
194 | } | ||
210 | 195 | ||
211 | // link me to the specified parent | 196 | public override void CrossingFailure() |
212 | public override void link(PhysicsActor obj) { | 197 | { |
213 | BSPrim parent = obj as BSPrim; | 198 | CrossingFailures++; |
214 | if (parent != null) | 199 | if (CrossingFailures > BSParam.CrossingFailuresBeforeOutOfBounds) |
215 | { | 200 | { |
216 | BSPhysObject parentBefore = Linkset.LinksetRoot; | 201 | base.RaiseOutOfBounds(RawPosition); |
217 | int childrenBefore = Linkset.NumberOfChildren; | 202 | } |
218 | 203 | else if (CrossingFailures == BSParam.CrossingFailuresBeforeOutOfBounds) | |
219 | Linkset = parent.Linkset.AddMeToLinkset(this); | 204 | { |
220 | 205 | m_log.WarnFormat("{0} Too many crossing failures for {1}", LogHeader, Name); | |
221 | DetailLog("{0},BSPrim.link,call,parentBefore={1}, childrenBefore=={2}, parentAfter={3}, childrenAfter={4}", | ||
222 | LocalID, parentBefore.LocalID, childrenBefore, Linkset.LinksetRoot.LocalID, Linkset.NumberOfChildren); | ||
223 | } | 206 | } |
224 | return; | 207 | return; |
225 | } | 208 | } |
226 | 209 | ||
210 | // link me to the specified parent | ||
211 | public override void link(PhysicsActor obj) { | ||
212 | } | ||
213 | |||
227 | // delink me from my linkset | 214 | // delink me from my linkset |
228 | public override void delink() { | 215 | public override void delink() { |
229 | // TODO: decide if this parent checking needs to happen at taint time | ||
230 | // Race condition here: if link() and delink() in same simulation tick, the delink will not happen | ||
231 | |||
232 | BSPhysObject parentBefore = Linkset.LinksetRoot; | ||
233 | int childrenBefore = Linkset.NumberOfChildren; | ||
234 | |||
235 | Linkset = Linkset.RemoveMeFromLinkset(this); | ||
236 | |||
237 | DetailLog("{0},BSPrim.delink,parentBefore={1},childrenBefore={2},parentAfter={3},childrenAfter={4}, ", | ||
238 | LocalID, parentBefore.LocalID, childrenBefore, Linkset.LinksetRoot.LocalID, Linkset.NumberOfChildren); | ||
239 | return; | ||
240 | } | 216 | } |
241 | 217 | ||
242 | // Set motion values to zero. | 218 | // Set motion values to zero. |
@@ -245,28 +221,28 @@ public sealed class BSPrim : BSPhysObject | |||
245 | // Called at taint time! | 221 | // Called at taint time! |
246 | public override void ZeroMotion(bool inTaintTime) | 222 | public override void ZeroMotion(bool inTaintTime) |
247 | { | 223 | { |
248 | _velocity = OMV.Vector3.Zero; | 224 | RawVelocity = OMV.Vector3.Zero; |
249 | _acceleration = OMV.Vector3.Zero; | 225 | _acceleration = OMV.Vector3.Zero; |
250 | _rotationalVelocity = OMV.Vector3.Zero; | 226 | _rotationalVelocity = OMV.Vector3.Zero; |
251 | 227 | ||
252 | // Zero some other properties in the physics engine | 228 | // Zero some other properties in the physics engine |
253 | PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate() | 229 | PhysScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate() |
254 | { | 230 | { |
255 | if (PhysBody.HasPhysicalBody) | 231 | if (PhysBody.HasPhysicalBody) |
256 | PhysicsScene.PE.ClearAllForces(PhysBody); | 232 | PhysScene.PE.ClearAllForces(PhysBody); |
257 | }); | 233 | }); |
258 | } | 234 | } |
259 | public override void ZeroAngularMotion(bool inTaintTime) | 235 | public override void ZeroAngularMotion(bool inTaintTime) |
260 | { | 236 | { |
261 | _rotationalVelocity = OMV.Vector3.Zero; | 237 | _rotationalVelocity = OMV.Vector3.Zero; |
262 | // Zero some other properties in the physics engine | 238 | // Zero some other properties in the physics engine |
263 | PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate() | 239 | PhysScene.TaintedObject(inTaintTime, "BSPrim.ZeroMotion", delegate() |
264 | { | 240 | { |
265 | // DetailLog("{0},BSPrim.ZeroAngularMotion,call,rotVel={1}", LocalID, _rotationalVelocity); | 241 | // DetailLog("{0},BSPrim.ZeroAngularMotion,call,rotVel={1}", LocalID, _rotationalVelocity); |
266 | if (PhysBody.HasPhysicalBody) | 242 | if (PhysBody.HasPhysicalBody) |
267 | { | 243 | { |
268 | PhysicsScene.PE.SetInterpolationAngularVelocity(PhysBody, _rotationalVelocity); | 244 | PhysScene.PE.SetInterpolationAngularVelocity(PhysBody, _rotationalVelocity); |
269 | PhysicsScene.PE.SetAngularVelocity(PhysBody, _rotationalVelocity); | 245 | PhysScene.PE.SetAngularVelocity(PhysBody, _rotationalVelocity); |
270 | } | 246 | } |
271 | }); | 247 | }); |
272 | } | 248 | } |
@@ -274,6 +250,25 @@ public sealed class BSPrim : BSPhysObject | |||
274 | public override void LockAngularMotion(OMV.Vector3 axis) | 250 | public override void LockAngularMotion(OMV.Vector3 axis) |
275 | { | 251 | { |
276 | DetailLog("{0},BSPrim.LockAngularMotion,call,axis={1}", LocalID, axis); | 252 | DetailLog("{0},BSPrim.LockAngularMotion,call,axis={1}", LocalID, axis); |
253 | |||
254 | // "1" means free, "0" means locked | ||
255 | OMV.Vector3 locking = LockedAxisFree; | ||
256 | if (axis.X != 1) locking.X = 0f; | ||
257 | if (axis.Y != 1) locking.Y = 0f; | ||
258 | if (axis.Z != 1) locking.Z = 0f; | ||
259 | LockedAngularAxis = locking; | ||
260 | |||
261 | EnableActor(LockedAngularAxis != LockedAxisFree, LockedAxisActorName, delegate() | ||
262 | { | ||
263 | return new BSActorLockAxis(PhysScene, this, LockedAxisActorName); | ||
264 | }); | ||
265 | |||
266 | // Update parameters so the new actor's Refresh() action is called at the right time. | ||
267 | PhysScene.TaintedObject("BSPrim.LockAngularMotion", delegate() | ||
268 | { | ||
269 | UpdatePhysicalParameters(); | ||
270 | }); | ||
271 | |||
277 | return; | 272 | return; |
278 | } | 273 | } |
279 | 274 | ||
@@ -284,15 +279,8 @@ public sealed class BSPrim : BSPhysObject | |||
284 | } | 279 | } |
285 | public override OMV.Vector3 Position { | 280 | public override OMV.Vector3 Position { |
286 | get { | 281 | get { |
287 | /* NOTE: this refetch is not necessary. The simulator knows about linkset children | ||
288 | * and does not fetch this position info for children. Thus this is commented out. | ||
289 | // child prims move around based on their parent. Need to get the latest location | ||
290 | if (!Linkset.IsRoot(this)) | ||
291 | _position = Linkset.PositionGet(this); | ||
292 | */ | ||
293 | |||
294 | // don't do the GetObjectPosition for root elements because this function is called a zillion times. | 282 | // don't do the GetObjectPosition for root elements because this function is called a zillion times. |
295 | // _position = PhysicsScene.PE.GetObjectPosition2(PhysicsScene.World, BSBody); | 283 | // _position = ForcePosition; |
296 | return _position; | 284 | return _position; |
297 | } | 285 | } |
298 | set { | 286 | set { |
@@ -306,26 +294,24 @@ public sealed class BSPrim : BSPhysObject | |||
306 | _position = value; | 294 | _position = value; |
307 | PositionSanityCheck(false); | 295 | PositionSanityCheck(false); |
308 | 296 | ||
309 | // A linkset might need to know if a component information changed. | 297 | PhysScene.TaintedObject("BSPrim.setPosition", delegate() |
310 | Linkset.UpdateProperties(this, false); | ||
311 | |||
312 | PhysicsScene.TaintedObject("BSPrim.setPosition", delegate() | ||
313 | { | 298 | { |
314 | DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation); | 299 | DetailLog("{0},BSPrim.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation); |
315 | ForcePosition = _position; | 300 | ForcePosition = _position; |
316 | }); | 301 | }); |
317 | } | 302 | } |
318 | } | 303 | } |
304 | |||
319 | public override OMV.Vector3 ForcePosition { | 305 | public override OMV.Vector3 ForcePosition { |
320 | get { | 306 | get { |
321 | _position = PhysicsScene.PE.GetPosition(PhysBody); | 307 | _position = PhysScene.PE.GetPosition(PhysBody); |
322 | return _position; | 308 | return _position; |
323 | } | 309 | } |
324 | set { | 310 | set { |
325 | _position = value; | 311 | _position = value; |
326 | if (PhysBody.HasPhysicalBody) | 312 | if (PhysBody.HasPhysicalBody) |
327 | { | 313 | { |
328 | PhysicsScene.PE.SetTranslation(PhysBody, _position, _orientation); | 314 | PhysScene.PE.SetTranslation(PhysBody, _position, _orientation); |
329 | ActivateIfPhysical(false); | 315 | ActivateIfPhysical(false); |
330 | } | 316 | } |
331 | } | 317 | } |
@@ -338,7 +324,11 @@ public sealed class BSPrim : BSPhysObject | |||
338 | { | 324 | { |
339 | bool ret = false; | 325 | bool ret = false; |
340 | 326 | ||
341 | if (!PhysicsScene.TerrainManager.IsWithinKnownTerrain(_position)) | 327 | // We don't care where non-physical items are placed |
328 | if (!IsPhysicallyActive) | ||
329 | return ret; | ||
330 | |||
331 | if (!PhysScene.TerrainManager.IsWithinKnownTerrain(RawPosition)) | ||
342 | { | 332 | { |
343 | // The physical object is out of the known/simulated area. | 333 | // The physical object is out of the known/simulated area. |
344 | // Upper levels of code will handle the transition to other areas so, for | 334 | // Upper levels of code will handle the transition to other areas so, for |
@@ -346,39 +336,74 @@ public sealed class BSPrim : BSPhysObject | |||
346 | return ret; | 336 | return ret; |
347 | } | 337 | } |
348 | 338 | ||
349 | float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position); | 339 | float terrainHeight = PhysScene.TerrainManager.GetTerrainHeightAtXYZ(RawPosition); |
350 | OMV.Vector3 upForce = OMV.Vector3.Zero; | 340 | OMV.Vector3 upForce = OMV.Vector3.Zero; |
351 | if (RawPosition.Z < terrainHeight) | 341 | float approxSize = Math.Max(Size.X, Math.Max(Size.Y, Size.Z)); |
342 | if ((RawPosition.Z + approxSize / 2f) < terrainHeight) | ||
352 | { | 343 | { |
353 | DetailLog("{0},BSPrim.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight); | 344 | DetailLog("{0},BSPrim.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, RawPosition, terrainHeight); |
354 | float targetHeight = terrainHeight + (Size.Z / 2f); | 345 | float targetHeight = terrainHeight + (Size.Z / 2f); |
355 | // Upforce proportional to the distance away from the terrain. Correct the error in 1 sec. | 346 | // If the object is below ground it just has to be moved up because pushing will |
356 | upForce.Z = (terrainHeight - RawPosition.Z) * 1f; | 347 | // not get it through the terrain |
348 | _position.Z = targetHeight; | ||
349 | if (inTaintTime) | ||
350 | { | ||
351 | ForcePosition = _position; | ||
352 | } | ||
353 | // If we are throwing the object around, zero its other forces | ||
354 | ZeroMotion(inTaintTime); | ||
357 | ret = true; | 355 | ret = true; |
358 | } | 356 | } |
359 | 357 | ||
360 | if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0) | 358 | if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0) |
361 | { | 359 | { |
362 | float waterHeight = PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(_position); | 360 | float waterHeight = PhysScene.TerrainManager.GetWaterLevelAtXYZ(_position); |
363 | // TODO: a floating motor so object will bob in the water | 361 | // TODO: a floating motor so object will bob in the water |
364 | if (Math.Abs(RawPosition.Z - waterHeight) > 0.1f) | 362 | if (Math.Abs(RawPosition.Z - waterHeight) > 0.1f) |
365 | { | 363 | { |
366 | // Upforce proportional to the distance away from the water. Correct the error in 1 sec. | 364 | // Upforce proportional to the distance away from the water. Correct the error in 1 sec. |
367 | upForce.Z = (waterHeight - RawPosition.Z) * 1f; | 365 | upForce.Z = (waterHeight - RawPosition.Z) * 1f; |
366 | |||
367 | // Apply upforce and overcome gravity. | ||
368 | OMV.Vector3 correctionForce = upForce - PhysScene.DefaultGravity; | ||
369 | DetailLog("{0},BSPrim.PositionSanityCheck,applyForce,pos={1},upForce={2},correctionForce={3}", LocalID, _position, upForce, correctionForce); | ||
370 | AddForce(correctionForce, false, inTaintTime); | ||
368 | ret = true; | 371 | ret = true; |
369 | } | 372 | } |
370 | } | 373 | } |
371 | 374 | ||
372 | // The above code computes a force to apply to correct any out-of-bounds problems. Apply same. | 375 | return ret; |
373 | // TODO: This should be intergrated with a geneal physics action mechanism. | 376 | } |
374 | // TODO: This should be moderated with PID'ness. | 377 | |
375 | if (ret) | 378 | // Occasionally things will fly off and really get lost. |
379 | // Find the wanderers and bring them back. | ||
380 | // Return 'true' if some parameter need some sanity. | ||
381 | private bool ExtremeSanityCheck(bool inTaintTime) | ||
382 | { | ||
383 | bool ret = false; | ||
384 | |||
385 | uint wayOutThere = Constants.RegionSize * Constants.RegionSize; | ||
386 | // There have been instances of objects getting thrown way out of bounds and crashing | ||
387 | // the border crossing code. | ||
388 | if ( _position.X < -Constants.RegionSize || _position.X > wayOutThere | ||
389 | || _position.Y < -Constants.RegionSize || _position.Y > wayOutThere | ||
390 | || _position.Z < -Constants.RegionSize || _position.Z > wayOutThere) | ||
391 | { | ||
392 | _position = new OMV.Vector3(10, 10, 50); | ||
393 | ZeroMotion(inTaintTime); | ||
394 | ret = true; | ||
395 | } | ||
396 | if (RawVelocity.LengthSquared() > BSParam.MaxLinearVelocity) | ||
376 | { | 397 | { |
377 | // Apply upforce and overcome gravity. | 398 | RawVelocity = Util.ClampV(RawVelocity, BSParam.MaxLinearVelocity); |
378 | OMV.Vector3 correctionForce = upForce - PhysicsScene.DefaultGravity; | 399 | ret = true; |
379 | DetailLog("{0},BSPrim.PositionSanityCheck,applyForce,pos={1},upForce={2},correctionForce={3}", LocalID, _position, upForce, correctionForce); | ||
380 | AddForce(correctionForce, false, inTaintTime); | ||
381 | } | 400 | } |
401 | if (_rotationalVelocity.LengthSquared() > BSParam.MaxAngularVelocitySquared) | ||
402 | { | ||
403 | _rotationalVelocity = Util.ClampV(_rotationalVelocity, BSParam.MaxAngularVelocity); | ||
404 | ret = true; | ||
405 | } | ||
406 | |||
382 | return ret; | 407 | return ret; |
383 | } | 408 | } |
384 | 409 | ||
@@ -387,72 +412,69 @@ public sealed class BSPrim : BSPhysObject | |||
387 | // If the simulator cares about the mass of the linkset, it will sum it itself. | 412 | // If the simulator cares about the mass of the linkset, it will sum it itself. |
388 | public override float Mass | 413 | public override float Mass |
389 | { | 414 | { |
390 | get | 415 | get { return _mass; } |
391 | { | 416 | } |
392 | return _mass; | 417 | // TotalMass returns the mass of the large object the prim may be in (overridden by linkset code) |
393 | } | 418 | public virtual float TotalMass |
419 | { | ||
420 | get { return _mass; } | ||
394 | } | 421 | } |
395 | |||
396 | // used when we only want this prim's mass and not the linkset thing | 422 | // used when we only want this prim's mass and not the linkset thing |
397 | public override float RawMass { | 423 | public override float RawMass { |
398 | get { return _mass; } | 424 | get { return _mass; } |
399 | } | 425 | } |
400 | // Set the physical mass to the passed mass. | 426 | // Set the physical mass to the passed mass. |
401 | // Note that this does not change _mass! | 427 | // Note that this does not change _mass! |
402 | public override void UpdatePhysicalMassProperties(float physMass, bool inWorld) | 428 | public override void UpdatePhysicalMassProperties(float physMass, bool inWorld) |
403 | { | 429 | { |
404 | if (PhysBody.HasPhysicalBody) | 430 | if (PhysBody.HasPhysicalBody && PhysShape.HasPhysicalShape) |
405 | { | 431 | { |
406 | if (IsStatic) | 432 | if (IsStatic) |
407 | { | 433 | { |
408 | PhysicsScene.PE.SetGravity(PhysBody, PhysicsScene.DefaultGravity); | 434 | PhysScene.PE.SetGravity(PhysBody, PhysScene.DefaultGravity); |
409 | Inertia = OMV.Vector3.Zero; | 435 | Inertia = OMV.Vector3.Zero; |
410 | PhysicsScene.PE.SetMassProps(PhysBody, 0f, Inertia); | 436 | PhysScene.PE.SetMassProps(PhysBody, 0f, Inertia); |
411 | PhysicsScene.PE.UpdateInertiaTensor(PhysBody); | 437 | PhysScene.PE.UpdateInertiaTensor(PhysBody); |
412 | } | 438 | } |
413 | else | 439 | else |
414 | { | 440 | { |
415 | OMV.Vector3 grav = ComputeGravity(); | ||
416 | |||
417 | if (inWorld) | 441 | if (inWorld) |
418 | { | 442 | { |
419 | // Changing interesting properties doesn't change proxy and collision cache | 443 | // Changing interesting properties doesn't change proxy and collision cache |
420 | // information. The Bullet solution is to re-add the object to the world | 444 | // information. The Bullet solution is to re-add the object to the world |
421 | // after parameters are changed. | 445 | // after parameters are changed. |
422 | PhysicsScene.PE.RemoveObjectFromWorld(PhysicsScene.World, PhysBody); | 446 | PhysScene.PE.RemoveObjectFromWorld(PhysScene.World, PhysBody); |
423 | } | 447 | } |
424 | 448 | ||
425 | // The computation of mass props requires gravity to be set on the object. | 449 | // The computation of mass props requires gravity to be set on the object. |
426 | PhysicsScene.PE.SetGravity(PhysBody, grav); | 450 | Gravity = ComputeGravity(Buoyancy); |
451 | PhysScene.PE.SetGravity(PhysBody, Gravity); | ||
427 | 452 | ||
428 | Inertia = PhysicsScene.PE.CalculateLocalInertia(PhysShape, physMass); | 453 | Inertia = PhysScene.PE.CalculateLocalInertia(PhysShape.physShapeInfo, physMass); |
429 | PhysicsScene.PE.SetMassProps(PhysBody, physMass, Inertia); | 454 | PhysScene.PE.SetMassProps(PhysBody, physMass, Inertia); |
430 | PhysicsScene.PE.UpdateInertiaTensor(PhysBody); | 455 | PhysScene.PE.UpdateInertiaTensor(PhysBody); |
431 | 456 | ||
432 | // center of mass is at the zero of the object | 457 | DetailLog("{0},BSPrim.UpdateMassProperties,mass={1},localInertia={2},grav={3},inWorld={4}", |
433 | // DEBUG DEBUG PhysicsScene.PE.SetCenterOfMassByPosRot(PhysBody, ForcePosition, ForceOrientation); | 458 | LocalID, physMass, Inertia, Gravity, inWorld); |
434 | DetailLog("{0},BSPrim.UpdateMassProperties,mass={1},localInertia={2},grav={3},inWorld={4}", LocalID, physMass, Inertia, grav, inWorld); | ||
435 | 459 | ||
436 | if (inWorld) | 460 | if (inWorld) |
437 | { | 461 | { |
438 | AddObjectToPhysicalWorld(); | 462 | AddObjectToPhysicalWorld(); |
439 | } | 463 | } |
440 | |||
441 | // Must set gravity after it has been added to the world because, for unknown reasons, | ||
442 | // adding the object resets the object's gravity to world gravity | ||
443 | PhysicsScene.PE.SetGravity(PhysBody, grav); | ||
444 | |||
445 | } | 464 | } |
446 | } | 465 | } |
447 | } | 466 | } |
448 | 467 | ||
449 | // Return what gravity should be set to this very moment | 468 | // Return what gravity should be set to this very moment |
450 | private OMV.Vector3 ComputeGravity() | 469 | public OMV.Vector3 ComputeGravity(float buoyancy) |
451 | { | 470 | { |
452 | OMV.Vector3 ret = PhysicsScene.DefaultGravity; | 471 | OMV.Vector3 ret = PhysScene.DefaultGravity; |
453 | 472 | ||
454 | if (!IsStatic) | 473 | if (!IsStatic) |
455 | ret *= (1f - Buoyancy); | 474 | { |
475 | ret *= (1f - buoyancy); | ||
476 | ret *= GravModifier; | ||
477 | } | ||
456 | 478 | ||
457 | return ret; | 479 | return ret; |
458 | } | 480 | } |
@@ -460,93 +482,70 @@ public sealed class BSPrim : BSPhysObject | |||
460 | // Is this used? | 482 | // Is this used? |
461 | public override OMV.Vector3 CenterOfMass | 483 | public override OMV.Vector3 CenterOfMass |
462 | { | 484 | { |
463 | get { return Linkset.CenterOfMass; } | 485 | get { return RawPosition; } |
464 | } | 486 | } |
465 | 487 | ||
466 | // Is this used? | 488 | // Is this used? |
467 | public override OMV.Vector3 GeometricCenter | 489 | public override OMV.Vector3 GeometricCenter |
468 | { | 490 | { |
469 | get { return Linkset.GeometricCenter; } | 491 | get { return RawPosition; } |
470 | } | 492 | } |
471 | 493 | ||
472 | public override OMV.Vector3 Force { | 494 | public override OMV.Vector3 Force { |
473 | get { return _force; } | 495 | get { return RawForce; } |
474 | set { | 496 | set { |
475 | _force = value; | 497 | RawForce = value; |
476 | if (_force != OMV.Vector3.Zero) | 498 | EnableActor(RawForce != OMV.Vector3.Zero, SetForceActorName, delegate() |
477 | { | 499 | { |
478 | // If the force is non-zero, it must be reapplied each tick because | 500 | return new BSActorSetForce(PhysScene, this, SetForceActorName); |
479 | // Bullet clears the forces applied last frame. | 501 | }); |
480 | RegisterPreStepAction("BSPrim.setForce", LocalID, | ||
481 | delegate(float timeStep) | ||
482 | { | ||
483 | DetailLog("{0},BSPrim.setForce,preStep,force={1}", LocalID, _force); | ||
484 | if (PhysBody.HasPhysicalBody) | ||
485 | { | ||
486 | PhysicsScene.PE.ApplyCentralForce(PhysBody, _force); | ||
487 | ActivateIfPhysical(false); | ||
488 | } | ||
489 | } | ||
490 | ); | ||
491 | } | ||
492 | else | ||
493 | { | ||
494 | UnRegisterPreStepAction("BSPrim.setForce", LocalID); | ||
495 | } | ||
496 | } | 502 | } |
497 | } | 503 | } |
498 | 504 | ||
499 | public override int VehicleType { | 505 | public override int VehicleType { |
500 | get { | 506 | get { |
501 | return (int)_vehicle.Type; // if we are a vehicle, return that type | 507 | return (int)VehicleActor.Type; |
502 | } | 508 | } |
503 | set { | 509 | set { |
504 | Vehicle type = (Vehicle)value; | 510 | Vehicle type = (Vehicle)value; |
505 | 511 | ||
506 | PhysicsScene.TaintedObject("setVehicleType", delegate() | 512 | PhysScene.TaintedObject("setVehicleType", delegate() |
507 | { | 513 | { |
508 | // Done at taint time so we're sure the physics engine is not using the variables | 514 | ZeroMotion(true /* inTaintTime */); |
509 | // Vehicle code changes the parameters for this vehicle type. | 515 | VehicleActor.ProcessTypeChange(type); |
510 | _vehicle.ProcessTypeChange(type); | ||
511 | ActivateIfPhysical(false); | 516 | ActivateIfPhysical(false); |
512 | |||
513 | // If an active vehicle, register the vehicle code to be called before each step | ||
514 | if (_vehicle.Type == Vehicle.TYPE_NONE) | ||
515 | UnRegisterPreStepAction("BSPrim.Vehicle", LocalID); | ||
516 | else | ||
517 | RegisterPreStepAction("BSPrim.Vehicle", LocalID, _vehicle.Step); | ||
518 | }); | 517 | }); |
519 | } | 518 | } |
520 | } | 519 | } |
521 | public override void VehicleFloatParam(int param, float value) | 520 | public override void VehicleFloatParam(int param, float value) |
522 | { | 521 | { |
523 | PhysicsScene.TaintedObject("BSPrim.VehicleFloatParam", delegate() | 522 | PhysScene.TaintedObject("BSPrim.VehicleFloatParam", delegate() |
524 | { | 523 | { |
525 | _vehicle.ProcessFloatVehicleParam((Vehicle)param, value); | 524 | VehicleActor.ProcessFloatVehicleParam((Vehicle)param, value); |
526 | ActivateIfPhysical(false); | 525 | ActivateIfPhysical(false); |
527 | }); | 526 | }); |
528 | } | 527 | } |
529 | public override void VehicleVectorParam(int param, OMV.Vector3 value) | 528 | public override void VehicleVectorParam(int param, OMV.Vector3 value) |
530 | { | 529 | { |
531 | PhysicsScene.TaintedObject("BSPrim.VehicleVectorParam", delegate() | 530 | PhysScene.TaintedObject("BSPrim.VehicleVectorParam", delegate() |
532 | { | 531 | { |
533 | _vehicle.ProcessVectorVehicleParam((Vehicle)param, value); | 532 | VehicleActor.ProcessVectorVehicleParam((Vehicle)param, value); |
534 | ActivateIfPhysical(false); | 533 | ActivateIfPhysical(false); |
535 | }); | 534 | }); |
536 | } | 535 | } |
537 | public override void VehicleRotationParam(int param, OMV.Quaternion rotation) | 536 | public override void VehicleRotationParam(int param, OMV.Quaternion rotation) |
538 | { | 537 | { |
539 | PhysicsScene.TaintedObject("BSPrim.VehicleRotationParam", delegate() | 538 | PhysScene.TaintedObject("BSPrim.VehicleRotationParam", delegate() |
540 | { | 539 | { |
541 | _vehicle.ProcessRotationVehicleParam((Vehicle)param, rotation); | 540 | VehicleActor.ProcessRotationVehicleParam((Vehicle)param, rotation); |
542 | ActivateIfPhysical(false); | 541 | ActivateIfPhysical(false); |
543 | }); | 542 | }); |
544 | } | 543 | } |
545 | public override void VehicleFlags(int param, bool remove) | 544 | public override void VehicleFlags(int param, bool remove) |
546 | { | 545 | { |
547 | PhysicsScene.TaintedObject("BSPrim.VehicleFlags", delegate() | 546 | PhysScene.TaintedObject("BSPrim.VehicleFlags", delegate() |
548 | { | 547 | { |
549 | _vehicle.ProcessVehicleFlags(param, remove); | 548 | VehicleActor.ProcessVehicleFlags(param, remove); |
550 | }); | 549 | }); |
551 | } | 550 | } |
552 | 551 | ||
@@ -556,7 +555,7 @@ public sealed class BSPrim : BSPhysObject | |||
556 | if (_isVolumeDetect != newValue) | 555 | if (_isVolumeDetect != newValue) |
557 | { | 556 | { |
558 | _isVolumeDetect = newValue; | 557 | _isVolumeDetect = newValue; |
559 | PhysicsScene.TaintedObject("BSPrim.SetVolumeDetect", delegate() | 558 | PhysScene.TaintedObject("BSPrim.SetVolumeDetect", delegate() |
560 | { | 559 | { |
561 | // DetailLog("{0},setVolumeDetect,taint,volDetect={1}", LocalID, _isVolumeDetect); | 560 | // DetailLog("{0},setVolumeDetect,taint,volDetect={1}", LocalID, _isVolumeDetect); |
562 | SetObjectDynamic(true); | 561 | SetObjectDynamic(true); |
@@ -564,56 +563,110 @@ public sealed class BSPrim : BSPhysObject | |||
564 | } | 563 | } |
565 | return; | 564 | return; |
566 | } | 565 | } |
566 | public override void SetMaterial(int material) | ||
567 | { | ||
568 | base.SetMaterial(material); | ||
569 | PhysScene.TaintedObject("BSPrim.SetMaterial", delegate() | ||
570 | { | ||
571 | UpdatePhysicalParameters(); | ||
572 | }); | ||
573 | } | ||
574 | public override float Friction | ||
575 | { | ||
576 | get { return base.Friction; } | ||
577 | set | ||
578 | { | ||
579 | if (base.Friction != value) | ||
580 | { | ||
581 | base.Friction = value; | ||
582 | PhysScene.TaintedObject("BSPrim.setFriction", delegate() | ||
583 | { | ||
584 | UpdatePhysicalParameters(); | ||
585 | }); | ||
586 | } | ||
587 | } | ||
588 | } | ||
589 | public override float Restitution | ||
590 | { | ||
591 | get { return base.Restitution; } | ||
592 | set | ||
593 | { | ||
594 | if (base.Restitution != value) | ||
595 | { | ||
596 | base.Restitution = value; | ||
597 | PhysScene.TaintedObject("BSPrim.setRestitution", delegate() | ||
598 | { | ||
599 | UpdatePhysicalParameters(); | ||
600 | }); | ||
601 | } | ||
602 | } | ||
603 | } | ||
604 | // The simulator/viewer keep density as 100kg/m3. | ||
605 | // Remember to use BSParam.DensityScaleFactor to create the physical density. | ||
606 | public override float Density | ||
607 | { | ||
608 | get { return base.Density; } | ||
609 | set | ||
610 | { | ||
611 | if (base.Density != value) | ||
612 | { | ||
613 | base.Density = value; | ||
614 | PhysScene.TaintedObject("BSPrim.setDensity", delegate() | ||
615 | { | ||
616 | UpdatePhysicalParameters(); | ||
617 | }); | ||
618 | } | ||
619 | } | ||
620 | } | ||
621 | public override float GravModifier | ||
622 | { | ||
623 | get { return base.GravModifier; } | ||
624 | set | ||
625 | { | ||
626 | if (base.GravModifier != value) | ||
627 | { | ||
628 | base.GravModifier = value; | ||
629 | PhysScene.TaintedObject("BSPrim.setGravityModifier", delegate() | ||
630 | { | ||
631 | UpdatePhysicalParameters(); | ||
632 | }); | ||
633 | } | ||
634 | } | ||
635 | } | ||
567 | public override OMV.Vector3 Velocity { | 636 | public override OMV.Vector3 Velocity { |
568 | get { return _velocity; } | 637 | get { return RawVelocity; } |
569 | set { | 638 | set { |
570 | _velocity = value; | 639 | RawVelocity = value; |
571 | PhysicsScene.TaintedObject("BSPrim.setVelocity", delegate() | 640 | PhysScene.TaintedObject("BSPrim.setVelocity", delegate() |
572 | { | 641 | { |
573 | // DetailLog("{0},BSPrim.SetVelocity,taint,vel={1}", LocalID, _velocity); | 642 | // DetailLog("{0},BSPrim.SetVelocity,taint,vel={1}", LocalID, RawVelocity); |
574 | ForceVelocity = _velocity; | 643 | ForceVelocity = RawVelocity; |
575 | }); | 644 | }); |
576 | } | 645 | } |
577 | } | 646 | } |
578 | public override OMV.Vector3 ForceVelocity { | 647 | public override OMV.Vector3 ForceVelocity { |
579 | get { return _velocity; } | 648 | get { return RawVelocity; } |
580 | set { | 649 | set { |
581 | PhysicsScene.AssertInTaintTime("BSPrim.ForceVelocity"); | 650 | PhysScene.AssertInTaintTime("BSPrim.ForceVelocity"); |
582 | 651 | ||
583 | _velocity = value; | 652 | RawVelocity = Util.ClampV(value, BSParam.MaxLinearVelocity); |
584 | if (PhysBody.HasPhysicalBody) | 653 | if (PhysBody.HasPhysicalBody) |
585 | { | 654 | { |
586 | PhysicsScene.PE.SetLinearVelocity(PhysBody, _velocity); | 655 | DetailLog("{0},BSPrim.ForceVelocity,taint,vel={1}", LocalID, RawVelocity); |
656 | PhysScene.PE.SetLinearVelocity(PhysBody, RawVelocity); | ||
587 | ActivateIfPhysical(false); | 657 | ActivateIfPhysical(false); |
588 | } | 658 | } |
589 | } | 659 | } |
590 | } | 660 | } |
591 | public override OMV.Vector3 Torque { | 661 | public override OMV.Vector3 Torque { |
592 | get { return _torque; } | 662 | get { return RawTorque; } |
593 | set { | 663 | set { |
594 | _torque = value; | 664 | RawTorque = value; |
595 | if (_torque != OMV.Vector3.Zero) | 665 | EnableActor(RawTorque != OMV.Vector3.Zero, SetTorqueActorName, delegate() |
596 | { | 666 | { |
597 | // If the torque is non-zero, it must be reapplied each tick because | 667 | return new BSActorSetTorque(PhysScene, this, SetTorqueActorName); |
598 | // Bullet clears the forces applied last frame. | 668 | }); |
599 | RegisterPreStepAction("BSPrim.setTorque", LocalID, | 669 | DetailLog("{0},BSPrim.SetTorque,call,torque={1}", LocalID, RawTorque); |
600 | delegate(float timeStep) | ||
601 | { | ||
602 | if (PhysBody.HasPhysicalBody) | ||
603 | AddAngularForce(_torque, false, true); | ||
604 | } | ||
605 | ); | ||
606 | } | ||
607 | else | ||
608 | { | ||
609 | UnRegisterPreStepAction("BSPrim.setTorque", LocalID); | ||
610 | } | ||
611 | // DetailLog("{0},BSPrim.SetTorque,call,torque={1}", LocalID, _torque); | ||
612 | } | ||
613 | } | ||
614 | public override float CollisionScore { | ||
615 | get { return _collisionScore; } | ||
616 | set { _collisionScore = value; | ||
617 | } | 670 | } |
618 | } | 671 | } |
619 | public override OMV.Vector3 Acceleration { | 672 | public override OMV.Vector3 Acceleration { |
@@ -627,14 +680,6 @@ public sealed class BSPrim : BSPhysObject | |||
627 | } | 680 | } |
628 | public override OMV.Quaternion Orientation { | 681 | public override OMV.Quaternion Orientation { |
629 | get { | 682 | get { |
630 | /* NOTE: this refetch is not necessary. The simulator knows about linkset children | ||
631 | * and does not fetch this position info for children. Thus this is commented out. | ||
632 | // Children move around because tied to parent. Get a fresh value. | ||
633 | if (!Linkset.IsRoot(this)) | ||
634 | { | ||
635 | _orientation = Linkset.OrientationGet(this); | ||
636 | } | ||
637 | */ | ||
638 | return _orientation; | 683 | return _orientation; |
639 | } | 684 | } |
640 | set { | 685 | set { |
@@ -642,17 +687,9 @@ public sealed class BSPrim : BSPhysObject | |||
642 | return; | 687 | return; |
643 | _orientation = value; | 688 | _orientation = value; |
644 | 689 | ||
645 | // A linkset might need to know if a component information changed. | 690 | PhysScene.TaintedObject("BSPrim.setOrientation", delegate() |
646 | Linkset.UpdateProperties(this, false); | ||
647 | |||
648 | PhysicsScene.TaintedObject("BSPrim.setOrientation", delegate() | ||
649 | { | 691 | { |
650 | if (PhysBody.HasPhysicalBody) | 692 | ForceOrientation = _orientation; |
651 | { | ||
652 | // _position = PhysicsScene.PE.GetObjectPosition(PhysicsScene.World, BSBody); | ||
653 | // DetailLog("{0},BSPrim.setOrientation,taint,pos={1},orient={2}", LocalID, _position, _orientation); | ||
654 | PhysicsScene.PE.SetTranslation(PhysBody, _position, _orientation); | ||
655 | } | ||
656 | }); | 693 | }); |
657 | } | 694 | } |
658 | } | 695 | } |
@@ -661,13 +698,14 @@ public sealed class BSPrim : BSPhysObject | |||
661 | { | 698 | { |
662 | get | 699 | get |
663 | { | 700 | { |
664 | _orientation = PhysicsScene.PE.GetOrientation(PhysBody); | 701 | _orientation = PhysScene.PE.GetOrientation(PhysBody); |
665 | return _orientation; | 702 | return _orientation; |
666 | } | 703 | } |
667 | set | 704 | set |
668 | { | 705 | { |
669 | _orientation = value; | 706 | _orientation = value; |
670 | PhysicsScene.PE.SetTranslation(PhysBody, _position, _orientation); | 707 | if (PhysBody.HasPhysicalBody) |
708 | PhysScene.PE.SetTranslation(PhysBody, _position, _orientation); | ||
671 | } | 709 | } |
672 | } | 710 | } |
673 | public override int PhysicsActorType { | 711 | public override int PhysicsActorType { |
@@ -680,12 +718,13 @@ public sealed class BSPrim : BSPhysObject | |||
680 | if (_isPhysical != value) | 718 | if (_isPhysical != value) |
681 | { | 719 | { |
682 | _isPhysical = value; | 720 | _isPhysical = value; |
683 | PhysicsScene.TaintedObject("BSPrim.setIsPhysical", delegate() | 721 | PhysScene.TaintedObject("BSPrim.setIsPhysical", delegate() |
684 | { | 722 | { |
685 | DetailLog("{0},setIsPhysical,taint,isPhys={1}", LocalID, _isPhysical); | 723 | DetailLog("{0},setIsPhysical,taint,isPhys={1}", LocalID, _isPhysical); |
686 | SetObjectDynamic(true); | 724 | SetObjectDynamic(true); |
687 | // whether phys-to-static or static-to-phys, the object is not moving. | 725 | // whether phys-to-static or static-to-phys, the object is not moving. |
688 | ZeroMotion(true); | 726 | ZeroMotion(true); |
727 | |||
689 | }); | 728 | }); |
690 | } | 729 | } |
691 | } | 730 | } |
@@ -703,6 +742,12 @@ public sealed class BSPrim : BSPhysObject | |||
703 | get { return !IsPhantom && !_isVolumeDetect; } | 742 | get { return !IsPhantom && !_isVolumeDetect; } |
704 | } | 743 | } |
705 | 744 | ||
745 | // The object is moving and is actively being dynamic in the physical world | ||
746 | public override bool IsPhysicallyActive | ||
747 | { | ||
748 | get { return !_isSelected && IsPhysical; } | ||
749 | } | ||
750 | |||
706 | // Make gravity work if the object is physical and not selected | 751 | // Make gravity work if the object is physical and not selected |
707 | // Called at taint-time!! | 752 | // Called at taint-time!! |
708 | private void SetObjectDynamic(bool forceRebuild) | 753 | private void SetObjectDynamic(bool forceRebuild) |
@@ -717,19 +762,24 @@ public sealed class BSPrim : BSPhysObject | |||
717 | // isSolid: other objects bounce off of this object | 762 | // isSolid: other objects bounce off of this object |
718 | // isVolumeDetect: other objects pass through but can generate collisions | 763 | // isVolumeDetect: other objects pass through but can generate collisions |
719 | // collisionEvents: whether this object returns collision events | 764 | // collisionEvents: whether this object returns collision events |
720 | private void UpdatePhysicalParameters() | 765 | public virtual void UpdatePhysicalParameters() |
721 | { | 766 | { |
722 | // DetailLog("{0},BSPrim.UpdatePhysicalParameters,entry,body={1},shape={2}", LocalID, BSBody, BSShape); | 767 | if (!PhysBody.HasPhysicalBody) |
768 | { | ||
769 | // This would only happen if updates are called for during initialization when the body is not set up yet. | ||
770 | // DetailLog("{0},BSPrim.UpdatePhysicalParameters,taint,calledWithNoPhysBody", LocalID); | ||
771 | return; | ||
772 | } | ||
723 | 773 | ||
724 | // Mangling all the physical properties requires the object not be in the physical world. | 774 | // Mangling all the physical properties requires the object not be in the physical world. |
725 | // This is a NOOP if the object is not in the world (BulletSim and Bullet ignore objects not found). | 775 | // This is a NOOP if the object is not in the world (BulletSim and Bullet ignore objects not found). |
726 | PhysicsScene.PE.RemoveObjectFromWorld(PhysicsScene.World, PhysBody); | 776 | PhysScene.PE.RemoveObjectFromWorld(PhysScene.World, PhysBody); |
727 | 777 | ||
728 | // Set up the object physicalness (does gravity and collisions move this object) | 778 | // Set up the object physicalness (does gravity and collisions move this object) |
729 | MakeDynamic(IsStatic); | 779 | MakeDynamic(IsStatic); |
730 | 780 | ||
731 | // Update vehicle specific parameters (after MakeDynamic() so can change physical parameters) | 781 | // Update vehicle specific parameters (after MakeDynamic() so can change physical parameters) |
732 | _vehicle.Refresh(); | 782 | PhysicalActors.Refresh(); |
733 | 783 | ||
734 | // Arrange for collision events if the simulator wants them | 784 | // Arrange for collision events if the simulator wants them |
735 | EnableCollisions(SubscribedEvents()); | 785 | EnableCollisions(SubscribedEvents()); |
@@ -740,16 +790,11 @@ public sealed class BSPrim : BSPhysObject | |||
740 | AddObjectToPhysicalWorld(); | 790 | AddObjectToPhysicalWorld(); |
741 | 791 | ||
742 | // Rebuild its shape | 792 | // Rebuild its shape |
743 | PhysicsScene.PE.UpdateSingleAabb(PhysicsScene.World, PhysBody); | 793 | PhysScene.PE.UpdateSingleAabb(PhysScene.World, PhysBody); |
744 | |||
745 | // Recompute any linkset parameters. | ||
746 | // When going from non-physical to physical, this re-enables the constraints that | ||
747 | // had been automatically disabled when the mass was set to zero. | ||
748 | // For compound based linksets, this enables and disables interactions of the children. | ||
749 | Linkset.Refresh(this); | ||
750 | 794 | ||
751 | DetailLog("{0},BSPrim.UpdatePhysicalParameters,taintExit,static={1},solid={2},mass={3},collide={4},cf={5:X},cType={6},body={7},shape={8}", | 795 | DetailLog("{0},BSPrim.UpdatePhysicalParameters,taintExit,static={1},solid={2},mass={3},collide={4},cf={5:X},cType={6},body={7},shape={8}", |
752 | LocalID, IsStatic, IsSolid, Mass, SubscribedEvents(), CurrentCollisionFlags, PhysBody.collisionType, PhysBody, PhysShape); | 796 | LocalID, IsStatic, IsSolid, Mass, SubscribedEvents(), |
797 | CurrentCollisionFlags, PhysBody.collisionType, PhysBody, PhysShape); | ||
753 | } | 798 | } |
754 | 799 | ||
755 | // "Making dynamic" means changing to and from static. | 800 | // "Making dynamic" means changing to and from static. |
@@ -757,59 +802,55 @@ public sealed class BSPrim : BSPhysObject | |||
757 | // When dynamic, the object can fall and be pushed by others. | 802 | // When dynamic, the object can fall and be pushed by others. |
758 | // This is independent of its 'solidness' which controls what passes through | 803 | // This is independent of its 'solidness' which controls what passes through |
759 | // this object and what interacts with it. | 804 | // this object and what interacts with it. |
760 | private void MakeDynamic(bool makeStatic) | 805 | protected virtual void MakeDynamic(bool makeStatic) |
761 | { | 806 | { |
762 | if (makeStatic) | 807 | if (makeStatic) |
763 | { | 808 | { |
764 | // Become a Bullet 'static' object type | 809 | // Become a Bullet 'static' object type |
765 | CurrentCollisionFlags = PhysicsScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.CF_STATIC_OBJECT); | 810 | CurrentCollisionFlags = PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.CF_STATIC_OBJECT); |
766 | // Stop all movement | 811 | // Stop all movement |
767 | ZeroMotion(true); | 812 | ZeroMotion(true); |
768 | 813 | ||
769 | // Set various physical properties so other object interact properly | 814 | // Set various physical properties so other object interact properly |
770 | MaterialAttributes matAttrib = BSMaterials.GetAttributes(Material, false); | 815 | PhysScene.PE.SetFriction(PhysBody, Friction); |
771 | PhysicsScene.PE.SetFriction(PhysBody, matAttrib.friction); | 816 | PhysScene.PE.SetRestitution(PhysBody, Restitution); |
772 | PhysicsScene.PE.SetRestitution(PhysBody, matAttrib.restitution); | 817 | PhysScene.PE.SetContactProcessingThreshold(PhysBody, BSParam.ContactProcessingThreshold); |
773 | 818 | ||
774 | // Mass is zero which disables a bunch of physics stuff in Bullet | 819 | // Mass is zero which disables a bunch of physics stuff in Bullet |
775 | UpdatePhysicalMassProperties(0f, false); | 820 | UpdatePhysicalMassProperties(0f, false); |
776 | // Set collision detection parameters | 821 | // Set collision detection parameters |
777 | if (BSParam.CcdMotionThreshold > 0f) | 822 | if (BSParam.CcdMotionThreshold > 0f) |
778 | { | 823 | { |
779 | PhysicsScene.PE.SetCcdMotionThreshold(PhysBody, BSParam.CcdMotionThreshold); | 824 | PhysScene.PE.SetCcdMotionThreshold(PhysBody, BSParam.CcdMotionThreshold); |
780 | PhysicsScene.PE.SetCcdSweptSphereRadius(PhysBody, BSParam.CcdSweptSphereRadius); | 825 | PhysScene.PE.SetCcdSweptSphereRadius(PhysBody, BSParam.CcdSweptSphereRadius); |
781 | } | 826 | } |
782 | 827 | ||
783 | // The activation state is 'disabled' so Bullet will not try to act on it. | 828 | // The activation state is 'disabled' so Bullet will not try to act on it. |
784 | // PhysicsScene.PE.ForceActivationState(PhysBody, ActivationState.DISABLE_SIMULATION); | 829 | // PhysicsScene.PE.ForceActivationState(PhysBody, ActivationState.DISABLE_SIMULATION); |
785 | // Start it out sleeping and physical actions could wake it up. | 830 | // Start it out sleeping and physical actions could wake it up. |
786 | PhysicsScene.PE.ForceActivationState(PhysBody, ActivationState.ISLAND_SLEEPING); | 831 | PhysScene.PE.ForceActivationState(PhysBody, ActivationState.ISLAND_SLEEPING); |
787 | 832 | ||
788 | // This collides like a static object | 833 | // This collides like a static object |
789 | PhysBody.collisionType = CollisionType.Static; | 834 | PhysBody.collisionType = CollisionType.Static; |
790 | |||
791 | // There can be special things needed for implementing linksets | ||
792 | Linkset.MakeStatic(this); | ||
793 | } | 835 | } |
794 | else | 836 | else |
795 | { | 837 | { |
796 | // Not a Bullet static object | 838 | // Not a Bullet static object |
797 | CurrentCollisionFlags = PhysicsScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.CF_STATIC_OBJECT); | 839 | CurrentCollisionFlags = PhysScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.CF_STATIC_OBJECT); |
798 | 840 | ||
799 | // Set various physical properties so other object interact properly | 841 | // Set various physical properties so other object interact properly |
800 | MaterialAttributes matAttrib = BSMaterials.GetAttributes(Material, true); | 842 | PhysScene.PE.SetFriction(PhysBody, Friction); |
801 | PhysicsScene.PE.SetFriction(PhysBody, matAttrib.friction); | 843 | PhysScene.PE.SetRestitution(PhysBody, Restitution); |
802 | PhysicsScene.PE.SetRestitution(PhysBody, matAttrib.restitution); | 844 | // DetailLog("{0},BSPrim.MakeDynamic,frict={1},rest={2}", LocalID, Friction, Restitution); |
803 | 845 | ||
804 | // per http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=3382 | 846 | // per http://www.bulletphysics.org/Bullet/phpBB3/viewtopic.php?t=3382 |
805 | // Since this can be called multiple times, only zero forces when becoming physical | 847 | // Since this can be called multiple times, only zero forces when becoming physical |
806 | // PhysicsScene.PE.ClearAllForces(BSBody); | 848 | // PhysicsScene.PE.ClearAllForces(BSBody); |
807 | 849 | ||
808 | // For good measure, make sure the transform is set through to the motion state | 850 | // For good measure, make sure the transform is set through to the motion state |
809 | PhysicsScene.PE.SetTranslation(PhysBody, _position, _orientation); | 851 | ForcePosition = _position; |
810 | 852 | ForceVelocity = RawVelocity; | |
811 | // Center of mass is at the center of the object | 853 | ForceRotationalVelocity = _rotationalVelocity; |
812 | // DEBUG DEBUG PhysicsScene.PE.SetCenterOfMassByPosRot(Linkset.LinksetRoot.PhysBody, _position, _orientation); | ||
813 | 854 | ||
814 | // A dynamic object has mass | 855 | // A dynamic object has mass |
815 | UpdatePhysicalMassProperties(RawMass, false); | 856 | UpdatePhysicalMassProperties(RawMass, false); |
@@ -817,25 +858,22 @@ public sealed class BSPrim : BSPhysObject | |||
817 | // Set collision detection parameters | 858 | // Set collision detection parameters |
818 | if (BSParam.CcdMotionThreshold > 0f) | 859 | if (BSParam.CcdMotionThreshold > 0f) |
819 | { | 860 | { |
820 | PhysicsScene.PE.SetCcdMotionThreshold(PhysBody, BSParam.CcdMotionThreshold); | 861 | PhysScene.PE.SetCcdMotionThreshold(PhysBody, BSParam.CcdMotionThreshold); |
821 | PhysicsScene.PE.SetCcdSweptSphereRadius(PhysBody, BSParam.CcdSweptSphereRadius); | 862 | PhysScene.PE.SetCcdSweptSphereRadius(PhysBody, BSParam.CcdSweptSphereRadius); |
822 | } | 863 | } |
823 | 864 | ||
824 | // Various values for simulation limits | 865 | // Various values for simulation limits |
825 | PhysicsScene.PE.SetDamping(PhysBody, BSParam.LinearDamping, BSParam.AngularDamping); | 866 | PhysScene.PE.SetDamping(PhysBody, BSParam.LinearDamping, BSParam.AngularDamping); |
826 | PhysicsScene.PE.SetDeactivationTime(PhysBody, BSParam.DeactivationTime); | 867 | PhysScene.PE.SetDeactivationTime(PhysBody, BSParam.DeactivationTime); |
827 | PhysicsScene.PE.SetSleepingThresholds(PhysBody, BSParam.LinearSleepingThreshold, BSParam.AngularSleepingThreshold); | 868 | PhysScene.PE.SetSleepingThresholds(PhysBody, BSParam.LinearSleepingThreshold, BSParam.AngularSleepingThreshold); |
828 | PhysicsScene.PE.SetContactProcessingThreshold(PhysBody, BSParam.ContactProcessingThreshold); | 869 | PhysScene.PE.SetContactProcessingThreshold(PhysBody, BSParam.ContactProcessingThreshold); |
829 | 870 | ||
830 | // This collides like an object. | 871 | // This collides like an object. |
831 | PhysBody.collisionType = CollisionType.Dynamic; | 872 | PhysBody.collisionType = CollisionType.Dynamic; |
832 | 873 | ||
833 | // Force activation of the object so Bullet will act on it. | 874 | // Force activation of the object so Bullet will act on it. |
834 | // Must do the ForceActivationState2() to overcome the DISABLE_SIMULATION from static objects. | 875 | // Must do the ForceActivationState2() to overcome the DISABLE_SIMULATION from static objects. |
835 | PhysicsScene.PE.ForceActivationState(PhysBody, ActivationState.ACTIVE_TAG); | 876 | PhysScene.PE.ForceActivationState(PhysBody, ActivationState.ACTIVE_TAG); |
836 | |||
837 | // There might be special things needed for implementing linksets. | ||
838 | Linkset.MakeDynamic(this); | ||
839 | } | 877 | } |
840 | } | 878 | } |
841 | 879 | ||
@@ -845,7 +883,7 @@ public sealed class BSPrim : BSPhysObject | |||
845 | // the functions after this one set up the state of a possibly newly created collision body. | 883 | // the functions after this one set up the state of a possibly newly created collision body. |
846 | private void MakeSolid(bool makeSolid) | 884 | private void MakeSolid(bool makeSolid) |
847 | { | 885 | { |
848 | CollisionObjectTypes bodyType = (CollisionObjectTypes)PhysicsScene.PE.GetBodyType(PhysBody); | 886 | CollisionObjectTypes bodyType = (CollisionObjectTypes)PhysScene.PE.GetBodyType(PhysBody); |
849 | if (makeSolid) | 887 | if (makeSolid) |
850 | { | 888 | { |
851 | // Verify the previous code created the correct shape for this type of thing. | 889 | // Verify the previous code created the correct shape for this type of thing. |
@@ -853,7 +891,7 @@ public sealed class BSPrim : BSPhysObject | |||
853 | { | 891 | { |
854 | m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for solidity. id={1}, type={2}", LogHeader, LocalID, bodyType); | 892 | m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for solidity. id={1}, type={2}", LogHeader, LocalID, bodyType); |
855 | } | 893 | } |
856 | CurrentCollisionFlags = PhysicsScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE); | 894 | CurrentCollisionFlags = PhysScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE); |
857 | } | 895 | } |
858 | else | 896 | else |
859 | { | 897 | { |
@@ -861,32 +899,23 @@ public sealed class BSPrim : BSPhysObject | |||
861 | { | 899 | { |
862 | m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for non-solidness. id={1}, type={2}", LogHeader, LocalID, bodyType); | 900 | m_log.ErrorFormat("{0} MakeSolid: physical body of wrong type for non-solidness. id={1}, type={2}", LogHeader, LocalID, bodyType); |
863 | } | 901 | } |
864 | CurrentCollisionFlags = PhysicsScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE); | 902 | CurrentCollisionFlags = PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.CF_NO_CONTACT_RESPONSE); |
865 | 903 | ||
866 | // Change collision info from a static object to a ghosty collision object | 904 | // Change collision info from a static object to a ghosty collision object |
867 | PhysBody.collisionType = CollisionType.VolumeDetect; | 905 | PhysBody.collisionType = CollisionType.VolumeDetect; |
868 | } | 906 | } |
869 | } | 907 | } |
870 | 908 | ||
871 | // Enable physical actions. Bullet will keep sleeping non-moving physical objects so | ||
872 | // they need waking up when parameters are changed. | ||
873 | // Called in taint-time!! | ||
874 | private void ActivateIfPhysical(bool forceIt) | ||
875 | { | ||
876 | if (IsPhysical && PhysBody.HasPhysicalBody) | ||
877 | PhysicsScene.PE.Activate(PhysBody, forceIt); | ||
878 | } | ||
879 | |||
880 | // Turn on or off the flag controlling whether collision events are returned to the simulator. | 909 | // Turn on or off the flag controlling whether collision events are returned to the simulator. |
881 | private void EnableCollisions(bool wantsCollisionEvents) | 910 | private void EnableCollisions(bool wantsCollisionEvents) |
882 | { | 911 | { |
883 | if (wantsCollisionEvents) | 912 | if (wantsCollisionEvents) |
884 | { | 913 | { |
885 | CurrentCollisionFlags = PhysicsScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); | 914 | CurrentCollisionFlags = PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); |
886 | } | 915 | } |
887 | else | 916 | else |
888 | { | 917 | { |
889 | CurrentCollisionFlags = PhysicsScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); | 918 | CurrentCollisionFlags = PhysScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.BS_SUBSCRIBE_COLLISION_EVENTS); |
890 | } | 919 | } |
891 | } | 920 | } |
892 | 921 | ||
@@ -897,12 +926,12 @@ public sealed class BSPrim : BSPhysObject | |||
897 | { | 926 | { |
898 | if (PhysBody.HasPhysicalBody) | 927 | if (PhysBody.HasPhysicalBody) |
899 | { | 928 | { |
900 | PhysicsScene.PE.AddObjectToWorld(PhysicsScene.World, PhysBody); | 929 | PhysScene.PE.AddObjectToWorld(PhysScene.World, PhysBody); |
901 | } | 930 | } |
902 | else | 931 | else |
903 | { | 932 | { |
904 | m_log.ErrorFormat("{0} Attempt to add physical object without body. id={1}", LogHeader, LocalID); | 933 | m_log.ErrorFormat("{0} Attempt to add physical object without body. id={1}", LogHeader, LocalID); |
905 | DetailLog("{0},BSPrim.UpdatePhysicalParameters,addObjectWithoutBody,cType={1}", LocalID, PhysBody.collisionType); | 934 | DetailLog("{0},BSPrim.AddObjectToPhysicalWorld,addObjectWithoutBody,cType={1}", LocalID, PhysBody.collisionType); |
906 | } | 935 | } |
907 | } | 936 | } |
908 | 937 | ||
@@ -932,12 +961,12 @@ public sealed class BSPrim : BSPhysObject | |||
932 | public override bool FloatOnWater { | 961 | public override bool FloatOnWater { |
933 | set { | 962 | set { |
934 | _floatOnWater = value; | 963 | _floatOnWater = value; |
935 | PhysicsScene.TaintedObject("BSPrim.setFloatOnWater", delegate() | 964 | PhysScene.TaintedObject("BSPrim.setFloatOnWater", delegate() |
936 | { | 965 | { |
937 | if (_floatOnWater) | 966 | if (_floatOnWater) |
938 | CurrentCollisionFlags = PhysicsScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER); | 967 | CurrentCollisionFlags = PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER); |
939 | else | 968 | else |
940 | CurrentCollisionFlags = PhysicsScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER); | 969 | CurrentCollisionFlags = PhysScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER); |
941 | }); | 970 | }); |
942 | } | 971 | } |
943 | } | 972 | } |
@@ -947,10 +976,10 @@ public sealed class BSPrim : BSPhysObject | |||
947 | } | 976 | } |
948 | set { | 977 | set { |
949 | _rotationalVelocity = value; | 978 | _rotationalVelocity = value; |
979 | Util.ClampV(_rotationalVelocity, BSParam.MaxAngularVelocity); | ||
950 | // m_log.DebugFormat("{0}: RotationalVelocity={1}", LogHeader, _rotationalVelocity); | 980 | // m_log.DebugFormat("{0}: RotationalVelocity={1}", LogHeader, _rotationalVelocity); |
951 | PhysicsScene.TaintedObject("BSPrim.setRotationalVelocity", delegate() | 981 | PhysScene.TaintedObject("BSPrim.setRotationalVelocity", delegate() |
952 | { | 982 | { |
953 | DetailLog("{0},BSPrim.SetRotationalVel,taint,rotvel={1}", LocalID, _rotationalVelocity); | ||
954 | ForceRotationalVelocity = _rotationalVelocity; | 983 | ForceRotationalVelocity = _rotationalVelocity; |
955 | }); | 984 | }); |
956 | } | 985 | } |
@@ -960,10 +989,12 @@ public sealed class BSPrim : BSPhysObject | |||
960 | return _rotationalVelocity; | 989 | return _rotationalVelocity; |
961 | } | 990 | } |
962 | set { | 991 | set { |
963 | _rotationalVelocity = value; | 992 | _rotationalVelocity = Util.ClampV(value, BSParam.MaxAngularVelocity); |
964 | if (PhysBody.HasPhysicalBody) | 993 | if (PhysBody.HasPhysicalBody) |
965 | { | 994 | { |
966 | PhysicsScene.PE.SetAngularVelocity(PhysBody, _rotationalVelocity); | 995 | DetailLog("{0},BSPrim.ForceRotationalVel,taint,rotvel={1}", LocalID, _rotationalVelocity); |
996 | PhysScene.PE.SetAngularVelocity(PhysBody, _rotationalVelocity); | ||
997 | // PhysicsScene.PE.SetInterpolationAngularVelocity(PhysBody, _rotationalVelocity); | ||
967 | ActivateIfPhysical(false); | 998 | ActivateIfPhysical(false); |
968 | } | 999 | } |
969 | } | 1000 | } |
@@ -978,7 +1009,7 @@ public sealed class BSPrim : BSPhysObject | |||
978 | get { return _buoyancy; } | 1009 | get { return _buoyancy; } |
979 | set { | 1010 | set { |
980 | _buoyancy = value; | 1011 | _buoyancy = value; |
981 | PhysicsScene.TaintedObject("BSPrim.setBuoyancy", delegate() | 1012 | PhysScene.TaintedObject("BSPrim.setBuoyancy", delegate() |
982 | { | 1013 | { |
983 | ForceBuoyancy = _buoyancy; | 1014 | ForceBuoyancy = _buoyancy; |
984 | }); | 1015 | }); |
@@ -990,96 +1021,113 @@ public sealed class BSPrim : BSPhysObject | |||
990 | _buoyancy = value; | 1021 | _buoyancy = value; |
991 | // DetailLog("{0},BSPrim.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy); | 1022 | // DetailLog("{0},BSPrim.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy); |
992 | // Force the recalculation of the various inertia,etc variables in the object | 1023 | // Force the recalculation of the various inertia,etc variables in the object |
993 | DetailLog("{0},BSPrim.ForceBuoyancy,buoy={1},mass={2}", LocalID, _buoyancy, _mass); | 1024 | UpdatePhysicalMassProperties(RawMass, true); |
994 | UpdatePhysicalMassProperties(_mass, true); | 1025 | DetailLog("{0},BSPrim.ForceBuoyancy,buoy={1},mass={2},grav={3}", LocalID, _buoyancy, RawMass, Gravity); |
995 | ActivateIfPhysical(false); | 1026 | ActivateIfPhysical(false); |
996 | } | 1027 | } |
997 | } | 1028 | } |
998 | 1029 | ||
999 | // Used for MoveTo | ||
1000 | public override OMV.Vector3 PIDTarget { | ||
1001 | set { _PIDTarget = value; } | ||
1002 | } | ||
1003 | public override float PIDTau { | ||
1004 | set { _PIDTau = value; } | ||
1005 | } | ||
1006 | public override bool PIDActive { | 1030 | public override bool PIDActive { |
1007 | set { _usePID = value; } | 1031 | set { |
1032 | base.MoveToTargetActive = value; | ||
1033 | EnableActor(MoveToTargetActive, MoveToTargetActorName, delegate() | ||
1034 | { | ||
1035 | return new BSActorMoveToTarget(PhysScene, this, MoveToTargetActorName); | ||
1036 | }); | ||
1037 | } | ||
1008 | } | 1038 | } |
1009 | 1039 | ||
1010 | // Used for llSetHoverHeight and maybe vehicle height | 1040 | // Used for llSetHoverHeight and maybe vehicle height |
1011 | // Hover Height will override MoveTo target's Z | 1041 | // Hover Height will override MoveTo target's Z |
1012 | public override bool PIDHoverActive { | 1042 | public override bool PIDHoverActive { |
1013 | set { _useHoverPID = value; } | 1043 | set { |
1014 | } | 1044 | base.HoverActive = value; |
1015 | public override float PIDHoverHeight { | 1045 | EnableActor(HoverActive, HoverActorName, delegate() |
1016 | set { _PIDHoverHeight = value; } | 1046 | { |
1017 | } | 1047 | return new BSActorHover(PhysScene, this, HoverActorName); |
1018 | public override PIDHoverType PIDHoverType { | 1048 | }); |
1019 | set { _PIDHoverType = value; } | 1049 | } |
1020 | } | ||
1021 | public override float PIDHoverTau { | ||
1022 | set { _PIDHoverTao = value; } | ||
1023 | } | 1050 | } |
1024 | 1051 | ||
1025 | // For RotLookAt | ||
1026 | public override OMV.Quaternion APIDTarget { set { return; } } | ||
1027 | public override bool APIDActive { set { return; } } | ||
1028 | public override float APIDStrength { set { return; } } | ||
1029 | public override float APIDDamping { set { return; } } | ||
1030 | |||
1031 | public override void AddForce(OMV.Vector3 force, bool pushforce) { | 1052 | public override void AddForce(OMV.Vector3 force, bool pushforce) { |
1053 | // Per documentation, max force is limited. | ||
1054 | OMV.Vector3 addForce = Util.ClampV(force, BSParam.MaxAddForceMagnitude); | ||
1055 | |||
1032 | // Since this force is being applied in only one step, make this a force per second. | 1056 | // Since this force is being applied in only one step, make this a force per second. |
1033 | OMV.Vector3 addForce = force / PhysicsScene.LastTimeStep; | 1057 | addForce /= PhysScene.LastTimeStep; |
1034 | AddForce(addForce, pushforce, false); | 1058 | AddForce(addForce, pushforce, false /* inTaintTime */); |
1035 | } | 1059 | } |
1060 | |||
1036 | // Applying a force just adds this to the total force on the object. | 1061 | // Applying a force just adds this to the total force on the object. |
1037 | // This added force will only last the next simulation tick. | 1062 | // This added force will only last the next simulation tick. |
1038 | public void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) { | 1063 | public void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) { |
1039 | // for an object, doesn't matter if force is a pushforce or not | 1064 | // for an object, doesn't matter if force is a pushforce or not |
1040 | if (force.IsFinite()) | 1065 | if (IsPhysicallyActive) |
1041 | { | 1066 | { |
1042 | float magnitude = force.Length(); | 1067 | if (force.IsFinite()) |
1043 | if (magnitude > BSParam.MaxAddForceMagnitude) | ||
1044 | { | 1068 | { |
1045 | // Force has a limit | 1069 | // DetailLog("{0},BSPrim.addForce,call,force={1}", LocalID, addForce); |
1046 | force = force / magnitude * BSParam.MaxAddForceMagnitude; | ||
1047 | } | ||
1048 | |||
1049 | OMV.Vector3 addForce = force; | ||
1050 | DetailLog("{0},BSPrim.addForce,call,force={1}", LocalID, addForce); | ||
1051 | 1070 | ||
1052 | PhysicsScene.TaintedObject(inTaintTime, "BSPrim.AddForce", delegate() | 1071 | OMV.Vector3 addForce = force; |
1053 | { | 1072 | PhysScene.TaintedObject(inTaintTime, "BSPrim.AddForce", delegate() |
1054 | // Bullet adds this central force to the total force for this tick | ||
1055 | DetailLog("{0},BSPrim.addForce,taint,force={1}", LocalID, addForce); | ||
1056 | if (PhysBody.HasPhysicalBody) | ||
1057 | { | 1073 | { |
1058 | PhysicsScene.PE.ApplyCentralForce(PhysBody, addForce); | 1074 | // Bullet adds this central force to the total force for this tick |
1059 | ActivateIfPhysical(false); | 1075 | DetailLog("{0},BSPrim.addForce,taint,force={1}", LocalID, addForce); |
1060 | } | 1076 | if (PhysBody.HasPhysicalBody) |
1061 | }); | 1077 | { |
1078 | PhysScene.PE.ApplyCentralForce(PhysBody, addForce); | ||
1079 | ActivateIfPhysical(false); | ||
1080 | } | ||
1081 | }); | ||
1082 | } | ||
1083 | else | ||
1084 | { | ||
1085 | m_log.WarnFormat("{0}: AddForce: Got a NaN force applied to a prim. LocalID={1}", LogHeader, LocalID); | ||
1086 | return; | ||
1087 | } | ||
1062 | } | 1088 | } |
1063 | else | 1089 | } |
1090 | |||
1091 | public void AddForceImpulse(OMV.Vector3 impulse, bool pushforce, bool inTaintTime) { | ||
1092 | // for an object, doesn't matter if force is a pushforce or not | ||
1093 | if (!IsPhysicallyActive) | ||
1064 | { | 1094 | { |
1065 | m_log.WarnFormat("{0}: Got a NaN force applied to a prim. LocalID={1}", LogHeader, LocalID); | 1095 | if (impulse.IsFinite()) |
1066 | return; | 1096 | { |
1097 | OMV.Vector3 addImpulse = Util.ClampV(impulse, BSParam.MaxAddForceMagnitude); | ||
1098 | // DetailLog("{0},BSPrim.addForceImpulse,call,impulse={1}", LocalID, impulse); | ||
1099 | |||
1100 | PhysScene.TaintedObject(inTaintTime, "BSPrim.AddImpulse", delegate() | ||
1101 | { | ||
1102 | // Bullet adds this impulse immediately to the velocity | ||
1103 | DetailLog("{0},BSPrim.addForceImpulse,taint,impulseforce={1}", LocalID, addImpulse); | ||
1104 | if (PhysBody.HasPhysicalBody) | ||
1105 | { | ||
1106 | PhysScene.PE.ApplyCentralImpulse(PhysBody, addImpulse); | ||
1107 | ActivateIfPhysical(false); | ||
1108 | } | ||
1109 | }); | ||
1110 | } | ||
1111 | else | ||
1112 | { | ||
1113 | m_log.WarnFormat("{0}: AddForceImpulse: Got a NaN impulse applied to a prim. LocalID={1}", LogHeader, LocalID); | ||
1114 | return; | ||
1115 | } | ||
1067 | } | 1116 | } |
1068 | } | 1117 | } |
1069 | 1118 | ||
1070 | public override void AddAngularForce(OMV.Vector3 force, bool pushforce) { | 1119 | // BSPhysObject.AddAngularForce() |
1071 | AddAngularForce(force, pushforce, false); | 1120 | public override void AddAngularForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) |
1072 | } | ||
1073 | public void AddAngularForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) | ||
1074 | { | 1121 | { |
1075 | if (force.IsFinite()) | 1122 | if (force.IsFinite()) |
1076 | { | 1123 | { |
1077 | OMV.Vector3 angForce = force; | 1124 | OMV.Vector3 angForce = force; |
1078 | PhysicsScene.TaintedObject(inTaintTime, "BSPrim.AddAngularForce", delegate() | 1125 | PhysScene.TaintedObject(inTaintTime, "BSPrim.AddAngularForce", delegate() |
1079 | { | 1126 | { |
1080 | if (PhysBody.HasPhysicalBody) | 1127 | if (PhysBody.HasPhysicalBody) |
1081 | { | 1128 | { |
1082 | PhysicsScene.PE.ApplyTorque(PhysBody, angForce); | 1129 | DetailLog("{0},BSPrim.AddAngularForce,taint,angForce={1}", LocalID, angForce); |
1130 | PhysScene.PE.ApplyTorque(PhysBody, angForce); | ||
1083 | ActivateIfPhysical(false); | 1131 | ActivateIfPhysical(false); |
1084 | } | 1132 | } |
1085 | }); | 1133 | }); |
@@ -1098,11 +1146,11 @@ public sealed class BSPrim : BSPhysObject | |||
1098 | public void ApplyTorqueImpulse(OMV.Vector3 impulse, bool inTaintTime) | 1146 | public void ApplyTorqueImpulse(OMV.Vector3 impulse, bool inTaintTime) |
1099 | { | 1147 | { |
1100 | OMV.Vector3 applyImpulse = impulse; | 1148 | OMV.Vector3 applyImpulse = impulse; |
1101 | PhysicsScene.TaintedObject(inTaintTime, "BSPrim.ApplyTorqueImpulse", delegate() | 1149 | PhysScene.TaintedObject(inTaintTime, "BSPrim.ApplyTorqueImpulse", delegate() |
1102 | { | 1150 | { |
1103 | if (PhysBody.HasPhysicalBody) | 1151 | if (PhysBody.HasPhysicalBody) |
1104 | { | 1152 | { |
1105 | PhysicsScene.PE.ApplyTorqueImpulse(PhysBody, applyImpulse); | 1153 | PhysScene.PE.ApplyTorqueImpulse(PhysBody, applyImpulse); |
1106 | ActivateIfPhysical(false); | 1154 | ActivateIfPhysical(false); |
1107 | } | 1155 | } |
1108 | }); | 1156 | }); |
@@ -1387,19 +1435,10 @@ public sealed class BSPrim : BSPhysObject | |||
1387 | profileEnd = 1.0f - (float)BaseShape.ProfileEnd * 2.0e-5f; | 1435 | profileEnd = 1.0f - (float)BaseShape.ProfileEnd * 2.0e-5f; |
1388 | volume *= (profileEnd - profileBegin); | 1436 | volume *= (profileEnd - profileBegin); |
1389 | 1437 | ||
1390 | returnMass = _density * volume; | 1438 | returnMass = Density * BSParam.DensityScaleFactor * volume; |
1391 | |||
1392 | /* Comment out code that computes the mass of the linkset. That is done in the Linkset class. | ||
1393 | if (IsRootOfLinkset) | ||
1394 | { | ||
1395 | foreach (BSPrim prim in _childrenPrims) | ||
1396 | { | ||
1397 | returnMass += prim.CalculateMass(); | ||
1398 | } | ||
1399 | } | ||
1400 | */ | ||
1401 | 1439 | ||
1402 | returnMass = Util.Clamp(returnMass, BSParam.MinimumObjectMass, BSParam.MaximumObjectMass); | 1440 | returnMass = Util.Clamp(returnMass, BSParam.MinimumObjectMass, BSParam.MaximumObjectMass); |
1441 | // DetailLog("{0},BSPrim.CalculateMass,den={1},vol={2},mass={3}", LocalID, Density, volume, returnMass); | ||
1403 | 1442 | ||
1404 | return returnMass; | 1443 | return returnMass; |
1405 | }// end CalculateMass | 1444 | }// end CalculateMass |
@@ -1410,104 +1449,68 @@ public sealed class BSPrim : BSPhysObject | |||
1410 | // Called at taint-time!!! | 1449 | // Called at taint-time!!! |
1411 | public void CreateGeomAndObject(bool forceRebuild) | 1450 | public void CreateGeomAndObject(bool forceRebuild) |
1412 | { | 1451 | { |
1413 | // If this prim is part of a linkset, we must remove and restore the physical | ||
1414 | // links if the body is rebuilt. | ||
1415 | bool needToRestoreLinkset = false; | ||
1416 | bool needToRestoreVehicle = false; | ||
1417 | |||
1418 | // Create the correct physical representation for this type of object. | 1452 | // Create the correct physical representation for this type of object. |
1419 | // Updates PhysBody and PhysShape with the new information. | 1453 | // Updates base.PhysBody and base.PhysShape with the new information. |
1420 | // Ignore 'forceRebuild'. This routine makes the right choices and changes of necessary. | 1454 | // Ignore 'forceRebuild'. 'GetBodyAndShape' makes the right choices and changes of necessary. |
1421 | PhysicsScene.Shapes.GetBodyAndShape(false, PhysicsScene.World, this, null, delegate(BulletBody dBody) | 1455 | PhysScene.Shapes.GetBodyAndShape(false /*forceRebuild */, PhysScene.World, this, delegate(BulletBody pBody, BulletShape pShape) |
1422 | { | 1456 | { |
1423 | // Called if the current prim body is about to be destroyed. | 1457 | // Called if the current prim body is about to be destroyed. |
1424 | // Remove all the physical dependencies on the old body. | 1458 | // Remove all the physical dependencies on the old body. |
1425 | // (Maybe someday make the changing of BSShape an event to be subscribed to by BSLinkset, ...) | 1459 | // (Maybe someday make the changing of BSShape an event to be subscribed to by BSLinkset, ...) |
1426 | needToRestoreLinkset = Linkset.RemoveBodyDependencies(this); | 1460 | // Note: this virtual function is overloaded by BSPrimLinkable to remove linkset constraints. |
1427 | needToRestoreVehicle = _vehicle.RemoveBodyDependencies(this); | 1461 | RemoveDependencies(); |
1428 | }); | 1462 | }); |
1429 | 1463 | ||
1430 | if (needToRestoreLinkset) | ||
1431 | { | ||
1432 | // If physical body dependencies were removed, restore them | ||
1433 | Linkset.RestoreBodyDependencies(this); | ||
1434 | } | ||
1435 | if (needToRestoreVehicle) | ||
1436 | { | ||
1437 | // If physical body dependencies were removed, restore them | ||
1438 | _vehicle.RestoreBodyDependencies(this); | ||
1439 | } | ||
1440 | |||
1441 | // Make sure the properties are set on the new object | 1464 | // Make sure the properties are set on the new object |
1442 | UpdatePhysicalParameters(); | 1465 | UpdatePhysicalParameters(); |
1443 | return; | 1466 | return; |
1444 | } | 1467 | } |
1445 | 1468 | ||
1446 | // The physics engine says that properties have updated. Update same and inform | 1469 | // Called at taint-time |
1447 | // the world that things have changed. | 1470 | protected virtual void RemoveDependencies() |
1448 | // TODO: do we really need to check for changed? Maybe just copy values and call RequestPhysicsterseUpdate() | 1471 | { |
1449 | enum UpdatedProperties { | 1472 | PhysicalActors.RemoveDependencies(); |
1450 | Position = 1 << 0, | ||
1451 | Rotation = 1 << 1, | ||
1452 | Velocity = 1 << 2, | ||
1453 | Acceleration = 1 << 3, | ||
1454 | RotationalVel = 1 << 4 | ||
1455 | } | 1473 | } |
1456 | 1474 | ||
1457 | const float ROTATION_TOLERANCE = 0.01f; | 1475 | // The physics engine says that properties have updated. Update same and inform |
1458 | const float VELOCITY_TOLERANCE = 0.001f; | 1476 | // the world that things have changed. |
1459 | const float POSITION_TOLERANCE = 0.05f; | ||
1460 | const float ACCELERATION_TOLERANCE = 0.01f; | ||
1461 | const float ROTATIONAL_VELOCITY_TOLERANCE = 0.01f; | ||
1462 | |||
1463 | public override void UpdateProperties(EntityProperties entprop) | 1477 | public override void UpdateProperties(EntityProperties entprop) |
1464 | { | 1478 | { |
1465 | // Updates only for individual prims and for the root object of a linkset. | 1479 | // Let anyone (like the actors) modify the updated properties before they are pushed into the object and the simulator. |
1466 | if (Linkset.IsRoot(this)) | 1480 | TriggerPreUpdatePropertyAction(ref entprop); |
1467 | { | ||
1468 | // A temporary kludge to suppress the rotational effects introduced on vehicles by Bullet | ||
1469 | // TODO: handle physics introduced by Bullet with computed vehicle physics. | ||
1470 | if (_vehicle.IsActive) | ||
1471 | { | ||
1472 | entprop.RotationalVelocity = OMV.Vector3.Zero; | ||
1473 | } | ||
1474 | 1481 | ||
1475 | // Assign directly to the local variables so the normal set action does not happen | 1482 | // DetailLog("{0},BSPrim.UpdateProperties,entry,entprop={1}", LocalID, entprop); // DEBUG DEBUG |
1476 | _position = entprop.Position; | ||
1477 | _orientation = entprop.Rotation; | ||
1478 | _velocity = entprop.Velocity; | ||
1479 | _acceleration = entprop.Acceleration; | ||
1480 | _rotationalVelocity = entprop.RotationalVelocity; | ||
1481 | 1483 | ||
1482 | // The sanity check can change the velocity and/or position. | 1484 | // Assign directly to the local variables so the normal set actions do not happen |
1483 | if (IsPhysical && PositionSanityCheck(true)) | 1485 | _position = entprop.Position; |
1484 | { | 1486 | _orientation = entprop.Rotation; |
1485 | entprop.Position = _position; | 1487 | // DEBUG DEBUG DEBUG -- smooth velocity changes a bit. The simulator seems to be |
1486 | entprop.Velocity = _velocity; | 1488 | // very sensitive to velocity changes. |
1487 | } | 1489 | if (entprop.Velocity == OMV.Vector3.Zero || !entprop.Velocity.ApproxEquals(RawVelocity, BSParam.UpdateVelocityChangeThreshold)) |
1490 | RawVelocity = entprop.Velocity; | ||
1491 | _acceleration = entprop.Acceleration; | ||
1492 | _rotationalVelocity = entprop.RotationalVelocity; | ||
1488 | 1493 | ||
1489 | OMV.Vector3 direction = OMV.Vector3.UnitX * _orientation; // DEBUG DEBUG DEBUG | 1494 | // DetailLog("{0},BSPrim.UpdateProperties,afterAssign,entprop={1}", LocalID, entprop); // DEBUG DEBUG |
1490 | DetailLog("{0},BSPrim.UpdateProperties,call,pos={1},orient={2},dir={3},vel={4},rotVel={5}", | ||
1491 | LocalID, _position, _orientation, direction, _velocity, _rotationalVelocity); | ||
1492 | 1495 | ||
1493 | // remember the current and last set values | 1496 | // The sanity check can change the velocity and/or position. |
1494 | LastEntityProperties = CurrentEntityProperties; | 1497 | if (PositionSanityCheck(true /* inTaintTime */ )) |
1495 | CurrentEntityProperties = entprop; | ||
1496 | |||
1497 | base.RequestPhysicsterseUpdate(); | ||
1498 | } | ||
1499 | /* | ||
1500 | else | ||
1501 | { | 1498 | { |
1502 | // For debugging, report the movement of children | 1499 | entprop.Position = _position; |
1503 | DetailLog("{0},BSPrim.UpdateProperties,child,pos={1},orient={2},vel={3},accel={4},rotVel={5}", | 1500 | entprop.Velocity = RawVelocity; |
1504 | LocalID, entprop.Position, entprop.Rotation, entprop.Velocity, | 1501 | entprop.RotationalVelocity = _rotationalVelocity; |
1505 | entprop.Acceleration, entprop.RotationalVelocity); | 1502 | entprop.Acceleration = _acceleration; |
1506 | } | 1503 | } |
1507 | */ | ||
1508 | 1504 | ||
1509 | // The linkset implimentation might want to know about this. | 1505 | OMV.Vector3 direction = OMV.Vector3.UnitX * _orientation; // DEBUG DEBUG DEBUG |
1510 | Linkset.UpdateProperties(this, true); | 1506 | DetailLog("{0},BSPrim.UpdateProperties,call,entProp={1},dir={2}", LocalID, entprop, direction); |
1507 | |||
1508 | // remember the current and last set values | ||
1509 | LastEntityProperties = CurrentEntityProperties; | ||
1510 | CurrentEntityProperties = entprop; | ||
1511 | |||
1512 | // Note that BSPrim can be overloaded by BSPrimLinkable which controls updates from root and children prims. | ||
1513 | base.RequestPhysicsterseUpdate(); | ||
1511 | } | 1514 | } |
1512 | } | 1515 | } |
1513 | } | 1516 | } |