diff options
Diffstat (limited to '')
-rw-r--r-- | OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs | 554 |
1 files changed, 392 insertions, 162 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs index 4c195e1..e208d3a 100644 --- a/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs +++ b/OpenSim/Region/Physics/BulletSPlugin/BSCharacter.cs | |||
@@ -45,7 +45,6 @@ public sealed class BSCharacter : BSPhysObject | |||
45 | private bool _selected; | 45 | private bool _selected; |
46 | private OMV.Vector3 _position; | 46 | private OMV.Vector3 _position; |
47 | private float _mass; | 47 | private float _mass; |
48 | private float _avatarDensity; | ||
49 | private float _avatarVolume; | 48 | private float _avatarVolume; |
50 | private OMV.Vector3 _force; | 49 | private OMV.Vector3 _force; |
51 | private OMV.Vector3 _velocity; | 50 | private OMV.Vector3 _velocity; |
@@ -58,16 +57,12 @@ public sealed class BSCharacter : BSPhysObject | |||
58 | private bool _flying; | 57 | private bool _flying; |
59 | private bool _setAlwaysRun; | 58 | private bool _setAlwaysRun; |
60 | private bool _throttleUpdates; | 59 | private bool _throttleUpdates; |
61 | private bool _isColliding; | ||
62 | private bool _collidingObj; | ||
63 | private bool _floatOnWater; | 60 | private bool _floatOnWater; |
64 | private OMV.Vector3 _rotationalVelocity; | 61 | private OMV.Vector3 _rotationalVelocity; |
65 | private bool _kinematic; | 62 | private bool _kinematic; |
66 | private float _buoyancy; | 63 | private float _buoyancy; |
67 | 64 | ||
68 | // The friction and velocity of the avatar is modified depending on whether walking or not. | 65 | private BSVMotor _velocityMotor; |
69 | private OMV.Vector3 _appliedVelocity; // the last velocity applied to the avatar | ||
70 | private float _currentFriction; // the friction currently being used (changed by setVelocity). | ||
71 | 66 | ||
72 | private OMV.Vector3 _PIDTarget; | 67 | private OMV.Vector3 _PIDTarget; |
73 | private bool _usePID; | 68 | private bool _usePID; |
@@ -83,34 +78,36 @@ public sealed class BSCharacter : BSPhysObject | |||
83 | _physicsActorType = (int)ActorTypes.Agent; | 78 | _physicsActorType = (int)ActorTypes.Agent; |
84 | _position = pos; | 79 | _position = pos; |
85 | 80 | ||
86 | // Old versions of ScenePresence passed only the height. If width and/or depth are zero, | ||
87 | // replace with the default values. | ||
88 | _size = size; | ||
89 | if (_size.X == 0f) _size.X = PhysicsScene.Params.avatarCapsuleDepth; | ||
90 | if (_size.Y == 0f) _size.Y = PhysicsScene.Params.avatarCapsuleWidth; | ||
91 | |||
92 | _flying = isFlying; | 81 | _flying = isFlying; |
93 | _orientation = OMV.Quaternion.Identity; | 82 | _orientation = OMV.Quaternion.Identity; |
94 | _velocity = OMV.Vector3.Zero; | 83 | _velocity = OMV.Vector3.Zero; |
95 | _appliedVelocity = OMV.Vector3.Zero; | ||
96 | _buoyancy = ComputeBuoyancyFromFlying(isFlying); | 84 | _buoyancy = ComputeBuoyancyFromFlying(isFlying); |
97 | _currentFriction = PhysicsScene.Params.avatarStandingFriction; | 85 | Friction = BSParam.AvatarStandingFriction; |
98 | _avatarDensity = PhysicsScene.Params.avatarDensity; | 86 | Density = BSParam.AvatarDensity / BSParam.DensityScaleFactor; |
87 | |||
88 | // Old versions of ScenePresence passed only the height. If width and/or depth are zero, | ||
89 | // replace with the default values. | ||
90 | _size = size; | ||
91 | if (_size.X == 0f) _size.X = BSParam.AvatarCapsuleDepth; | ||
92 | if (_size.Y == 0f) _size.Y = BSParam.AvatarCapsuleWidth; | ||
99 | 93 | ||
100 | // The dimensions of the avatar capsule are kept in the scale. | 94 | // The dimensions of the physical capsule are kept in the scale. |
101 | // Physics creates a unit capsule which is scaled by the physics engine. | 95 | // Physics creates a unit capsule which is scaled by the physics engine. |
102 | ComputeAvatarScale(_size); | 96 | Scale = ComputeAvatarScale(_size); |
103 | // set _avatarVolume and _mass based on capsule size, _density and Scale | 97 | // set _avatarVolume and _mass based on capsule size, _density and Scale |
104 | ComputeAvatarVolumeAndMass(); | 98 | ComputeAvatarVolumeAndMass(); |
99 | |||
100 | SetupMovementMotor(); | ||
101 | |||
105 | DetailLog("{0},BSCharacter.create,call,size={1},scale={2},density={3},volume={4},mass={5}", | 102 | DetailLog("{0},BSCharacter.create,call,size={1},scale={2},density={3},volume={4},mass={5}", |
106 | LocalID, _size, Scale, _avatarDensity, _avatarVolume, RawMass); | 103 | LocalID, _size, Scale, Density, _avatarVolume, RawMass); |
107 | 104 | ||
108 | // do actual create at taint time | 105 | // do actual creation in taint time |
109 | PhysicsScene.TaintedObject("BSCharacter.create", delegate() | 106 | PhysicsScene.TaintedObject("BSCharacter.create", delegate() |
110 | { | 107 | { |
111 | DetailLog("{0},BSCharacter.create,taint", LocalID); | 108 | DetailLog("{0},BSCharacter.create,taint", LocalID); |
112 | // New body and shape into PhysBody and PhysShape | 109 | // New body and shape into PhysBody and PhysShape |
113 | PhysicsScene.Shapes.GetBodyAndShape(true, PhysicsScene.World, this, null, null); | 110 | PhysicsScene.Shapes.GetBodyAndShape(true, PhysicsScene.World, this); |
114 | 111 | ||
115 | SetPhysicalProperties(); | 112 | SetPhysicalProperties(); |
116 | }); | 113 | }); |
@@ -120,54 +117,216 @@ public sealed class BSCharacter : BSPhysObject | |||
120 | // called when this character is being destroyed and the resources should be released | 117 | // called when this character is being destroyed and the resources should be released |
121 | public override void Destroy() | 118 | public override void Destroy() |
122 | { | 119 | { |
120 | base.Destroy(); | ||
121 | |||
123 | DetailLog("{0},BSCharacter.Destroy", LocalID); | 122 | DetailLog("{0},BSCharacter.Destroy", LocalID); |
124 | PhysicsScene.TaintedObject("BSCharacter.destroy", delegate() | 123 | PhysicsScene.TaintedObject("BSCharacter.destroy", delegate() |
125 | { | 124 | { |
126 | PhysicsScene.Shapes.DereferenceBody(PhysBody, true, null); | 125 | PhysicsScene.Shapes.DereferenceBody(PhysBody, null /* bodyCallback */); |
127 | PhysicsScene.Shapes.DereferenceShape(PhysShape, true, null); | 126 | PhysBody.Clear(); |
127 | PhysicsScene.Shapes.DereferenceShape(PhysShape, null /* bodyCallback */); | ||
128 | PhysShape.Clear(); | ||
128 | }); | 129 | }); |
129 | } | 130 | } |
130 | 131 | ||
131 | private void SetPhysicalProperties() | 132 | private void SetPhysicalProperties() |
132 | { | 133 | { |
133 | BulletSimAPI.RemoveObjectFromWorld2(PhysicsScene.World.ptr, PhysBody.ptr); | 134 | PhysicsScene.PE.RemoveObjectFromWorld(PhysicsScene.World, PhysBody); |
134 | 135 | ||
135 | ZeroMotion(true); | 136 | ZeroMotion(true); |
136 | ForcePosition = _position; | 137 | ForcePosition = _position; |
137 | // Set the velocity and compute the proper friction | 138 | |
139 | // Set the velocity | ||
140 | _velocityMotor.Reset(); | ||
141 | _velocityMotor.SetTarget(_velocity); | ||
142 | _velocityMotor.SetCurrent(_velocity); | ||
138 | ForceVelocity = _velocity; | 143 | ForceVelocity = _velocity; |
139 | 144 | ||
140 | // This will enable or disable the flying buoyancy of the avatar. | 145 | // This will enable or disable the flying buoyancy of the avatar. |
141 | // Needs to be reset especially when an avatar is recreated after crossing a region boundry. | 146 | // Needs to be reset especially when an avatar is recreated after crossing a region boundry. |
142 | Flying = _flying; | 147 | Flying = _flying; |
143 | 148 | ||
144 | BulletSimAPI.SetRestitution2(PhysBody.ptr, PhysicsScene.Params.avatarRestitution); | 149 | PhysicsScene.PE.SetRestitution(PhysBody, BSParam.AvatarRestitution); |
145 | BulletSimAPI.SetMargin2(PhysShape.ptr, PhysicsScene.Params.collisionMargin); | 150 | PhysicsScene.PE.SetMargin(PhysShape, PhysicsScene.Params.collisionMargin); |
146 | BulletSimAPI.SetLocalScaling2(PhysShape.ptr, Scale); | 151 | PhysicsScene.PE.SetLocalScaling(PhysShape, Scale); |
147 | BulletSimAPI.SetContactProcessingThreshold2(PhysBody.ptr, PhysicsScene.Params.contactProcessingThreshold); | 152 | PhysicsScene.PE.SetContactProcessingThreshold(PhysBody, BSParam.ContactProcessingThreshold); |
148 | if (PhysicsScene.Params.ccdMotionThreshold > 0f) | 153 | if (BSParam.CcdMotionThreshold > 0f) |
149 | { | 154 | { |
150 | BulletSimAPI.SetCcdMotionThreshold2(PhysBody.ptr, PhysicsScene.Params.ccdMotionThreshold); | 155 | PhysicsScene.PE.SetCcdMotionThreshold(PhysBody, BSParam.CcdMotionThreshold); |
151 | BulletSimAPI.SetCcdSweptSphereRadius2(PhysBody.ptr, PhysicsScene.Params.ccdSweptSphereRadius); | 156 | PhysicsScene.PE.SetCcdSweptSphereRadius(PhysBody, BSParam.CcdSweptSphereRadius); |
152 | } | 157 | } |
153 | 158 | ||
154 | UpdatePhysicalMassProperties(RawMass); | 159 | UpdatePhysicalMassProperties(RawMass, false); |
155 | 160 | ||
156 | // Make so capsule does not fall over | 161 | // Make so capsule does not fall over |
157 | BulletSimAPI.SetAngularFactorV2(PhysBody.ptr, OMV.Vector3.Zero); | 162 | PhysicsScene.PE.SetAngularFactorV(PhysBody, OMV.Vector3.Zero); |
158 | 163 | ||
159 | BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.CF_CHARACTER_OBJECT); | 164 | PhysicsScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.CF_CHARACTER_OBJECT); |
160 | 165 | ||
161 | BulletSimAPI.AddObjectToWorld2(PhysicsScene.World.ptr, PhysBody.ptr); | 166 | PhysicsScene.PE.AddObjectToWorld(PhysicsScene.World, PhysBody); |
162 | 167 | ||
163 | // BulletSimAPI.ForceActivationState2(BSBody.ptr, ActivationState.ACTIVE_TAG); | 168 | // PhysicsScene.PE.ForceActivationState(PhysBody, ActivationState.ACTIVE_TAG); |
164 | BulletSimAPI.ForceActivationState2(PhysBody.ptr, ActivationState.DISABLE_DEACTIVATION); | 169 | PhysicsScene.PE.ForceActivationState(PhysBody, ActivationState.DISABLE_DEACTIVATION); |
165 | BulletSimAPI.UpdateSingleAabb2(PhysicsScene.World.ptr, PhysBody.ptr); | 170 | PhysicsScene.PE.UpdateSingleAabb(PhysicsScene.World, PhysBody); |
166 | 171 | ||
167 | // Do this after the object has been added to the world | 172 | // Do this after the object has been added to the world |
168 | BulletSimAPI.SetCollisionFilterMask2(PhysBody.ptr, | 173 | PhysBody.collisionType = CollisionType.Avatar; |
169 | (uint)CollisionFilterGroups.AvatarFilter, | 174 | PhysBody.ApplyCollisionMask(PhysicsScene); |
170 | (uint)CollisionFilterGroups.AvatarMask); | 175 | } |
176 | |||
177 | // The avatar's movement is controlled by this motor that speeds up and slows down | ||
178 | // the avatar seeking to reach the motor's target speed. | ||
179 | // This motor runs as a prestep action for the avatar so it will keep the avatar | ||
180 | // standing as well as moving. Destruction of the avatar will destroy the pre-step action. | ||
181 | private void SetupMovementMotor() | ||
182 | { | ||
183 | // Infinite decay and timescale values so motor only changes current to target values. | ||
184 | _velocityMotor = new BSVMotor("BSCharacter.Velocity", | ||
185 | 0.2f, // time scale | ||
186 | BSMotor.Infinite, // decay time scale | ||
187 | BSMotor.InfiniteVector, // friction timescale | ||
188 | 1f // efficiency | ||
189 | ); | ||
190 | // _velocityMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG so motor will output detail log messages. | ||
191 | |||
192 | RegisterPreStepAction("BSCharactor.Movement", LocalID, delegate(float timeStep) | ||
193 | { | ||
194 | // TODO: Decide if the step parameters should be changed depending on the avatar's | ||
195 | // state (flying, colliding, ...). There is code in ODE to do this. | ||
196 | |||
197 | // COMMENTARY: when the user is making the avatar walk, except for falling, the velocity | ||
198 | // specified for the avatar is the one that should be used. For falling, if the avatar | ||
199 | // is not flying and is not colliding then it is presumed to be falling and the Z | ||
200 | // component is not fooled with (thus allowing gravity to do its thing). | ||
201 | // When the avatar is standing, though, the user has specified a velocity of zero and | ||
202 | // the avatar should be standing. But if the avatar is pushed by something in the world | ||
203 | // (raising elevator platform, moving vehicle, ...) the avatar should be allowed to | ||
204 | // move. Thus, the velocity cannot be forced to zero. The problem is that small velocity | ||
205 | // errors can creap in and the avatar will slowly float off in some direction. | ||
206 | // So, the problem is that, when an avatar is standing, we cannot tell creaping error | ||
207 | // from real pushing. | ||
208 | // The code below uses whether the collider is static or moving to decide whether to zero motion. | ||
209 | |||
210 | _velocityMotor.Step(timeStep); | ||
211 | |||
212 | // If we're not supposed to be moving, make sure things are zero. | ||
213 | if (_velocityMotor.ErrorIsZero() && _velocityMotor.TargetValue == OMV.Vector3.Zero) | ||
214 | { | ||
215 | // The avatar shouldn't be moving | ||
216 | _velocityMotor.Zero(); | ||
217 | |||
218 | if (IsColliding) | ||
219 | { | ||
220 | // If we are colliding with a stationary object, presume we're standing and don't move around | ||
221 | if (!ColliderIsMoving) | ||
222 | { | ||
223 | DetailLog("{0},BSCharacter.MoveMotor,collidingWithStationary,zeroingMotion", LocalID); | ||
224 | ZeroMotion(true /* inTaintTime */); | ||
225 | } | ||
226 | |||
227 | // Standing has more friction on the ground | ||
228 | if (Friction != BSParam.AvatarStandingFriction) | ||
229 | { | ||
230 | Friction = BSParam.AvatarStandingFriction; | ||
231 | PhysicsScene.PE.SetFriction(PhysBody, Friction); | ||
232 | } | ||
233 | } | ||
234 | else | ||
235 | { | ||
236 | if (Flying) | ||
237 | { | ||
238 | // Flying and not collising and velocity nearly zero. | ||
239 | ZeroMotion(true /* inTaintTime */); | ||
240 | } | ||
241 | } | ||
242 | |||
243 | DetailLog("{0},BSCharacter.MoveMotor,taint,stopping,target={1},colliding={2}", LocalID, _velocityMotor.TargetValue, IsColliding); | ||
244 | } | ||
245 | else | ||
246 | { | ||
247 | // Supposed to be moving. | ||
248 | OMV.Vector3 stepVelocity = _velocityMotor.CurrentValue; | ||
249 | |||
250 | if (Friction != BSParam.AvatarFriction) | ||
251 | { | ||
252 | // Probably starting up walking. Set friction to moving friction. | ||
253 | Friction = BSParam.AvatarFriction; | ||
254 | PhysicsScene.PE.SetFriction(PhysBody, Friction); | ||
255 | } | ||
256 | |||
257 | // If falling, we keep the world's downward vector no matter what the other axis specify. | ||
258 | // The check for _velocity.Z < 0 makes jumping work (temporary upward force). | ||
259 | if (!Flying && !IsColliding) | ||
260 | { | ||
261 | if (_velocity.Z < 0) | ||
262 | stepVelocity.Z = _velocity.Z; | ||
263 | // DetailLog("{0},BSCharacter.MoveMotor,taint,overrideStepZWithWorldZ,stepVel={1}", LocalID, stepVelocity); | ||
264 | } | ||
265 | |||
266 | // 'stepVelocity' is now the speed we'd like the avatar to move in. Turn that into an instantanous force. | ||
267 | OMV.Vector3 moveForce = (stepVelocity - _velocity) * Mass; | ||
268 | |||
269 | // Should we check for move force being small and forcing velocity to zero? | ||
270 | |||
271 | // Add special movement force to allow avatars to walk up stepped surfaces. | ||
272 | moveForce += WalkUpStairs(); | ||
273 | |||
274 | DetailLog("{0},BSCharacter.MoveMotor,move,stepVel={1},vel={2},mass={3},moveForce={4}", LocalID, stepVelocity, _velocity, Mass, moveForce); | ||
275 | PhysicsScene.PE.ApplyCentralImpulse(PhysBody, moveForce); | ||
276 | } | ||
277 | }); | ||
278 | } | ||
279 | |||
280 | // Decide if the character is colliding with a low object and compute a force to pop the | ||
281 | // avatar up so it can walk up and over the low objects. | ||
282 | private OMV.Vector3 WalkUpStairs() | ||
283 | { | ||
284 | OMV.Vector3 ret = OMV.Vector3.Zero; | ||
285 | |||
286 | // This test is done if moving forward, not flying and is colliding with something. | ||
287 | // DetailLog("{0},BSCharacter.WalkUpStairs,IsColliding={1},flying={2},targSpeed={3},collisions={4}", | ||
288 | // LocalID, IsColliding, Flying, TargetSpeed, CollisionsLastTick.Count); | ||
289 | if (IsColliding && !Flying && TargetVelocitySpeed > 0.1f /* && ForwardSpeed < 0.1f */) | ||
290 | { | ||
291 | // The range near the character's feet where we will consider stairs | ||
292 | float nearFeetHeightMin = RawPosition.Z - (Size.Z / 2f) + 0.05f; | ||
293 | float nearFeetHeightMax = nearFeetHeightMin + BSParam.AvatarStepHeight; | ||
294 | |||
295 | // Look for a collision point that is near the character's feet and is oriented the same as the charactor is | ||
296 | foreach (KeyValuePair<uint, ContactPoint> kvp in CollisionsLastTick.m_objCollisionList) | ||
297 | { | ||
298 | // Don't care about collisions with the terrain | ||
299 | if (kvp.Key > PhysicsScene.TerrainManager.HighestTerrainID) | ||
300 | { | ||
301 | OMV.Vector3 touchPosition = kvp.Value.Position; | ||
302 | // DetailLog("{0},BSCharacter.WalkUpStairs,min={1},max={2},touch={3}", | ||
303 | // LocalID, nearFeetHeightMin, nearFeetHeightMax, touchPosition); | ||
304 | if (touchPosition.Z >= nearFeetHeightMin && touchPosition.Z <= nearFeetHeightMax) | ||
305 | { | ||
306 | // This contact is within the 'near the feet' range. | ||
307 | // The normal should be our contact point to the object so it is pointing away | ||
308 | // thus the difference between our facing orientation and the normal should be small. | ||
309 | OMV.Vector3 directionFacing = OMV.Vector3.UnitX * RawOrientation; | ||
310 | OMV.Vector3 touchNormal = OMV.Vector3.Normalize(kvp.Value.SurfaceNormal); | ||
311 | float diff = Math.Abs(OMV.Vector3.Distance(directionFacing, touchNormal)); | ||
312 | if (diff < BSParam.AvatarStepApproachFactor) | ||
313 | { | ||
314 | // Found the stairs contact point. Push up a little to raise the character. | ||
315 | float upForce = (touchPosition.Z - nearFeetHeightMin) * Mass * BSParam.AvatarStepForceFactor; | ||
316 | ret = new OMV.Vector3(0f, 0f, upForce); | ||
317 | |||
318 | // Also move the avatar up for the new height | ||
319 | OMV.Vector3 displacement = new OMV.Vector3(0f, 0f, BSParam.AvatarStepHeight / 2f); | ||
320 | ForcePosition = RawPosition + displacement; | ||
321 | } | ||
322 | DetailLog("{0},BSCharacter.WalkUpStairs,touchPos={1},nearFeetMin={2},faceDir={3},norm={4},diff={5},ret={6}", | ||
323 | LocalID, touchPosition, nearFeetHeightMin, directionFacing, touchNormal, diff, ret); | ||
324 | } | ||
325 | } | ||
326 | } | ||
327 | } | ||
328 | |||
329 | return ret; | ||
171 | } | 330 | } |
172 | 331 | ||
173 | public override void RequestPhysicsterseUpdate() | 332 | public override void RequestPhysicsterseUpdate() |
@@ -185,24 +344,31 @@ public sealed class BSCharacter : BSPhysObject | |||
185 | } | 344 | } |
186 | 345 | ||
187 | set { | 346 | set { |
188 | // When an avatar's size is set, only the height is changed. | ||
189 | _size = value; | 347 | _size = value; |
190 | ComputeAvatarScale(_size); | 348 | // Old versions of ScenePresence passed only the height. If width and/or depth are zero, |
349 | // replace with the default values. | ||
350 | if (_size.X == 0f) _size.X = BSParam.AvatarCapsuleDepth; | ||
351 | if (_size.Y == 0f) _size.Y = BSParam.AvatarCapsuleWidth; | ||
352 | |||
353 | Scale = ComputeAvatarScale(_size); | ||
191 | ComputeAvatarVolumeAndMass(); | 354 | ComputeAvatarVolumeAndMass(); |
192 | DetailLog("{0},BSCharacter.setSize,call,size={1},scale={2},density={3},volume={4},mass={5}", | 355 | DetailLog("{0},BSCharacter.setSize,call,size={1},scale={2},density={3},volume={4},mass={5}", |
193 | LocalID, _size, Scale, _avatarDensity, _avatarVolume, RawMass); | 356 | LocalID, _size, Scale, Density, _avatarVolume, RawMass); |
194 | 357 | ||
195 | PhysicsScene.TaintedObject("BSCharacter.setSize", delegate() | 358 | PhysicsScene.TaintedObject("BSCharacter.setSize", delegate() |
196 | { | 359 | { |
197 | BulletSimAPI.SetLocalScaling2(PhysShape.ptr, Scale); | 360 | if (PhysBody.HasPhysicalBody && PhysShape.HasPhysicalShape) |
198 | UpdatePhysicalMassProperties(RawMass); | 361 | { |
362 | PhysicsScene.PE.SetLocalScaling(PhysShape, Scale); | ||
363 | UpdatePhysicalMassProperties(RawMass, true); | ||
364 | // Make sure this change appears as a property update event | ||
365 | PhysicsScene.PE.PushUpdate(PhysBody); | ||
366 | } | ||
199 | }); | 367 | }); |
200 | 368 | ||
201 | } | 369 | } |
202 | } | 370 | } |
203 | 371 | ||
204 | public override OMV.Vector3 Scale { get; set; } | ||
205 | |||
206 | public override PrimitiveBaseShape Shape | 372 | public override PrimitiveBaseShape Shape |
207 | { | 373 | { |
208 | set { BaseShape = value; } | 374 | set { BaseShape = value; } |
@@ -219,6 +385,10 @@ public sealed class BSCharacter : BSPhysObject | |||
219 | public override bool Selected { | 385 | public override bool Selected { |
220 | set { _selected = value; } | 386 | set { _selected = value; } |
221 | } | 387 | } |
388 | public override bool IsSelected | ||
389 | { | ||
390 | get { return _selected; } | ||
391 | } | ||
222 | public override void CrossingFailure() { return; } | 392 | public override void CrossingFailure() { return; } |
223 | public override void link(PhysicsActor obj) { return; } | 393 | public override void link(PhysicsActor obj) { return; } |
224 | public override void delink() { return; } | 394 | public override void delink() { return; } |
@@ -236,7 +406,8 @@ public sealed class BSCharacter : BSPhysObject | |||
236 | // Zero some other properties directly into the physics engine | 406 | // Zero some other properties directly into the physics engine |
237 | PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate() | 407 | PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate() |
238 | { | 408 | { |
239 | BulletSimAPI.ClearAllForces2(PhysBody.ptr); | 409 | if (PhysBody.HasPhysicalBody) |
410 | PhysicsScene.PE.ClearAllForces(PhysBody); | ||
240 | }); | 411 | }); |
241 | } | 412 | } |
242 | public override void ZeroAngularMotion(bool inTaintTime) | 413 | public override void ZeroAngularMotion(bool inTaintTime) |
@@ -245,10 +416,13 @@ public sealed class BSCharacter : BSPhysObject | |||
245 | 416 | ||
246 | PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate() | 417 | PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.ZeroMotion", delegate() |
247 | { | 418 | { |
248 | BulletSimAPI.SetInterpolationAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero); | 419 | if (PhysBody.HasPhysicalBody) |
249 | BulletSimAPI.SetAngularVelocity2(PhysBody.ptr, OMV.Vector3.Zero); | 420 | { |
250 | // The next also get rid of applied linear force but the linear velocity is untouched. | 421 | PhysicsScene.PE.SetInterpolationAngularVelocity(PhysBody, OMV.Vector3.Zero); |
251 | BulletSimAPI.ClearForces2(PhysBody.ptr); | 422 | PhysicsScene.PE.SetAngularVelocity(PhysBody, OMV.Vector3.Zero); |
423 | // The next also get rid of applied linear force but the linear velocity is untouched. | ||
424 | PhysicsScene.PE.ClearForces(PhysBody); | ||
425 | } | ||
252 | }); | 426 | }); |
253 | } | 427 | } |
254 | 428 | ||
@@ -263,29 +437,31 @@ public sealed class BSCharacter : BSPhysObject | |||
263 | public override OMV.Vector3 Position { | 437 | public override OMV.Vector3 Position { |
264 | get { | 438 | get { |
265 | // Don't refetch the position because this function is called a zillion times | 439 | // Don't refetch the position because this function is called a zillion times |
266 | // _position = BulletSimAPI.GetObjectPosition2(Scene.World.ptr, LocalID); | 440 | // _position = PhysicsScene.PE.GetObjectPosition(Scene.World, LocalID); |
267 | return _position; | 441 | return _position; |
268 | } | 442 | } |
269 | set { | 443 | set { |
270 | _position = value; | 444 | _position = value; |
271 | PositionSanityCheck(); | ||
272 | 445 | ||
273 | PhysicsScene.TaintedObject("BSCharacter.setPosition", delegate() | 446 | PhysicsScene.TaintedObject("BSCharacter.setPosition", delegate() |
274 | { | 447 | { |
275 | DetailLog("{0},BSCharacter.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation); | 448 | DetailLog("{0},BSCharacter.SetPosition,taint,pos={1},orient={2}", LocalID, _position, _orientation); |
276 | BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); | 449 | PositionSanityCheck(); |
450 | ForcePosition = _position; | ||
277 | }); | 451 | }); |
278 | } | 452 | } |
279 | } | 453 | } |
280 | public override OMV.Vector3 ForcePosition { | 454 | public override OMV.Vector3 ForcePosition { |
281 | get { | 455 | get { |
282 | _position = BulletSimAPI.GetPosition2(PhysBody.ptr); | 456 | _position = PhysicsScene.PE.GetPosition(PhysBody); |
283 | return _position; | 457 | return _position; |
284 | } | 458 | } |
285 | set { | 459 | set { |
286 | _position = value; | 460 | _position = value; |
287 | PositionSanityCheck(); | 461 | if (PhysBody.HasPhysicalBody) |
288 | BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); | 462 | { |
463 | PhysicsScene.PE.SetTranslation(PhysBody, _position, _orientation); | ||
464 | } | ||
289 | } | 465 | } |
290 | } | 466 | } |
291 | 467 | ||
@@ -297,17 +473,28 @@ public sealed class BSCharacter : BSPhysObject | |||
297 | { | 473 | { |
298 | bool ret = false; | 474 | bool ret = false; |
299 | 475 | ||
476 | // TODO: check for out of bounds | ||
477 | if (!PhysicsScene.TerrainManager.IsWithinKnownTerrain(RawPosition)) | ||
478 | { | ||
479 | // The character is out of the known/simulated area. | ||
480 | // Force the avatar position to be within known. ScenePresence will use the position | ||
481 | // plus the velocity to decide if the avatar is moving out of the region. | ||
482 | RawPosition = PhysicsScene.TerrainManager.ClampPositionIntoKnownTerrain(RawPosition); | ||
483 | DetailLog("{0},BSCharacter.PositionSanityCheck,notWithinKnownTerrain,clampedPos={1}", LocalID, RawPosition); | ||
484 | return true; | ||
485 | } | ||
486 | |||
300 | // If below the ground, move the avatar up | 487 | // If below the ground, move the avatar up |
301 | float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(_position); | 488 | float terrainHeight = PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(RawPosition); |
302 | if (Position.Z < terrainHeight) | 489 | if (Position.Z < terrainHeight) |
303 | { | 490 | { |
304 | DetailLog("{0},BSCharacter.PositionAdjustUnderGround,call,pos={1},terrain={2}", LocalID, _position, terrainHeight); | 491 | DetailLog("{0},BSCharacter.PositionSanityCheck,adjustForUnderGround,pos={1},terrain={2}", LocalID, _position, terrainHeight); |
305 | _position.Z = terrainHeight + 2.0f; | 492 | _position.Z = terrainHeight + BSParam.AvatarBelowGroundUpCorrectionMeters; |
306 | ret = true; | 493 | ret = true; |
307 | } | 494 | } |
308 | if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0) | 495 | if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0) |
309 | { | 496 | { |
310 | float waterHeight = PhysicsScene.GetWaterLevelAtXYZ(_position); | 497 | float waterHeight = PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(_position); |
311 | if (Position.Z < waterHeight) | 498 | if (Position.Z < waterHeight) |
312 | { | 499 | { |
313 | _position.Z = waterHeight; | 500 | _position.Z = waterHeight; |
@@ -315,7 +502,6 @@ public sealed class BSCharacter : BSPhysObject | |||
315 | } | 502 | } |
316 | } | 503 | } |
317 | 504 | ||
318 | // TODO: check for out of bounds | ||
319 | return ret; | 505 | return ret; |
320 | } | 506 | } |
321 | 507 | ||
@@ -332,7 +518,7 @@ public sealed class BSCharacter : BSPhysObject | |||
332 | PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.PositionSanityCheck", delegate() | 518 | PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.PositionSanityCheck", delegate() |
333 | { | 519 | { |
334 | DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation); | 520 | DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, _position, _orientation); |
335 | BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); | 521 | ForcePosition = _position; |
336 | }); | 522 | }); |
337 | ret = true; | 523 | ret = true; |
338 | } | 524 | } |
@@ -345,10 +531,10 @@ public sealed class BSCharacter : BSPhysObject | |||
345 | public override float RawMass { | 531 | public override float RawMass { |
346 | get {return _mass; } | 532 | get {return _mass; } |
347 | } | 533 | } |
348 | public override void UpdatePhysicalMassProperties(float physMass) | 534 | public override void UpdatePhysicalMassProperties(float physMass, bool inWorld) |
349 | { | 535 | { |
350 | OMV.Vector3 localInertia = BulletSimAPI.CalculateLocalInertia2(PhysShape.ptr, physMass); | 536 | OMV.Vector3 localInertia = PhysicsScene.PE.CalculateLocalInertia(PhysShape, physMass); |
351 | BulletSimAPI.SetMassProps2(PhysBody.ptr, physMass, localInertia); | 537 | PhysicsScene.PE.SetMassProps(PhysBody, physMass, localInertia); |
352 | } | 538 | } |
353 | 539 | ||
354 | public override OMV.Vector3 Force { | 540 | public override OMV.Vector3 Force { |
@@ -359,7 +545,8 @@ public sealed class BSCharacter : BSPhysObject | |||
359 | PhysicsScene.TaintedObject("BSCharacter.SetForce", delegate() | 545 | PhysicsScene.TaintedObject("BSCharacter.SetForce", delegate() |
360 | { | 546 | { |
361 | DetailLog("{0},BSCharacter.setForce,taint,force={1}", LocalID, _force); | 547 | DetailLog("{0},BSCharacter.setForce,taint,force={1}", LocalID, _force); |
362 | BulletSimAPI.SetObjectForce2(PhysBody.ptr, _force); | 548 | if (PhysBody.HasPhysicalBody) |
549 | PhysicsScene.PE.SetObjectForce(PhysBody, _force); | ||
363 | }); | 550 | }); |
364 | } | 551 | } |
365 | } | 552 | } |
@@ -376,6 +563,37 @@ public sealed class BSCharacter : BSPhysObject | |||
376 | 563 | ||
377 | public override OMV.Vector3 GeometricCenter { get { return OMV.Vector3.Zero; } } | 564 | public override OMV.Vector3 GeometricCenter { get { return OMV.Vector3.Zero; } } |
378 | public override OMV.Vector3 CenterOfMass { get { return OMV.Vector3.Zero; } } | 565 | public override OMV.Vector3 CenterOfMass { get { return OMV.Vector3.Zero; } } |
566 | |||
567 | // Sets the target in the motor. This starts the changing of the avatar's velocity. | ||
568 | public override OMV.Vector3 TargetVelocity | ||
569 | { | ||
570 | get | ||
571 | { | ||
572 | return m_targetVelocity; | ||
573 | } | ||
574 | set | ||
575 | { | ||
576 | DetailLog("{0},BSCharacter.setTargetVelocity,call,vel={1}", LocalID, value); | ||
577 | m_targetVelocity = value; | ||
578 | OMV.Vector3 targetVel = value; | ||
579 | if (_setAlwaysRun) | ||
580 | targetVel *= new OMV.Vector3(BSParam.AvatarAlwaysRunFactor, BSParam.AvatarAlwaysRunFactor, 0f); | ||
581 | |||
582 | PhysicsScene.TaintedObject("BSCharacter.setTargetVelocity", delegate() | ||
583 | { | ||
584 | _velocityMotor.Reset(); | ||
585 | _velocityMotor.SetTarget(targetVel); | ||
586 | _velocityMotor.SetCurrent(_velocity); | ||
587 | _velocityMotor.Enabled = true; | ||
588 | }); | ||
589 | } | ||
590 | } | ||
591 | public override OMV.Vector3 RawVelocity | ||
592 | { | ||
593 | get { return _velocity; } | ||
594 | set { _velocity = value; } | ||
595 | } | ||
596 | // Directly setting velocity means this is what the user really wants now. | ||
379 | public override OMV.Vector3 Velocity { | 597 | public override OMV.Vector3 Velocity { |
380 | get { return _velocity; } | 598 | get { return _velocity; } |
381 | set { | 599 | set { |
@@ -383,6 +601,11 @@ public sealed class BSCharacter : BSPhysObject | |||
383 | // m_log.DebugFormat("{0}: set velocity = {1}", LogHeader, _velocity); | 601 | // m_log.DebugFormat("{0}: set velocity = {1}", LogHeader, _velocity); |
384 | PhysicsScene.TaintedObject("BSCharacter.setVelocity", delegate() | 602 | PhysicsScene.TaintedObject("BSCharacter.setVelocity", delegate() |
385 | { | 603 | { |
604 | _velocityMotor.Reset(); | ||
605 | _velocityMotor.SetCurrent(_velocity); | ||
606 | _velocityMotor.SetTarget(_velocity); | ||
607 | _velocityMotor.Enabled = false; | ||
608 | |||
386 | DetailLog("{0},BSCharacter.setVelocity,taint,vel={1}", LocalID, _velocity); | 609 | DetailLog("{0},BSCharacter.setVelocity,taint,vel={1}", LocalID, _velocity); |
387 | ForceVelocity = _velocity; | 610 | ForceVelocity = _velocity; |
388 | }); | 611 | }); |
@@ -391,30 +614,11 @@ public sealed class BSCharacter : BSPhysObject | |||
391 | public override OMV.Vector3 ForceVelocity { | 614 | public override OMV.Vector3 ForceVelocity { |
392 | get { return _velocity; } | 615 | get { return _velocity; } |
393 | set { | 616 | set { |
394 | // Depending on whether the avatar is moving or not, change the friction | 617 | PhysicsScene.AssertInTaintTime("BSCharacter.ForceVelocity"); |
395 | // to keep the avatar from slipping around | ||
396 | if (_velocity.Length() == 0) | ||
397 | { | ||
398 | if (_currentFriction != PhysicsScene.Params.avatarStandingFriction) | ||
399 | { | ||
400 | _currentFriction = PhysicsScene.Params.avatarStandingFriction; | ||
401 | BulletSimAPI.SetFriction2(PhysBody.ptr, _currentFriction); | ||
402 | } | ||
403 | } | ||
404 | else | ||
405 | { | ||
406 | if (_currentFriction != PhysicsScene.Params.avatarFriction) | ||
407 | { | ||
408 | _currentFriction = PhysicsScene.Params.avatarFriction; | ||
409 | BulletSimAPI.SetFriction2(PhysBody.ptr, _currentFriction); | ||
410 | } | ||
411 | } | ||
412 | _velocity = value; | ||
413 | // Remember the set velocity so we can suppress the reduction by friction, ... | ||
414 | _appliedVelocity = value; | ||
415 | 618 | ||
416 | BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, _velocity); | 619 | _velocity = value; |
417 | BulletSimAPI.Activate2(PhysBody.ptr, true); | 620 | PhysicsScene.PE.SetLinearVelocity(PhysBody, _velocity); |
621 | PhysicsScene.PE.Activate(PhysBody, true); | ||
418 | } | 622 | } |
419 | } | 623 | } |
420 | public override OMV.Vector3 Torque { | 624 | public override OMV.Vector3 Torque { |
@@ -439,13 +643,16 @@ public sealed class BSCharacter : BSPhysObject | |||
439 | public override OMV.Quaternion Orientation { | 643 | public override OMV.Quaternion Orientation { |
440 | get { return _orientation; } | 644 | get { return _orientation; } |
441 | set { | 645 | set { |
442 | _orientation = value; | 646 | // Orientation is set zillions of times when an avatar is walking. It's like |
443 | // m_log.DebugFormat("{0}: set orientation to {1}", LogHeader, _orientation); | 647 | // the viewer doesn't trust us. |
444 | PhysicsScene.TaintedObject("BSCharacter.setOrientation", delegate() | 648 | if (_orientation != value) |
445 | { | 649 | { |
446 | // _position = BulletSimAPI.GetPosition2(BSBody.ptr); | 650 | _orientation = value; |
447 | BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); | 651 | PhysicsScene.TaintedObject("BSCharacter.setOrientation", delegate() |
448 | }); | 652 | { |
653 | ForceOrientation = _orientation; | ||
654 | }); | ||
655 | } | ||
449 | } | 656 | } |
450 | } | 657 | } |
451 | // Go directly to Bullet to get/set the value. | 658 | // Go directly to Bullet to get/set the value. |
@@ -453,13 +660,17 @@ public sealed class BSCharacter : BSPhysObject | |||
453 | { | 660 | { |
454 | get | 661 | get |
455 | { | 662 | { |
456 | _orientation = BulletSimAPI.GetOrientation2(PhysBody.ptr); | 663 | _orientation = PhysicsScene.PE.GetOrientation(PhysBody); |
457 | return _orientation; | 664 | return _orientation; |
458 | } | 665 | } |
459 | set | 666 | set |
460 | { | 667 | { |
461 | _orientation = value; | 668 | _orientation = value; |
462 | BulletSimAPI.SetTranslation2(PhysBody.ptr, _position, _orientation); | 669 | if (PhysBody.HasPhysicalBody) |
670 | { | ||
671 | // _position = PhysicsScene.PE.GetPosition(BSBody); | ||
672 | PhysicsScene.PE.SetTranslation(PhysBody, _position, _orientation); | ||
673 | } | ||
463 | } | 674 | } |
464 | } | 675 | } |
465 | public override int PhysicsActorType { | 676 | public override int PhysicsActorType { |
@@ -478,10 +689,14 @@ public sealed class BSCharacter : BSPhysObject | |||
478 | public override bool IsStatic { | 689 | public override bool IsStatic { |
479 | get { return false; } | 690 | get { return false; } |
480 | } | 691 | } |
692 | public override bool IsPhysicallyActive { | ||
693 | get { return true; } | ||
694 | } | ||
481 | public override bool Flying { | 695 | public override bool Flying { |
482 | get { return _flying; } | 696 | get { return _flying; } |
483 | set { | 697 | set { |
484 | _flying = value; | 698 | _flying = value; |
699 | |||
485 | // simulate flying by changing the effect of gravity | 700 | // simulate flying by changing the effect of gravity |
486 | Buoyancy = ComputeBuoyancyFromFlying(_flying); | 701 | Buoyancy = ComputeBuoyancyFromFlying(_flying); |
487 | } | 702 | } |
@@ -500,27 +715,18 @@ public sealed class BSCharacter : BSPhysObject | |||
500 | get { return _throttleUpdates; } | 715 | get { return _throttleUpdates; } |
501 | set { _throttleUpdates = value; } | 716 | set { _throttleUpdates = value; } |
502 | } | 717 | } |
503 | public override bool IsColliding { | ||
504 | get { return (CollidingStep == PhysicsScene.SimulationStep); } | ||
505 | set { _isColliding = value; } | ||
506 | } | ||
507 | public override bool CollidingGround { | ||
508 | get { return (CollidingGroundStep == PhysicsScene.SimulationStep); } | ||
509 | set { CollidingGround = value; } | ||
510 | } | ||
511 | public override bool CollidingObj { | ||
512 | get { return _collidingObj; } | ||
513 | set { _collidingObj = value; } | ||
514 | } | ||
515 | public override bool FloatOnWater { | 718 | public override bool FloatOnWater { |
516 | set { | 719 | set { |
517 | _floatOnWater = value; | 720 | _floatOnWater = value; |
518 | PhysicsScene.TaintedObject("BSCharacter.setFloatOnWater", delegate() | 721 | PhysicsScene.TaintedObject("BSCharacter.setFloatOnWater", delegate() |
519 | { | 722 | { |
520 | if (_floatOnWater) | 723 | if (PhysBody.HasPhysicalBody) |
521 | CurrentCollisionFlags = BulletSimAPI.AddToCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER); | 724 | { |
522 | else | 725 | if (_floatOnWater) |
523 | CurrentCollisionFlags = BulletSimAPI.RemoveFromCollisionFlags2(PhysBody.ptr, CollisionFlags.BS_FLOATS_ON_WATER); | 726 | CurrentCollisionFlags = PhysicsScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER); |
727 | else | ||
728 | CurrentCollisionFlags = PhysicsScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER); | ||
729 | } | ||
524 | }); | 730 | }); |
525 | } | 731 | } |
526 | } | 732 | } |
@@ -549,11 +755,16 @@ public sealed class BSCharacter : BSPhysObject | |||
549 | } | 755 | } |
550 | public override float ForceBuoyancy { | 756 | public override float ForceBuoyancy { |
551 | get { return _buoyancy; } | 757 | get { return _buoyancy; } |
552 | set { _buoyancy = value; | 758 | set { |
759 | PhysicsScene.AssertInTaintTime("BSCharacter.ForceBuoyancy"); | ||
760 | |||
761 | _buoyancy = value; | ||
553 | DetailLog("{0},BSCharacter.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy); | 762 | DetailLog("{0},BSCharacter.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy); |
554 | // Buoyancy is faked by changing the gravity applied to the object | 763 | // Buoyancy is faked by changing the gravity applied to the object |
555 | float grav = PhysicsScene.Params.gravity * (1f - _buoyancy); | 764 | float grav = BSParam.Gravity * (1f - _buoyancy); |
556 | BulletSimAPI.SetGravity2(PhysBody.ptr, new OMV.Vector3(0f, 0f, grav)); | 765 | Gravity = new OMV.Vector3(0f, 0f, grav); |
766 | if (PhysBody.HasPhysicalBody) | ||
767 | PhysicsScene.PE.SetGravity(PhysBody, Gravity); | ||
557 | } | 768 | } |
558 | } | 769 | } |
559 | 770 | ||
@@ -589,24 +800,33 @@ public sealed class BSCharacter : BSPhysObject | |||
589 | public override float APIDStrength { set { return; } } | 800 | public override float APIDStrength { set { return; } } |
590 | public override float APIDDamping { set { return; } } | 801 | public override float APIDDamping { set { return; } } |
591 | 802 | ||
592 | public override void AddForce(OMV.Vector3 force, bool pushforce) { | 803 | public override void AddForce(OMV.Vector3 force, bool pushforce) |
804 | { | ||
805 | // Since this force is being applied in only one step, make this a force per second. | ||
806 | OMV.Vector3 addForce = force / PhysicsScene.LastTimeStep; | ||
807 | AddForce(addForce, pushforce, false); | ||
808 | } | ||
809 | private void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) { | ||
593 | if (force.IsFinite()) | 810 | if (force.IsFinite()) |
594 | { | 811 | { |
595 | _force.X += force.X; | 812 | OMV.Vector3 addForce = Util.ClampV(force, BSParam.MaxAddForceMagnitude); |
596 | _force.Y += force.Y; | 813 | // DetailLog("{0},BSCharacter.addForce,call,force={1}", LocalID, addForce); |
597 | _force.Z += force.Z; | 814 | |
598 | // m_log.DebugFormat("{0}: AddForce. adding={1}, newForce={2}", LogHeader, force, _force); | 815 | PhysicsScene.TaintedObject(inTaintTime, "BSCharacter.AddForce", delegate() |
599 | PhysicsScene.TaintedObject("BSCharacter.AddForce", delegate() | ||
600 | { | 816 | { |
601 | DetailLog("{0},BSCharacter.setAddForce,taint,addedForce={1}", LocalID, _force); | 817 | // Bullet adds this central force to the total force for this tick |
602 | BulletSimAPI.SetObjectForce2(PhysBody.ptr, _force); | 818 | // DetailLog("{0},BSCharacter.addForce,taint,force={1}", LocalID, addForce); |
819 | if (PhysBody.HasPhysicalBody) | ||
820 | { | ||
821 | PhysicsScene.PE.ApplyCentralForce(PhysBody, addForce); | ||
822 | } | ||
603 | }); | 823 | }); |
604 | } | 824 | } |
605 | else | 825 | else |
606 | { | 826 | { |
607 | m_log.ErrorFormat("{0}: Got a NaN force applied to a Character", LogHeader); | 827 | m_log.WarnFormat("{0}: Got a NaN force applied to a character. LocalID={1}", LogHeader, LocalID); |
828 | return; | ||
608 | } | 829 | } |
609 | //m_lastUpdateSent = false; | ||
610 | } | 830 | } |
611 | 831 | ||
612 | public override void AddAngularForce(OMV.Vector3 force, bool pushforce) { | 832 | public override void AddAngularForce(OMV.Vector3 force, bool pushforce) { |
@@ -614,24 +834,31 @@ public sealed class BSCharacter : BSPhysObject | |||
614 | public override void SetMomentum(OMV.Vector3 momentum) { | 834 | public override void SetMomentum(OMV.Vector3 momentum) { |
615 | } | 835 | } |
616 | 836 | ||
617 | private void ComputeAvatarScale(OMV.Vector3 size) | 837 | private OMV.Vector3 ComputeAvatarScale(OMV.Vector3 size) |
618 | { | 838 | { |
619 | // The 'size' given by the simulator is the mid-point of the avatar | 839 | OMV.Vector3 newScale; |
620 | // and X and Y are unspecified. | 840 | |
621 | 841 | // Bullet's capsule total height is the "passed height + radius * 2"; | |
622 | OMV.Vector3 newScale = size; | 842 | // The base capsule is 1 diameter and 2 height (passed radius=0.5, passed height = 1) |
623 | // newScale.X = PhysicsScene.Params.avatarCapsuleWidth; | 843 | // The number we pass in for 'scaling' is the multiplier to get that base |
624 | // newScale.Y = PhysicsScene.Params.avatarCapsuleDepth; | 844 | // shape to be the size desired. |
625 | 845 | // So, when creating the scale for the avatar height, we take the passed height | |
626 | // From the total height, remove the capsule half spheres that are at each end | 846 | // (size.Z) and remove the caps. |
627 | // The 1.15f came from ODE. Not sure what this factors in. | 847 | // Another oddity of the Bullet capsule implementation is that it presumes the Y |
628 | // newScale.Z = (size.Z * 1.15f) - (newScale.X + newScale.Y); | 848 | // dimension is the radius of the capsule. Even though some of the code allows |
849 | // for a asymmetrical capsule, other parts of the code presume it is cylindrical. | ||
850 | |||
851 | // Scale is multiplier of radius with one of "0.5" | ||
852 | newScale.X = size.X / 2f; | ||
853 | newScale.Y = size.Y / 2f; | ||
629 | 854 | ||
630 | // The total scale height is the central cylindar plus the caps on the two ends. | 855 | // The total scale height is the central cylindar plus the caps on the two ends. |
631 | newScale.Z = size.Z + (Math.Min(size.X, size.Y) * 2f); | 856 | newScale.Z = (size.Z + (Math.Min(size.X, size.Y) * 2)) / 2f; |
857 | // If smaller than the endcaps, just fake like we're almost that small | ||
858 | if (newScale.Z < 0) | ||
859 | newScale.Z = 0.1f; | ||
632 | 860 | ||
633 | // Convert diameters to radii and height to half height -- the way Bullet expects it. | 861 | return newScale; |
634 | Scale = newScale / 2f; | ||
635 | } | 862 | } |
636 | 863 | ||
637 | // set _avatarVolume and _mass based on capsule size, _density and Scale | 864 | // set _avatarVolume and _mass based on capsule size, _density and Scale |
@@ -639,16 +866,16 @@ public sealed class BSCharacter : BSPhysObject | |||
639 | { | 866 | { |
640 | _avatarVolume = (float)( | 867 | _avatarVolume = (float)( |
641 | Math.PI | 868 | Math.PI |
642 | * Scale.X | 869 | * Size.X / 2f |
643 | * Scale.Y // the area of capsule cylinder | 870 | * Size.Y / 2f // the area of capsule cylinder |
644 | * Scale.Z // times height of capsule cylinder | 871 | * Size.Z // times height of capsule cylinder |
645 | + 1.33333333f | 872 | + 1.33333333f |
646 | * Math.PI | 873 | * Math.PI |
647 | * Scale.X | 874 | * Size.X / 2f |
648 | * Math.Min(Scale.X, Scale.Y) | 875 | * Math.Min(Size.X, Size.Y) / 2 |
649 | * Scale.Y // plus the volume of the capsule end caps | 876 | * Size.Y / 2f // plus the volume of the capsule end caps |
650 | ); | 877 | ); |
651 | _mass = _avatarDensity * _avatarVolume; | 878 | _mass = Density * BSParam.DensityScaleFactor * _avatarVolume; |
652 | } | 879 | } |
653 | 880 | ||
654 | // The physics engine says that properties have updated. Update same and inform | 881 | // The physics engine says that properties have updated. Update same and inform |
@@ -657,27 +884,30 @@ public sealed class BSCharacter : BSPhysObject | |||
657 | { | 884 | { |
658 | _position = entprop.Position; | 885 | _position = entprop.Position; |
659 | _orientation = entprop.Rotation; | 886 | _orientation = entprop.Rotation; |
660 | _velocity = entprop.Velocity; | 887 | |
888 | // Smooth velocity. OpenSimulator is VERY sensitive to changes in velocity of the avatar | ||
889 | // and will send agent updates to the clients if velocity changes by more than | ||
890 | // 0.001m/s. Bullet introduces a lot of jitter in the velocity which causes many | ||
891 | // extra updates. | ||
892 | if (!entprop.Velocity.ApproxEquals(_velocity, 0.1f)) | ||
893 | _velocity = entprop.Velocity; | ||
894 | |||
661 | _acceleration = entprop.Acceleration; | 895 | _acceleration = entprop.Acceleration; |
662 | _rotationalVelocity = entprop.RotationalVelocity; | 896 | _rotationalVelocity = entprop.RotationalVelocity; |
897 | |||
663 | // Do some sanity checking for the avatar. Make sure it's above ground and inbounds. | 898 | // Do some sanity checking for the avatar. Make sure it's above ground and inbounds. |
664 | PositionSanityCheck(true); | 899 | if (PositionSanityCheck(true)) |
900 | { | ||
901 | DetailLog("{0},BSCharacter.UpdateProperties,updatePosForSanity,pos={1}", LocalID, _position); | ||
902 | entprop.Position = _position; | ||
903 | } | ||
665 | 904 | ||
666 | // remember the current and last set values | 905 | // remember the current and last set values |
667 | LastEntityProperties = CurrentEntityProperties; | 906 | LastEntityProperties = CurrentEntityProperties; |
668 | CurrentEntityProperties = entprop; | 907 | CurrentEntityProperties = entprop; |
669 | 908 | ||
670 | if (entprop.Velocity != LastEntityProperties.Velocity) | ||
671 | { | ||
672 | // Changes in the velocity are suppressed in avatars. | ||
673 | // That's just the way they are defined. | ||
674 | OMV.Vector3 avVel = new OMV.Vector3(_appliedVelocity.X, _appliedVelocity.Y, entprop.Velocity.Z); | ||
675 | _velocity = avVel; | ||
676 | BulletSimAPI.SetLinearVelocity2(PhysBody.ptr, avVel); | ||
677 | } | ||
678 | |||
679 | // Tell the linkset about value changes | 909 | // Tell the linkset about value changes |
680 | Linkset.UpdateProperties(this); | 910 | // Linkset.UpdateProperties(UpdatedProperties.EntPropUpdates, this); |
681 | 911 | ||
682 | // Avatars don't report their changes the usual way. Changes are checked for in the heartbeat loop. | 912 | // Avatars don't report their changes the usual way. Changes are checked for in the heartbeat loop. |
683 | // base.RequestPhysicsterseUpdate(); | 913 | // base.RequestPhysicsterseUpdate(); |