clockwerk-opensim – Blame information for rev 1

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