aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/PhysicsModules/BulletS/BSCharacter.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/PhysicsModules/BulletS/BSCharacter.cs')
-rw-r--r--OpenSim/Region/PhysicsModules/BulletS/BSCharacter.cs813
1 files changed, 813 insertions, 0 deletions
diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSCharacter.cs b/OpenSim/Region/PhysicsModules/BulletS/BSCharacter.cs
new file mode 100644
index 0000000..9c3f160
--- /dev/null
+++ b/OpenSim/Region/PhysicsModules/BulletS/BSCharacter.cs
@@ -0,0 +1,813 @@
1/*
2 * Copyright (c) Contributors, http://opensimulator.org/
3 * See CONTRIBUTORS.TXT for a full list of copyright holders.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions are met:
7 * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above copyrightD
10 * notice, this list of conditions and the following disclaimer in the
11 * documentation and/or other materials provided with the distribution.
12 * * Neither the name of the OpenSimulator Project nor the
13 * names of its contributors may be used to endorse or promote products
14 * derived from this software without specific prior written permission.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
17 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
20 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27using System;
28using System.Collections.Generic;
29using System.Reflection;
30using log4net;
31using OMV = OpenMetaverse;
32using OpenSim.Framework;
33using OpenSim.Region.Physics.Manager;
34
35namespace OpenSim.Region.Physics.BulletSPlugin
36{
37public sealed class BSCharacter : BSPhysObject
38{
39 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
40 private static readonly string LogHeader = "[BULLETS CHAR]";
41
42 // private bool _stopped;
43 private OMV.Vector3 _size;
44 private bool _grabbed;
45 private bool _selected;
46 private float _mass;
47 private float _avatarVolume;
48 private float _collisionScore;
49 private OMV.Vector3 _acceleration;
50 private int _physicsActorType;
51 private bool _isPhysical;
52 private bool _flying;
53 private bool _setAlwaysRun;
54 private bool _throttleUpdates;
55 private bool _floatOnWater;
56 private OMV.Vector3 _rotationalVelocity;
57 private bool _kinematic;
58 private float _buoyancy;
59
60 private BSActorAvatarMove m_moveActor;
61 private const string AvatarMoveActorName = "BSCharacter.AvatarMove";
62
63 private OMV.Vector3 _PIDTarget;
64 private float _PIDTau;
65
66// public override OMV.Vector3 RawVelocity
67// { get { return base.RawVelocity; }
68// set {
69// if (value != base.RawVelocity)
70// Util.PrintCallStack();
71// Console.WriteLine("Set rawvel to {0}", value);
72// base.RawVelocity = value; }
73// }
74
75 // Avatars are always complete (in the physics engine sense)
76 public override bool IsIncomplete { get { return false; } }
77
78 public BSCharacter(
79 uint localID, String avName, BSScene parent_scene, OMV.Vector3 pos, OMV.Vector3 vel, OMV.Vector3 size, bool isFlying)
80
81 : base(parent_scene, localID, avName, "BSCharacter")
82 {
83 _physicsActorType = (int)ActorTypes.Agent;
84 RawPosition = pos;
85
86 _flying = isFlying;
87 RawOrientation = OMV.Quaternion.Identity;
88 RawVelocity = vel;
89 _buoyancy = ComputeBuoyancyFromFlying(isFlying);
90 Friction = BSParam.AvatarStandingFriction;
91 Density = BSParam.AvatarDensity;
92
93 // Old versions of ScenePresence passed only the height. If width and/or depth are zero,
94 // replace with the default values.
95 _size = size;
96 if (_size.X == 0f) _size.X = BSParam.AvatarCapsuleDepth;
97 if (_size.Y == 0f) _size.Y = BSParam.AvatarCapsuleWidth;
98
99 // The dimensions of the physical capsule are kept in the scale.
100 // Physics creates a unit capsule which is scaled by the physics engine.
101 Scale = ComputeAvatarScale(_size);
102 // set _avatarVolume and _mass based on capsule size, _density and Scale
103 ComputeAvatarVolumeAndMass();
104
105 DetailLog(
106 "{0},BSCharacter.create,call,size={1},scale={2},density={3},volume={4},mass={5},pos={6},vel={7}",
107 LocalID, _size, Scale, Density, _avatarVolume, RawMass, pos, vel);
108
109 // do actual creation in taint time
110 PhysScene.TaintedObject(LocalID, "BSCharacter.create", delegate()
111 {
112 DetailLog("{0},BSCharacter.create,taint", LocalID);
113
114 // New body and shape into PhysBody and PhysShape
115 PhysScene.Shapes.GetBodyAndShape(true, PhysScene.World, this);
116
117 // The avatar's movement is controlled by this motor that speeds up and slows down
118 // the avatar seeking to reach the motor's target speed.
119 // This motor runs as a prestep action for the avatar so it will keep the avatar
120 // standing as well as moving. Destruction of the avatar will destroy the pre-step action.
121 m_moveActor = new BSActorAvatarMove(PhysScene, this, AvatarMoveActorName);
122 PhysicalActors.Add(AvatarMoveActorName, m_moveActor);
123
124 SetPhysicalProperties();
125
126 IsInitialized = true;
127 });
128 return;
129 }
130
131 // called when this character is being destroyed and the resources should be released
132 public override void Destroy()
133 {
134 IsInitialized = false;
135
136 base.Destroy();
137
138 DetailLog("{0},BSCharacter.Destroy", LocalID);
139 PhysScene.TaintedObject(LocalID, "BSCharacter.destroy", delegate()
140 {
141 PhysScene.Shapes.DereferenceBody(PhysBody, null /* bodyCallback */);
142 PhysBody.Clear();
143 PhysShape.Dereference(PhysScene);
144 PhysShape = new BSShapeNull();
145 });
146 }
147
148 private void SetPhysicalProperties()
149 {
150 PhysScene.PE.RemoveObjectFromWorld(PhysScene.World, PhysBody);
151
152 ForcePosition = RawPosition;
153
154 // Set the velocity
155 if (m_moveActor != null)
156 m_moveActor.SetVelocityAndTarget(RawVelocity, RawVelocity, false);
157
158 ForceVelocity = RawVelocity;
159 TargetVelocity = RawVelocity;
160
161 // This will enable or disable the flying buoyancy of the avatar.
162 // Needs to be reset especially when an avatar is recreated after crossing a region boundry.
163 Flying = _flying;
164
165 PhysScene.PE.SetRestitution(PhysBody, BSParam.AvatarRestitution);
166 PhysScene.PE.SetMargin(PhysShape.physShapeInfo, PhysScene.Params.collisionMargin);
167 PhysScene.PE.SetLocalScaling(PhysShape.physShapeInfo, Scale);
168 PhysScene.PE.SetContactProcessingThreshold(PhysBody, BSParam.ContactProcessingThreshold);
169 if (BSParam.CcdMotionThreshold > 0f)
170 {
171 PhysScene.PE.SetCcdMotionThreshold(PhysBody, BSParam.CcdMotionThreshold);
172 PhysScene.PE.SetCcdSweptSphereRadius(PhysBody, BSParam.CcdSweptSphereRadius);
173 }
174
175 UpdatePhysicalMassProperties(RawMass, false);
176
177 // Make so capsule does not fall over
178 PhysScene.PE.SetAngularFactorV(PhysBody, OMV.Vector3.Zero);
179
180 // The avatar mover sets some parameters.
181 PhysicalActors.Refresh();
182
183 PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.CF_CHARACTER_OBJECT);
184
185 PhysScene.PE.AddObjectToWorld(PhysScene.World, PhysBody);
186
187 // PhysicsScene.PE.ForceActivationState(PhysBody, ActivationState.ACTIVE_TAG);
188 PhysScene.PE.ForceActivationState(PhysBody, ActivationState.DISABLE_DEACTIVATION);
189 PhysScene.PE.UpdateSingleAabb(PhysScene.World, PhysBody);
190
191 // Do this after the object has been added to the world
192 if (BSParam.AvatarToAvatarCollisionsByDefault)
193 PhysBody.collisionType = CollisionType.Avatar;
194 else
195 PhysBody.collisionType = CollisionType.PhantomToOthersAvatar;
196
197 PhysBody.ApplyCollisionMask(PhysScene);
198 }
199
200 public override void RequestPhysicsterseUpdate()
201 {
202 base.RequestPhysicsterseUpdate();
203 }
204
205 // No one calls this method so I don't know what it could possibly mean
206 public override bool Stopped { get { return false; } }
207
208 public override OMV.Vector3 Size {
209 get
210 {
211 // Avatar capsule size is kept in the scale parameter.
212 return _size;
213 }
214
215 set {
216 // This is how much the avatar size is changing. Positive means getting bigger.
217 // The avatar altitude must be adjusted for this change.
218 float heightChange = value.Z - _size.Z;
219
220 _size = value;
221 // Old versions of ScenePresence passed only the height. If width and/or depth are zero,
222 // replace with the default values.
223 if (_size.X == 0f) _size.X = BSParam.AvatarCapsuleDepth;
224 if (_size.Y == 0f) _size.Y = BSParam.AvatarCapsuleWidth;
225
226 Scale = ComputeAvatarScale(_size);
227 ComputeAvatarVolumeAndMass();
228 DetailLog("{0},BSCharacter.setSize,call,size={1},scale={2},density={3},volume={4},mass={5}",
229 LocalID, _size, Scale, Density, _avatarVolume, RawMass);
230
231 PhysScene.TaintedObject(LocalID, "BSCharacter.setSize", delegate()
232 {
233 if (PhysBody.HasPhysicalBody && PhysShape.physShapeInfo.HasPhysicalShape)
234 {
235 PhysScene.PE.SetLocalScaling(PhysShape.physShapeInfo, Scale);
236 UpdatePhysicalMassProperties(RawMass, true);
237
238 // Adjust the avatar's position to account for the increase/decrease in size
239 ForcePosition = new OMV.Vector3(RawPosition.X, RawPosition.Y, RawPosition.Z + heightChange / 2f);
240
241 // Make sure this change appears as a property update event
242 PhysScene.PE.PushUpdate(PhysBody);
243 }
244 });
245
246 }
247 }
248
249 public override PrimitiveBaseShape Shape
250 {
251 set { BaseShape = value; }
252 }
253
254 public override bool Grabbed {
255 set { _grabbed = value; }
256 }
257 public override bool Selected {
258 set { _selected = value; }
259 }
260 public override bool IsSelected
261 {
262 get { return _selected; }
263 }
264 public override void CrossingFailure() { return; }
265 public override void link(PhysicsActor obj) { return; }
266 public override void delink() { return; }
267
268 // Set motion values to zero.
269 // Do it to the properties so the values get set in the physics engine.
270 // Push the setting of the values to the viewer.
271 // Called at taint time!
272 public override void ZeroMotion(bool inTaintTime)
273 {
274 RawVelocity = OMV.Vector3.Zero;
275 _acceleration = OMV.Vector3.Zero;
276 _rotationalVelocity = OMV.Vector3.Zero;
277
278 // Zero some other properties directly into the physics engine
279 PhysScene.TaintedObject(inTaintTime, LocalID, "BSCharacter.ZeroMotion", delegate()
280 {
281 if (PhysBody.HasPhysicalBody)
282 PhysScene.PE.ClearAllForces(PhysBody);
283 });
284 }
285
286 public override void ZeroAngularMotion(bool inTaintTime)
287 {
288 _rotationalVelocity = OMV.Vector3.Zero;
289
290 PhysScene.TaintedObject(inTaintTime, LocalID, "BSCharacter.ZeroMotion", delegate()
291 {
292 if (PhysBody.HasPhysicalBody)
293 {
294 PhysScene.PE.SetInterpolationAngularVelocity(PhysBody, OMV.Vector3.Zero);
295 PhysScene.PE.SetAngularVelocity(PhysBody, OMV.Vector3.Zero);
296 // The next also get rid of applied linear force but the linear velocity is untouched.
297 PhysScene.PE.ClearForces(PhysBody);
298 }
299 });
300 }
301
302
303 public override void LockAngularMotion(OMV.Vector3 axis) { return; }
304
305 public override OMV.Vector3 Position {
306 get {
307 // Don't refetch the position because this function is called a zillion times
308 // RawPosition = PhysicsScene.PE.GetObjectPosition(Scene.World, LocalID);
309 return RawPosition;
310 }
311 set {
312 RawPosition = value;
313
314 PhysScene.TaintedObject(LocalID, "BSCharacter.setPosition", delegate()
315 {
316 DetailLog("{0},BSCharacter.SetPosition,taint,pos={1},orient={2}", LocalID, RawPosition, RawOrientation);
317 PositionSanityCheck();
318 ForcePosition = RawPosition;
319 });
320 }
321 }
322 public override OMV.Vector3 ForcePosition {
323 get {
324 RawPosition = PhysScene.PE.GetPosition(PhysBody);
325 return RawPosition;
326 }
327 set {
328 RawPosition = value;
329 if (PhysBody.HasPhysicalBody)
330 {
331 PhysScene.PE.SetTranslation(PhysBody, RawPosition, RawOrientation);
332 }
333 }
334 }
335
336
337 // Check that the current position is sane and, if not, modify the position to make it so.
338 // Check for being below terrain or on water.
339 // Returns 'true' of the position was made sane by some action.
340 private bool PositionSanityCheck()
341 {
342 bool ret = false;
343
344 // TODO: check for out of bounds
345 if (!PhysScene.TerrainManager.IsWithinKnownTerrain(RawPosition))
346 {
347 // The character is out of the known/simulated area.
348 // Force the avatar position to be within known. ScenePresence will use the position
349 // plus the velocity to decide if the avatar is moving out of the region.
350 RawPosition = PhysScene.TerrainManager.ClampPositionIntoKnownTerrain(RawPosition);
351 DetailLog("{0},BSCharacter.PositionSanityCheck,notWithinKnownTerrain,clampedPos={1}", LocalID, RawPosition);
352 return true;
353 }
354
355 // If below the ground, move the avatar up
356 float terrainHeight = PhysScene.TerrainManager.GetTerrainHeightAtXYZ(RawPosition);
357 if (Position.Z < terrainHeight)
358 {
359 DetailLog("{0},BSCharacter.PositionSanityCheck,adjustForUnderGround,pos={1},terrain={2}", LocalID, RawPosition, terrainHeight);
360 RawPosition = new OMV.Vector3(RawPosition.X, RawPosition.Y, terrainHeight + BSParam.AvatarBelowGroundUpCorrectionMeters);
361 ret = true;
362 }
363 if ((CurrentCollisionFlags & CollisionFlags.BS_FLOATS_ON_WATER) != 0)
364 {
365 float waterHeight = PhysScene.TerrainManager.GetWaterLevelAtXYZ(RawPosition);
366 if (Position.Z < waterHeight)
367 {
368 RawPosition = new OMV.Vector3(RawPosition.X, RawPosition.Y, waterHeight);
369 ret = true;
370 }
371 }
372
373 return ret;
374 }
375
376 // A version of the sanity check that also makes sure a new position value is
377 // pushed back to the physics engine. This routine would be used by anyone
378 // who is not already pushing the value.
379 private bool PositionSanityCheck(bool inTaintTime)
380 {
381 bool ret = false;
382 if (PositionSanityCheck())
383 {
384 // The new position value must be pushed into the physics engine but we can't
385 // just assign to "Position" because of potential call loops.
386 PhysScene.TaintedObject(inTaintTime, LocalID, "BSCharacter.PositionSanityCheck", delegate()
387 {
388 DetailLog("{0},BSCharacter.PositionSanityCheck,taint,pos={1},orient={2}", LocalID, RawPosition, RawOrientation);
389 ForcePosition = RawPosition;
390 });
391 ret = true;
392 }
393 return ret;
394 }
395
396 public override float Mass { get { return _mass; } }
397
398 // used when we only want this prim's mass and not the linkset thing
399 public override float RawMass {
400 get {return _mass; }
401 }
402 public override void UpdatePhysicalMassProperties(float physMass, bool inWorld)
403 {
404 OMV.Vector3 localInertia = PhysScene.PE.CalculateLocalInertia(PhysShape.physShapeInfo, physMass);
405 PhysScene.PE.SetMassProps(PhysBody, physMass, localInertia);
406 }
407
408 public override OMV.Vector3 Force {
409 get { return RawForce; }
410 set {
411 RawForce = value;
412 // m_log.DebugFormat("{0}: Force = {1}", LogHeader, _force);
413 PhysScene.TaintedObject(LocalID, "BSCharacter.SetForce", delegate()
414 {
415 DetailLog("{0},BSCharacter.setForce,taint,force={1}", LocalID, RawForce);
416 if (PhysBody.HasPhysicalBody)
417 PhysScene.PE.SetObjectForce(PhysBody, RawForce);
418 });
419 }
420 }
421
422 // Avatars don't do vehicles
423 public override int VehicleType { get { return (int)Vehicle.TYPE_NONE; } set { return; } }
424 public override void VehicleFloatParam(int param, float value) { }
425 public override void VehicleVectorParam(int param, OMV.Vector3 value) {}
426 public override void VehicleRotationParam(int param, OMV.Quaternion rotation) { }
427 public override void VehicleFlags(int param, bool remove) { }
428
429 // Allows the detection of collisions with inherently non-physical prims. see llVolumeDetect for more
430 public override void SetVolumeDetect(int param) { return; }
431 public override bool IsVolumeDetect { get { return false; } }
432
433 public override OMV.Vector3 GeometricCenter { get { return OMV.Vector3.Zero; } }
434 public override OMV.Vector3 CenterOfMass { get { return OMV.Vector3.Zero; } }
435
436 // Sets the target in the motor. This starts the changing of the avatar's velocity.
437 public override OMV.Vector3 TargetVelocity
438 {
439 get
440 {
441 return base.m_targetVelocity;
442 }
443 set
444 {
445 DetailLog("{0},BSCharacter.setTargetVelocity,call,vel={1}", LocalID, value);
446 m_targetVelocity = value;
447 OMV.Vector3 targetVel = value;
448 if (_setAlwaysRun && !_flying)
449 targetVel *= new OMV.Vector3(BSParam.AvatarAlwaysRunFactor, BSParam.AvatarAlwaysRunFactor, 1f);
450
451 if (m_moveActor != null)
452 m_moveActor.SetVelocityAndTarget(RawVelocity, targetVel, false /* inTaintTime */);
453 }
454 }
455 // Directly setting velocity means this is what the user really wants now.
456 public override OMV.Vector3 Velocity {
457 get { return RawVelocity; }
458 set {
459 RawVelocity = value;
460 OMV.Vector3 vel = RawVelocity;
461
462 DetailLog("{0}: set Velocity = {1}", LocalID, value);
463
464 PhysScene.TaintedObject(LocalID, "BSCharacter.setVelocity", delegate()
465 {
466 if (m_moveActor != null)
467 m_moveActor.SetVelocityAndTarget(vel, vel, true /* inTaintTime */);
468
469 DetailLog("{0},BSCharacter.setVelocity,taint,vel={1}", LocalID, vel);
470 ForceVelocity = vel;
471 });
472 }
473 }
474
475 public override OMV.Vector3 ForceVelocity {
476 get { return RawVelocity; }
477 set {
478 PhysScene.AssertInTaintTime("BSCharacter.ForceVelocity");
479// Util.PrintCallStack();
480 DetailLog("{0}: set ForceVelocity = {1}", LocalID, value);
481
482 RawVelocity = value;
483 PhysScene.PE.SetLinearVelocity(PhysBody, RawVelocity);
484 PhysScene.PE.Activate(PhysBody, true);
485 }
486 }
487
488 public override OMV.Vector3 Torque {
489 get { return RawTorque; }
490 set { RawTorque = value;
491 }
492 }
493
494 public override float CollisionScore {
495 get { return _collisionScore; }
496 set { _collisionScore = value;
497 }
498 }
499 public override OMV.Vector3 Acceleration {
500 get { return _acceleration; }
501 set { _acceleration = value; }
502 }
503 public override OMV.Quaternion Orientation {
504 get { return RawOrientation; }
505 set {
506 // Orientation is set zillions of times when an avatar is walking. It's like
507 // the viewer doesn't trust us.
508 if (RawOrientation != value)
509 {
510 RawOrientation = value;
511 PhysScene.TaintedObject(LocalID, "BSCharacter.setOrientation", delegate()
512 {
513 // Bullet assumes we know what we are doing when forcing orientation
514 // so it lets us go against all the rules and just compensates for them later.
515 // This forces rotation to be only around the Z axis and doesn't change any of the other axis.
516 // This keeps us from flipping the capsule over which the veiwer does not understand.
517 float oRoll, oPitch, oYaw;
518 RawOrientation.GetEulerAngles(out oRoll, out oPitch, out oYaw);
519 OMV.Quaternion trimmedOrientation = OMV.Quaternion.CreateFromEulers(0f, 0f, oYaw);
520 // DetailLog("{0},BSCharacter.setOrientation,taint,val={1},valDir={2},conv={3},convDir={4}",
521 // LocalID, RawOrientation, OMV.Vector3.UnitX * RawOrientation,
522 // trimmedOrientation, OMV.Vector3.UnitX * trimmedOrientation);
523 ForceOrientation = trimmedOrientation;
524 });
525 }
526 }
527 }
528 // Go directly to Bullet to get/set the value.
529 public override OMV.Quaternion ForceOrientation
530 {
531 get
532 {
533 RawOrientation = PhysScene.PE.GetOrientation(PhysBody);
534 return RawOrientation;
535 }
536 set
537 {
538 RawOrientation = value;
539 if (PhysBody.HasPhysicalBody)
540 {
541 // RawPosition = PhysicsScene.PE.GetPosition(BSBody);
542 PhysScene.PE.SetTranslation(PhysBody, RawPosition, RawOrientation);
543 }
544 }
545 }
546 public override int PhysicsActorType {
547 get { return _physicsActorType; }
548 set { _physicsActorType = value;
549 }
550 }
551 public override bool IsPhysical {
552 get { return _isPhysical; }
553 set { _isPhysical = value;
554 }
555 }
556 public override bool IsSolid {
557 get { return true; }
558 }
559 public override bool IsStatic {
560 get { return false; }
561 }
562 public override bool IsPhysicallyActive {
563 get { return true; }
564 }
565 public override bool Flying {
566 get { return _flying; }
567 set {
568 _flying = value;
569
570 // simulate flying by changing the effect of gravity
571 Buoyancy = ComputeBuoyancyFromFlying(_flying);
572 }
573 }
574 // Flying is implimented by changing the avatar's buoyancy.
575 // Would this be done better with a vehicle type?
576 private float ComputeBuoyancyFromFlying(bool ifFlying) {
577 return ifFlying ? 1f : 0f;
578 }
579 public override bool
580 SetAlwaysRun {
581 get { return _setAlwaysRun; }
582 set { _setAlwaysRun = value; }
583 }
584 public override bool ThrottleUpdates {
585 get { return _throttleUpdates; }
586 set { _throttleUpdates = value; }
587 }
588 public override bool FloatOnWater {
589 set {
590 _floatOnWater = value;
591 PhysScene.TaintedObject(LocalID, "BSCharacter.setFloatOnWater", delegate()
592 {
593 if (PhysBody.HasPhysicalBody)
594 {
595 if (_floatOnWater)
596 CurrentCollisionFlags = PhysScene.PE.AddToCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER);
597 else
598 CurrentCollisionFlags = PhysScene.PE.RemoveFromCollisionFlags(PhysBody, CollisionFlags.BS_FLOATS_ON_WATER);
599 }
600 });
601 }
602 }
603 public override OMV.Vector3 RotationalVelocity {
604 get { return _rotationalVelocity; }
605 set { _rotationalVelocity = value; }
606 }
607 public override OMV.Vector3 ForceRotationalVelocity {
608 get { return _rotationalVelocity; }
609 set { _rotationalVelocity = value; }
610 }
611 public override bool Kinematic {
612 get { return _kinematic; }
613 set { _kinematic = value; }
614 }
615 // neg=fall quickly, 0=1g, 1=0g, pos=float up
616 public override float Buoyancy {
617 get { return _buoyancy; }
618 set { _buoyancy = value;
619 PhysScene.TaintedObject(LocalID, "BSCharacter.setBuoyancy", delegate()
620 {
621 DetailLog("{0},BSCharacter.setBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
622 ForceBuoyancy = _buoyancy;
623 });
624 }
625 }
626 public override float ForceBuoyancy {
627 get { return _buoyancy; }
628 set {
629 PhysScene.AssertInTaintTime("BSCharacter.ForceBuoyancy");
630
631 _buoyancy = value;
632 DetailLog("{0},BSCharacter.setForceBuoyancy,taint,buoy={1}", LocalID, _buoyancy);
633 // Buoyancy is faked by changing the gravity applied to the object
634 float grav = BSParam.Gravity * (1f - _buoyancy);
635 Gravity = new OMV.Vector3(0f, 0f, grav);
636 if (PhysBody.HasPhysicalBody)
637 PhysScene.PE.SetGravity(PhysBody, Gravity);
638 }
639 }
640
641 // Used for MoveTo
642 public override OMV.Vector3 PIDTarget {
643 set { _PIDTarget = value; }
644 }
645
646 public override bool PIDActive { get; set; }
647
648 public override float PIDTau {
649 set { _PIDTau = value; }
650 }
651
652 public override void AddForce(OMV.Vector3 force, bool pushforce)
653 {
654 // Since this force is being applied in only one step, make this a force per second.
655 OMV.Vector3 addForce = force / PhysScene.LastTimeStep;
656 AddForce(addForce, pushforce, false);
657 }
658 public override void AddForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) {
659 if (force.IsFinite())
660 {
661 OMV.Vector3 addForce = Util.ClampV(force, BSParam.MaxAddForceMagnitude);
662 // DetailLog("{0},BSCharacter.addForce,call,force={1}", LocalID, addForce);
663
664 PhysScene.TaintedObject(inTaintTime, LocalID, "BSCharacter.AddForce", delegate()
665 {
666 // Bullet adds this central force to the total force for this tick
667 // DetailLog("{0},BSCharacter.addForce,taint,force={1}", LocalID, addForce);
668 if (PhysBody.HasPhysicalBody)
669 {
670 PhysScene.PE.ApplyCentralForce(PhysBody, addForce);
671 }
672 });
673 }
674 else
675 {
676 m_log.WarnFormat("{0}: Got a NaN force applied to a character. LocalID={1}", LogHeader, LocalID);
677 return;
678 }
679 }
680
681 public override void AddAngularForce(OMV.Vector3 force, bool pushforce, bool inTaintTime) {
682 }
683 public override void SetMomentum(OMV.Vector3 momentum) {
684 }
685
686 private OMV.Vector3 ComputeAvatarScale(OMV.Vector3 size)
687 {
688 OMV.Vector3 newScale = size;
689
690 // Bullet's capsule total height is the "passed height + radius * 2";
691 // The base capsule is 1 unit in diameter and 2 units in height (passed radius=0.5, passed height = 1)
692 // The number we pass in for 'scaling' is the multiplier to get that base
693 // shape to be the size desired.
694 // So, when creating the scale for the avatar height, we take the passed height
695 // (size.Z) and remove the caps.
696 // An oddity of the Bullet capsule implementation is that it presumes the Y
697 // dimension is the radius of the capsule. Even though some of the code allows
698 // for a asymmetrical capsule, other parts of the code presume it is cylindrical.
699
700 // Scale is multiplier of radius with one of "0.5"
701
702 float heightAdjust = BSParam.AvatarHeightMidFudge;
703 if (BSParam.AvatarHeightLowFudge != 0f || BSParam.AvatarHeightHighFudge != 0f)
704 {
705 const float AVATAR_LOW = 1.1f;
706 const float AVATAR_MID = 1.775f; // 1.87f
707 const float AVATAR_HI = 2.45f;
708 // An avatar is between 1.1 and 2.45 meters. Midpoint is 1.775m.
709 float midHeightOffset = size.Z - AVATAR_MID;
710 if (midHeightOffset < 0f)
711 {
712 // Small avatar. Add the adjustment based on the distance from midheight
713 heightAdjust += ((-1f * midHeightOffset) / (AVATAR_MID - AVATAR_LOW)) * BSParam.AvatarHeightLowFudge;
714 }
715 else
716 {
717 // Large avatar. Add the adjustment based on the distance from midheight
718 heightAdjust += ((midHeightOffset) / (AVATAR_HI - AVATAR_MID)) * BSParam.AvatarHeightHighFudge;
719 }
720 }
721 if (BSParam.AvatarShape == BSShapeCollection.AvatarShapeCapsule)
722 {
723 newScale.X = size.X / 2f;
724 newScale.Y = size.Y / 2f;
725 // The total scale height is the central cylindar plus the caps on the two ends.
726 newScale.Z = (size.Z + (Math.Min(size.X, size.Y) * 2) + heightAdjust) / 2f;
727 }
728 else
729 {
730 newScale.Z = size.Z + heightAdjust;
731 }
732 // m_log.DebugFormat("{0} ComputeAvatarScale: size={1},adj={2},scale={3}", LogHeader, size, heightAdjust, newScale);
733
734 // If smaller than the endcaps, just fake like we're almost that small
735 if (newScale.Z < 0)
736 newScale.Z = 0.1f;
737
738 DetailLog("{0},BSCharacter.ComputerAvatarScale,size={1},lowF={2},midF={3},hiF={4},adj={5},newScale={6}",
739 LocalID, size, BSParam.AvatarHeightLowFudge, BSParam.AvatarHeightMidFudge, BSParam.AvatarHeightHighFudge, heightAdjust, newScale);
740
741 return newScale;
742 }
743
744 // set _avatarVolume and _mass based on capsule size, _density and Scale
745 private void ComputeAvatarVolumeAndMass()
746 {
747 _avatarVolume = (float)(
748 Math.PI
749 * Size.X / 2f
750 * Size.Y / 2f // the area of capsule cylinder
751 * Size.Z // times height of capsule cylinder
752 + 1.33333333f
753 * Math.PI
754 * Size.X / 2f
755 * Math.Min(Size.X, Size.Y) / 2
756 * Size.Y / 2f // plus the volume of the capsule end caps
757 );
758 _mass = Density * BSParam.DensityScaleFactor * _avatarVolume;
759 }
760
761 // The physics engine says that properties have updated. Update same and inform
762 // the world that things have changed.
763 public override void UpdateProperties(EntityProperties entprop)
764 {
765 // Let anyone (like the actors) modify the updated properties before they are pushed into the object and the simulator.
766 TriggerPreUpdatePropertyAction(ref entprop);
767
768 RawPosition = entprop.Position;
769 RawOrientation = entprop.Rotation;
770
771 // Smooth velocity. OpenSimulator is VERY sensitive to changes in velocity of the avatar
772 // and will send agent updates to the clients if velocity changes by more than
773 // 0.001m/s. Bullet introduces a lot of jitter in the velocity which causes many
774 // extra updates.
775 //
776 // XXX: Contrary to the above comment, setting an update threshold here above 0.4 actually introduces jitter to
777 // avatar movement rather than removes it. The larger the threshold, the bigger the jitter.
778 // This is most noticeable in level flight and can be seen with
779 // the "show updates" option in a viewer. With an update threshold, the RawVelocity cycles between a lower
780 // bound and an upper bound, where the difference between the two is enough to trigger a large delta v update
781 // and subsequently trigger an update in ScenePresence.SendTerseUpdateToAllClients(). The cause of this cycle (feedback?)
782 // has not yet been identified.
783 //
784 // If there is a threshold below 0.4 or no threshold check at all (as in ODE), then RawVelocity stays constant and extra
785 // updates are not triggered in ScenePresence.SendTerseUpdateToAllClients().
786// if (!entprop.Velocity.ApproxEquals(RawVelocity, 0.1f))
787 RawVelocity = entprop.Velocity;
788
789 _acceleration = entprop.Acceleration;
790 _rotationalVelocity = entprop.RotationalVelocity;
791
792 // Do some sanity checking for the avatar. Make sure it's above ground and inbounds.
793 if (PositionSanityCheck(true))
794 {
795 DetailLog("{0},BSCharacter.UpdateProperties,updatePosForSanity,pos={1}", LocalID, RawPosition);
796 entprop.Position = RawPosition;
797 }
798
799 // remember the current and last set values
800 LastEntityProperties = CurrentEntityProperties;
801 CurrentEntityProperties = entprop;
802
803 // Tell the linkset about value changes
804 // Linkset.UpdateProperties(UpdatedProperties.EntPropUpdates, this);
805
806 // Avatars don't report their changes the usual way. Changes are checked for in the heartbeat loop.
807 // PhysScene.PostUpdate(this);
808
809 DetailLog("{0},BSCharacter.UpdateProperties,call,pos={1},orient={2},vel={3},accel={4},rotVel={5}",
810 LocalID, RawPosition, RawOrientation, RawVelocity, _acceleration, _rotationalVelocity);
811 }
812}
813}