aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs')
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs863
1 files changed, 554 insertions, 309 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs
index 95a4134..fa3110c 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs
@@ -24,30 +24,17 @@
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 * 26 *
27 27 * The quotations from http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial
28/* RA: June 14, 2011. Copied from ODEDynamics.cs and converted to 28 * are Copyright (c) 2009 Linden Research, Inc and are used under their license
29 * call the BulletSim system. 29 * of Creative Commons Attribution-Share Alike 3.0
30 */ 30 * (http://creativecommons.org/licenses/by-sa/3.0/).
31/* Revised Aug, Sept 2009 by Kitto Flora. ODEDynamics.cs replaces
32 * ODEVehicleSettings.cs. It and ODEPrim.cs are re-organised:
33 * ODEPrim.cs contains methods dealing with Prim editing, Prim
34 * characteristics and Kinetic motion.
35 * ODEDynamics.cs contains methods dealing with Prim Physical motion
36 * (dynamics) and the associated settings. Old Linear and angular
37 * motors for dynamic motion have been replace with MoveLinear()
38 * and MoveAngular(); 'Physical' is used only to switch ODE dynamic
39 * simualtion on/off; VEHICAL_TYPE_NONE/VEHICAL_TYPE_<other> is to
40 * switch between 'VEHICLE' parameter use and general dynamics
41 * settings use.
42 */ 31 */
43 32
44using System; 33using System;
45using System.Collections.Generic; 34using System.Collections.Generic;
46using System.Reflection; 35using System.Reflection;
47using System.Runtime.InteropServices; 36using System.Runtime.InteropServices;
48using log4net;
49using OpenMetaverse; 37using OpenMetaverse;
50using OpenSim.Framework;
51using OpenSim.Region.Physics.Manager; 38using OpenSim.Region.Physics.Manager;
52 39
53namespace OpenSim.Region.Physics.BulletSPlugin 40namespace OpenSim.Region.Physics.BulletSPlugin
@@ -100,7 +87,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
100 private float m_angularMotorTimescale = 0; // motor angular velocity ramp up rate 87 private float m_angularMotorTimescale = 0; // motor angular velocity ramp up rate
101 private float m_angularMotorDecayTimescale = 0; // motor angular velocity decay rate 88 private float m_angularMotorDecayTimescale = 0; // motor angular velocity decay rate
102 private Vector3 m_angularFrictionTimescale = Vector3.Zero; // body angular velocity decay rate 89 private Vector3 m_angularFrictionTimescale = Vector3.Zero; // body angular velocity decay rate
103 private Vector3 m_lastAngularVelocity = Vector3.Zero; // what was last applied to body 90 private Vector3 m_lastAngularCorrection = Vector3.Zero;
104 private Vector3 m_lastVertAttractor = Vector3.Zero; // what VA was last applied to body 91 private Vector3 m_lastVertAttractor = Vector3.Zero; // what VA was last applied to body
105 92
106 //Deflection properties 93 //Deflection properties
@@ -125,8 +112,11 @@ namespace OpenSim.Region.Physics.BulletSPlugin
125 // Therefore only m_VehicleBuoyancy=1 (0g) will use the script-requested .Z velocity. 112 // Therefore only m_VehicleBuoyancy=1 (0g) will use the script-requested .Z velocity.
126 113
127 //Attractor properties 114 //Attractor properties
128 private float m_verticalAttractionEfficiency = 1.0f; // damped 115 private BSVMotor m_verticalAttractionMotor = new BSVMotor("VerticalAttraction");
129 private float m_verticalAttractionTimescale = 500f; // Timescale > 300 means no vert attractor. 116 private float m_verticalAttractionEfficiency = 1.0f; // damped
117 private float m_verticalAttractionCutoff = 500f; // per the documentation
118 // Timescale > cutoff means no vert attractor.
119 private float m_verticalAttractionTimescale = 510f;
130 120
131 public BSDynamics(BSScene myScene, BSPrim myPrim) 121 public BSDynamics(BSScene myScene, BSPrim myPrim)
132 { 122 {
@@ -153,7 +143,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
153 m_angularDeflectionTimescale = Math.Max(pValue, 0.01f); 143 m_angularDeflectionTimescale = Math.Max(pValue, 0.01f);
154 break; 144 break;
155 case Vehicle.ANGULAR_MOTOR_DECAY_TIMESCALE: 145 case Vehicle.ANGULAR_MOTOR_DECAY_TIMESCALE:
156 m_angularMotorDecayTimescale = Math.Max(0.01f, Math.Min(pValue,120)); 146 m_angularMotorDecayTimescale = ClampInRange(0.01f, pValue, 120);
157 m_angularMotor.TargetValueDecayTimeScale = m_angularMotorDecayTimescale; 147 m_angularMotor.TargetValueDecayTimeScale = m_angularMotorDecayTimescale;
158 break; 148 break;
159 case Vehicle.ANGULAR_MOTOR_TIMESCALE: 149 case Vehicle.ANGULAR_MOTOR_TIMESCALE:
@@ -161,7 +151,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
161 m_angularMotor.TimeScale = m_angularMotorTimescale; 151 m_angularMotor.TimeScale = m_angularMotorTimescale;
162 break; 152 break;
163 case Vehicle.BANKING_EFFICIENCY: 153 case Vehicle.BANKING_EFFICIENCY:
164 m_bankingEfficiency = Math.Max(-1f, Math.Min(pValue, 1f)); 154 m_bankingEfficiency = ClampInRange(-1f, pValue, 1f);
165 break; 155 break;
166 case Vehicle.BANKING_MIX: 156 case Vehicle.BANKING_MIX:
167 m_bankingMix = Math.Max(pValue, 0.01f); 157 m_bankingMix = Math.Max(pValue, 0.01f);
@@ -170,10 +160,10 @@ namespace OpenSim.Region.Physics.BulletSPlugin
170 m_bankingTimescale = Math.Max(pValue, 0.01f); 160 m_bankingTimescale = Math.Max(pValue, 0.01f);
171 break; 161 break;
172 case Vehicle.BUOYANCY: 162 case Vehicle.BUOYANCY:
173 m_VehicleBuoyancy = Math.Max(-1f, Math.Min(pValue, 1f)); 163 m_VehicleBuoyancy = ClampInRange(-1f, pValue, 1f);
174 break; 164 break;
175 case Vehicle.HOVER_EFFICIENCY: 165 case Vehicle.HOVER_EFFICIENCY:
176 m_VhoverEfficiency = Math.Max(0f, Math.Min(pValue, 1f)); 166 m_VhoverEfficiency = ClampInRange(0f, pValue, 1f);
177 break; 167 break;
178 case Vehicle.HOVER_HEIGHT: 168 case Vehicle.HOVER_HEIGHT:
179 m_VhoverHeight = pValue; 169 m_VhoverHeight = pValue;
@@ -188,7 +178,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
188 m_linearDeflectionTimescale = Math.Max(pValue, 0.01f); 178 m_linearDeflectionTimescale = Math.Max(pValue, 0.01f);
189 break; 179 break;
190 case Vehicle.LINEAR_MOTOR_DECAY_TIMESCALE: 180 case Vehicle.LINEAR_MOTOR_DECAY_TIMESCALE:
191 m_linearMotorDecayTimescale = Math.Max(0.01f, Math.Min(pValue,120)); 181 m_linearMotorDecayTimescale = ClampInRange(0.01f, pValue, 120);
192 m_linearMotor.TargetValueDecayTimeScale = m_linearMotorDecayTimescale; 182 m_linearMotor.TargetValueDecayTimeScale = m_linearMotorDecayTimescale;
193 break; 183 break;
194 case Vehicle.LINEAR_MOTOR_TIMESCALE: 184 case Vehicle.LINEAR_MOTOR_TIMESCALE:
@@ -196,10 +186,12 @@ namespace OpenSim.Region.Physics.BulletSPlugin
196 m_linearMotor.TimeScale = m_linearMotorTimescale; 186 m_linearMotor.TimeScale = m_linearMotorTimescale;
197 break; 187 break;
198 case Vehicle.VERTICAL_ATTRACTION_EFFICIENCY: 188 case Vehicle.VERTICAL_ATTRACTION_EFFICIENCY:
199 m_verticalAttractionEfficiency = Math.Max(0.1f, Math.Min(pValue, 1f)); 189 m_verticalAttractionEfficiency = ClampInRange(0.1f, pValue, 1f);
190 m_verticalAttractionMotor.Efficiency = m_verticalAttractionEfficiency;
200 break; 191 break;
201 case Vehicle.VERTICAL_ATTRACTION_TIMESCALE: 192 case Vehicle.VERTICAL_ATTRACTION_TIMESCALE:
202 m_verticalAttractionTimescale = Math.Max(pValue, 0.01f); 193 m_verticalAttractionTimescale = Math.Max(pValue, 0.01f);
194 m_verticalAttractionMotor.TimeScale = m_verticalAttractionTimescale;
203 break; 195 break;
204 196
205 // These are vector properties but the engine lets you use a single float value to 197 // These are vector properties but the engine lets you use a single float value to
@@ -239,9 +231,9 @@ namespace OpenSim.Region.Physics.BulletSPlugin
239 break; 231 break;
240 case Vehicle.ANGULAR_MOTOR_DIRECTION: 232 case Vehicle.ANGULAR_MOTOR_DIRECTION:
241 // Limit requested angular speed to 2 rps= 4 pi rads/sec 233 // Limit requested angular speed to 2 rps= 4 pi rads/sec
242 pValue.X = Math.Max(-12.56f, Math.Min(pValue.X, 12.56f)); 234 pValue.X = ClampInRange(-12.56f, pValue.X, 12.56f);
243 pValue.Y = Math.Max(-12.56f, Math.Min(pValue.Y, 12.56f)); 235 pValue.Y = ClampInRange(-12.56f, pValue.Y, 12.56f);
244 pValue.Z = Math.Max(-12.56f, Math.Min(pValue.Z, 12.56f)); 236 pValue.Z = ClampInRange(-12.56f, pValue.Z, 12.56f);
245 m_angularMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z); 237 m_angularMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z);
246 m_angularMotor.SetTarget(m_angularMotorDirection); 238 m_angularMotor.SetTarget(m_angularMotorDirection);
247 break; 239 break;
@@ -314,7 +306,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
314 m_VhoverEfficiency = 0; 306 m_VhoverEfficiency = 0;
315 m_VhoverTimescale = 0; 307 m_VhoverTimescale = 0;
316 m_VehicleBuoyancy = 0; 308 m_VehicleBuoyancy = 0;
317 309
318 m_linearDeflectionEfficiency = 1; 310 m_linearDeflectionEfficiency = 1;
319 m_linearDeflectionTimescale = 1; 311 m_linearDeflectionTimescale = 1;
320 312
@@ -363,13 +355,14 @@ namespace OpenSim.Region.Physics.BulletSPlugin
363 m_bankingMix = 1; 355 m_bankingMix = 1;
364 356
365 m_referenceFrame = Quaternion.Identity; 357 m_referenceFrame = Quaternion.Identity;
366 m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY 358 m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY
367 | VehicleFlag.HOVER_TERRAIN_ONLY 359 | VehicleFlag.HOVER_TERRAIN_ONLY
368 | VehicleFlag.HOVER_GLOBAL_HEIGHT 360 | VehicleFlag.HOVER_GLOBAL_HEIGHT
369 | VehicleFlag.HOVER_UP_ONLY); 361 | VehicleFlag.HOVER_UP_ONLY);
370 m_flags |= (VehicleFlag.NO_DEFLECTION_UP 362 m_flags |= (VehicleFlag.NO_DEFLECTION_UP
371 | VehicleFlag.LIMIT_ROLL_ONLY 363 | VehicleFlag.LIMIT_ROLL_ONLY
372 | VehicleFlag.LIMIT_MOTOR_UP); 364 | VehicleFlag.LIMIT_MOTOR_UP);
365
373 break; 366 break;
374 case Vehicle.TYPE_CAR: 367 case Vehicle.TYPE_CAR:
375 m_linearMotorDirection = Vector3.Zero; 368 m_linearMotorDirection = Vector3.Zero;
@@ -513,6 +506,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
513 m_bankingEfficiency = 0; 506 m_bankingEfficiency = 0;
514 m_bankingMix = 0.7f; 507 m_bankingMix = 0.7f;
515 m_bankingTimescale = 5; 508 m_bankingTimescale = 5;
509
516 m_referenceFrame = Quaternion.Identity; 510 m_referenceFrame = Quaternion.Identity;
517 511
518 m_referenceFrame = Quaternion.Identity; 512 m_referenceFrame = Quaternion.Identity;
@@ -530,13 +524,21 @@ namespace OpenSim.Region.Physics.BulletSPlugin
530 Refresh(); 524 Refresh();
531 525
532 m_linearMotor = new BSVMotor("LinearMotor", m_linearMotorTimescale, 526 m_linearMotor = new BSVMotor("LinearMotor", m_linearMotorTimescale,
533 m_linearMotorDecayTimescale, m_linearFrictionTimescale, 1f); 527 m_linearMotorDecayTimescale, m_linearFrictionTimescale,
528 1f);
534 m_linearMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG DEBUG (enables detail logging) 529 m_linearMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG DEBUG (enables detail logging)
530
535 m_angularMotor = new BSVMotor("AngularMotor", m_angularMotorTimescale, 531 m_angularMotor = new BSVMotor("AngularMotor", m_angularMotorTimescale,
536 m_angularMotorDecayTimescale, m_angularFrictionTimescale, 1f); 532 m_angularMotorDecayTimescale, m_angularFrictionTimescale,
533 1f);
537 m_angularMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG DEBUG (enables detail logging) 534 m_angularMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG DEBUG (enables detail logging)
538 535
539 // m_bankingMotor = new BSVMotor("BankingMotor", ...); 536 m_verticalAttractionMotor = new BSVMotor("VerticalAttraction", m_verticalAttractionTimescale,
537 BSMotor.Infinite, BSMotor.InfiniteVector,
538 m_verticalAttractionEfficiency);
539 // Z goes away and we keep X and Y
540 m_verticalAttractionMotor.FrictionTimescale = new Vector3(BSMotor.Infinite, BSMotor.Infinite, 0.1f);
541 m_verticalAttractionMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG DEBUG (enables detail logging)
540 } 542 }
541 543
542 // Some of the properties of this prim may have changed. 544 // Some of the properties of this prim may have changed.
@@ -545,9 +547,10 @@ namespace OpenSim.Region.Physics.BulletSPlugin
545 { 547 {
546 if (IsActive) 548 if (IsActive)
547 { 549 {
550 // Remember the mass so we don't have to fetch it every step
548 m_vehicleMass = Prim.Linkset.LinksetMass; 551 m_vehicleMass = Prim.Linkset.LinksetMass;
549 552
550 // Friction effects are handled by this vehicle code 553 // Friction affects are handled by this vehicle code
551 float friction = 0f; 554 float friction = 0f;
552 BulletSimAPI.SetFriction2(Prim.PhysBody.ptr, friction); 555 BulletSimAPI.SetFriction2(Prim.PhysBody.ptr, friction);
553 556
@@ -557,14 +560,23 @@ namespace OpenSim.Region.Physics.BulletSPlugin
557 float angularDamping = PhysicsScene.Params.vehicleAngularDamping; 560 float angularDamping = PhysicsScene.Params.vehicleAngularDamping;
558 BulletSimAPI.SetAngularDamping2(Prim.PhysBody.ptr, angularDamping); 561 BulletSimAPI.SetAngularDamping2(Prim.PhysBody.ptr, angularDamping);
559 562
563 // Vehicles report collision events so we know when it's on the ground
564 BulletSimAPI.AddToCollisionFlags2(Prim.PhysBody.ptr, CollisionFlags.BS_VEHICLE_COLLISIONS);
565
560 // DEBUG DEBUG DEBUG: use uniform inertia to smooth movement added by Bullet 566 // DEBUG DEBUG DEBUG: use uniform inertia to smooth movement added by Bullet
561 // Vector3 localInertia = new Vector3(1f, 1f, 1f); 567 // Vector3 localInertia = new Vector3(1f, 1f, 1f);
562 Vector3 localInertia = new Vector3(m_vehicleMass, m_vehicleMass, m_vehicleMass); 568 // Vector3 localInertia = new Vector3(m_vehicleMass, m_vehicleMass, m_vehicleMass);
569 Vector3 localInertia = BulletSimAPI.CalculateLocalInertia2(Prim.PhysShape.ptr, m_vehicleMass);
563 BulletSimAPI.SetMassProps2(Prim.PhysBody.ptr, m_vehicleMass, localInertia); 570 BulletSimAPI.SetMassProps2(Prim.PhysBody.ptr, m_vehicleMass, localInertia);
571 BulletSimAPI.UpdateInertiaTensor2(Prim.PhysBody.ptr);
564 572
565 VDetailLog("{0},BSDynamics.Refresh,frict={1},inert={2},aDamp={3}", 573 VDetailLog("{0},BSDynamics.Refresh,frict={1},inert={2},aDamp={3}",
566 Prim.LocalID, friction, localInertia, angularDamping); 574 Prim.LocalID, friction, localInertia, angularDamping);
567 } 575 }
576 else
577 {
578 BulletSimAPI.RemoveFromCollisionFlags2(Prim.PhysBody.ptr, CollisionFlags.BS_VEHICLE_COLLISIONS);
579 }
568 } 580 }
569 581
570 public bool RemoveBodyDependencies(BSPhysObject prim) 582 public bool RemoveBodyDependencies(BSPhysObject prim)
@@ -585,60 +597,288 @@ namespace OpenSim.Region.Physics.BulletSPlugin
585 Refresh(); 597 Refresh();
586 } 598 }
587 599
600 #region Known vehicle value functions
601 // Vehicle physical parameters that we buffer from constant getting and setting.
602 // The "m_known*" variables are initialized to 'null', fetched only if referenced
603 // and stored back into the physics engine only if updated.
604 // This does two things: 1) saves continuious calls into unmanaged code, and
605 // 2) signals when a physics property update must happen back to the simulator
606 // to update values modified for the vehicle.
607 private int m_knownChanged;
608 private float? m_knownTerrainHeight;
609 private float? m_knownWaterLevel;
610 private Vector3? m_knownPosition;
611 private Vector3? m_knownVelocity;
612 private Vector3 m_knownForce;
613 private Quaternion? m_knownOrientation;
614 private Vector3? m_knownRotationalVelocity;
615 private Vector3 m_knownRotationalForce;
616 private float? m_knownForwardSpeed;
617
618 private const int m_knownChangedPosition = 1 << 0;
619 private const int m_knownChangedVelocity = 1 << 1;
620 private const int m_knownChangedForce = 1 << 2;
621 private const int m_knownChangedOrientation = 1 << 3;
622 private const int m_knownChangedRotationalVelocity = 1 << 4;
623 private const int m_knownChangedRotationalForce = 1 << 5;
624
625 private void ForgetKnownVehicleProperties()
626 {
627 m_knownTerrainHeight = null;
628 m_knownWaterLevel = null;
629 m_knownPosition = null;
630 m_knownVelocity = null;
631 m_knownForce = Vector3.Zero;
632 m_knownOrientation = null;
633 m_knownRotationalVelocity = null;
634 m_knownRotationalForce = Vector3.Zero;
635 m_knownForwardSpeed = null;
636 m_knownChanged = 0;
637 }
638 private void PushKnownChanged()
639 {
640 if (m_knownChanged != 0)
641 {
642 if ((m_knownChanged & m_knownChangedPosition) != 0)
643 Prim.ForcePosition = VehiclePosition;
644 if ((m_knownChanged & m_knownChangedOrientation) != 0)
645 Prim.ForceOrientation = VehicleOrientation;
646 if ((m_knownChanged & m_knownChangedVelocity) != 0)
647 {
648 Prim.ForceVelocity = VehicleVelocity;
649 BulletSimAPI.SetInterpolationLinearVelocity2(Prim.PhysBody.ptr, VehicleVelocity);
650 }
651 if ((m_knownChanged & m_knownChangedForce) != 0)
652 Prim.AddForce((Vector3)m_knownForce, false, true);
653
654 if ((m_knownChanged & m_knownChangedRotationalVelocity) != 0)
655 {
656 Prim.ForceRotationalVelocity = VehicleRotationalVelocity;
657 // Fake out Bullet by making it think the velocity is the same as last time.
658 BulletSimAPI.SetInterpolationAngularVelocity2(Prim.PhysBody.ptr, VehicleRotationalVelocity);
659 }
660 if ((m_knownChanged & m_knownChangedRotationalForce) != 0)
661 Prim.AddAngularForce((Vector3)m_knownRotationalForce, false, true);
662
663 // If we set one of the values (ie, the physics engine didn't do it) we must force
664 // an UpdateProperties event to send the changes up to the simulator.
665 BulletSimAPI.PushUpdate2(Prim.PhysBody.ptr);
666 }
667 }
668
669 // Since the computation of terrain height can be a little involved, this routine
670 // is used ot fetch the height only once for each vehicle simulation step.
671 private float GetTerrainHeight(Vector3 pos)
672 {
673 if (m_knownTerrainHeight == null)
674 m_knownTerrainHeight = Prim.PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(pos);
675 return (float)m_knownTerrainHeight;
676 }
677
678 // Since the computation of water level can be a little involved, this routine
679 // is used ot fetch the level only once for each vehicle simulation step.
680 private float GetWaterLevel(Vector3 pos)
681 {
682 if (m_knownWaterLevel == null)
683 m_knownWaterLevel = Prim.PhysicsScene.TerrainManager.GetWaterLevelAtXYZ(pos);
684 return (float)m_knownWaterLevel;
685 }
686
687 private Vector3 VehiclePosition
688 {
689 get
690 {
691 if (m_knownPosition == null)
692 m_knownPosition = Prim.ForcePosition;
693 return (Vector3)m_knownPosition;
694 }
695 set
696 {
697 m_knownPosition = value;
698 m_knownChanged |= m_knownChangedPosition;
699 }
700 }
701
702 private Quaternion VehicleOrientation
703 {
704 get
705 {
706 if (m_knownOrientation == null)
707 m_knownOrientation = Prim.ForceOrientation;
708 return (Quaternion)m_knownOrientation;
709 }
710 set
711 {
712 m_knownOrientation = value;
713 m_knownChanged |= m_knownChangedOrientation;
714 }
715 }
716
717 private Vector3 VehicleVelocity
718 {
719 get
720 {
721 if (m_knownVelocity == null)
722 m_knownVelocity = Prim.ForceVelocity;
723 return (Vector3)m_knownVelocity;
724 }
725 set
726 {
727 m_knownVelocity = value;
728 m_knownChanged |= m_knownChangedVelocity;
729 }
730 }
731
732 private void VehicleAddForce(Vector3 aForce)
733 {
734 m_knownForce += aForce;
735 m_knownChanged |= m_knownChangedForce;
736 }
737
738 private Vector3 VehicleRotationalVelocity
739 {
740 get
741 {
742 if (m_knownRotationalVelocity == null)
743 m_knownRotationalVelocity = Prim.ForceRotationalVelocity;
744 return (Vector3)m_knownRotationalVelocity;
745 }
746 set
747 {
748 m_knownRotationalVelocity = value;
749 m_knownChanged |= m_knownChangedRotationalVelocity;
750 }
751 }
752 private void VehicleAddAngularForce(Vector3 aForce)
753 {
754 m_knownRotationalForce += aForce;
755 m_knownChanged |= m_knownChangedRotationalForce;
756 }
757 private float VehicleForwardSpeed
758 {
759 get
760 {
761 if (m_knownForwardSpeed == null)
762 m_knownForwardSpeed = (VehicleVelocity * Quaternion.Inverse(VehicleOrientation)).X;
763 return (float)m_knownForwardSpeed;
764 }
765 }
766
767 #endregion // Known vehicle value functions
768
588 // One step of the vehicle properties for the next 'pTimestep' seconds. 769 // One step of the vehicle properties for the next 'pTimestep' seconds.
589 internal void Step(float pTimestep) 770 internal void Step(float pTimestep)
590 { 771 {
591 if (!IsActive) return; 772 if (!IsActive) return;
592 773
774 ForgetKnownVehicleProperties();
775
593 MoveLinear(pTimestep); 776 MoveLinear(pTimestep);
594 MoveAngular(pTimestep); 777 MoveAngular(pTimestep);
595 778
596 LimitRotation(pTimestep); 779 LimitRotation(pTimestep);
597 780
598 // remember the position so next step we can limit absolute movement effects 781 // remember the position so next step we can limit absolute movement effects
599 m_lastPositionVector = Prim.ForcePosition; 782 m_lastPositionVector = VehiclePosition;
783
784 // If we forced the changing of some vehicle parameters, update the values and
785 // for the physics engine to note the changes so an UpdateProperties event will happen.
786 PushKnownChanged();
600 787
601 VDetailLog("{0},BSDynamics.Step,done,pos={1},force={2},velocity={3},angvel={4}", 788 VDetailLog("{0},BSDynamics.Step,done,pos={1},force={2},velocity={3},angvel={4}",
602 Prim.LocalID, Prim.ForcePosition, Prim.Force, Prim.ForceVelocity, Prim.RotationalVelocity); 789 Prim.LocalID, VehiclePosition, Prim.Force, VehicleVelocity, VehicleRotationalVelocity);
603 } 790 }
604 791
605 // Apply the effect of the linear motor. 792 // Apply the effect of the linear motor and other linear motions (like hover and float).
606 // Also does hover and float.
607 private void MoveLinear(float pTimestep) 793 private void MoveLinear(float pTimestep)
608 { 794 {
609 Vector3 linearMotorContribution = m_linearMotor.Step(pTimestep); 795 Vector3 linearMotorContribution = m_linearMotor.Step(pTimestep);
610 796
611 // Rotate new object velocity from vehicle relative to world coordinates 797 // The movement computed in the linear motor is relative to the vehicle
612 linearMotorContribution *= Prim.ForceOrientation; 798 // coordinates. Rotate the movement to world coordinates.
799 linearMotorContribution *= VehicleOrientation;
613 800
614 // ================================================================== 801 // ==================================================================
615 // Gravity and Buoyancy 802 // Buoyancy: force to overcome gravity.
616 // There is some gravity, make a gravity force vector that is applied after object velocity.
617 // m_VehicleBuoyancy: -1=2g; 0=1g; 1=0g; 803 // m_VehicleBuoyancy: -1=2g; 0=1g; 1=0g;
618 Vector3 grav = Prim.PhysicsScene.DefaultGravity * (1f - m_VehicleBuoyancy); 804 // So, if zero, don't change anything (let gravity happen). If one, negate the effect of gravity.
805 Vector3 buoyancyContribution = Prim.PhysicsScene.DefaultGravity * m_VehicleBuoyancy;
806
807 Vector3 terrainHeightContribution = ComputeLinearTerrainHeightCorrection(pTimestep);
808
809 Vector3 hoverContribution = ComputeLinearHover(pTimestep);
810
811 ComputeLinearBlockingEndPoint(pTimestep);
812
813 Vector3 limitMotorUpContribution = ComputeLinearMotorUp(pTimestep);
814
815 // ==================================================================
816 Vector3 newVelocity = linearMotorContribution
817 + terrainHeightContribution
818 + hoverContribution
819 + limitMotorUpContribution;
820
821 // If not changing some axis, reduce out velocity
822 if ((m_flags & (VehicleFlag.NO_X)) != 0)
823 newVelocity.X = 0;
824 if ((m_flags & (VehicleFlag.NO_Y)) != 0)
825 newVelocity.Y = 0;
826 if ((m_flags & (VehicleFlag.NO_Z)) != 0)
827 newVelocity.Z = 0;
619 828
620 // Current vehicle position 829 // ==================================================================
621 Vector3 pos = Prim.ForcePosition; 830 // Clamp high or low velocities
831 float newVelocityLengthSq = newVelocity.LengthSquared();
832 // if (newVelocityLengthSq > 1e6f)
833 if (newVelocityLengthSq > 1000f)
834 {
835 newVelocity /= newVelocity.Length();
836 newVelocity *= 1000f;
837 }
838 // else if (newVelocityLengthSq < 1e-6f)
839 else if (newVelocityLengthSq < 0.001f)
840 newVelocity = Vector3.Zero;
622 841
623 // ================================================================== 842 // ==================================================================
624 Vector3 terrainHeightContribution = Vector3.Zero; 843 // Stuff new linear velocity into the vehicle.
844 // Since the velocity is just being set, it is not scaled by pTimeStep. Bullet will do that for us.
845 VehicleVelocity = newVelocity;
846
847 // Other linear forces are applied as forces.
848 Vector3 totalDownForce = buoyancyContribution * m_vehicleMass;
849 if (!totalDownForce.ApproxEquals(Vector3.Zero, 0.01f))
850 {
851 VehicleAddForce(totalDownForce);
852 }
853
854 VDetailLog("{0}, MoveLinear,done,newVel={1},totDown={2},IsColliding={3}",
855 Prim.LocalID, newVelocity, totalDownForce, Prim.IsColliding);
856 VDetailLog("{0}, MoveLinear,done,linContrib={1},terrContrib={2},hoverContrib={3},limitContrib={4},buoyContrib={5}",
857 Prim.LocalID,
858 linearMotorContribution, terrainHeightContribution, hoverContribution,
859 limitMotorUpContribution, buoyancyContribution
860 );
861
862 } // end MoveLinear()
863
864 public Vector3 ComputeLinearTerrainHeightCorrection(float pTimestep)
865 {
866 Vector3 ret = Vector3.Zero;
625 // If below the terrain, move us above the ground a little. 867 // If below the terrain, move us above the ground a little.
626 float terrainHeight = Prim.PhysicsScene.TerrainManager.GetTerrainHeightAtXYZ(pos); 868 // TODO: Consider taking the rotated size of the object or possibly casting a ray.
627 // Taking the rotated size doesn't work here because m_prim.Size is the size of the root prim and not the linkset. 869 if (VehiclePosition.Z < GetTerrainHeight(VehiclePosition))
628 // TODO: Add a m_prim.LinkSet.Size similar to m_prim.LinkSet.Mass.
629 // Vector3 rotatedSize = m_prim.Size * m_prim.ForceOrientation;
630 // if (rotatedSize.Z < terrainHeight)
631 if (pos.Z < terrainHeight)
632 { 870 {
633 // TODO: correct position by applying force rather than forcing position. 871 // TODO: correct position by applying force rather than forcing position.
634 pos.Z = terrainHeight + 2; 872 VehiclePosition += new Vector3(0f, 0f, GetTerrainHeight(VehiclePosition) + 2f);
635 Prim.ForcePosition = pos; 873 VDetailLog("{0}, MoveLinear,terrainHeight,terrainHeight={1},pos={2}", Prim.LocalID, GetTerrainHeight(VehiclePosition), VehiclePosition);
636 VDetailLog("{0},MoveLinear,terrainHeight,terrainHeight={1},pos={2}", Prim.LocalID, terrainHeight, pos);
637 } 874 }
875 return ret;
876 }
877
878 public Vector3 ComputeLinearHover(float pTimestep)
879 {
880 Vector3 ret = Vector3.Zero;
638 881
639 // ==================================================================
640 Vector3 hoverContribution = Vector3.Zero;
641 // Check if hovering
642 // m_VhoverEfficiency: 0=bouncy, 1=totally damped 882 // m_VhoverEfficiency: 0=bouncy, 1=totally damped
643 // m_VhoverTimescale: time to achieve height 883 // m_VhoverTimescale: time to achieve height
644 if ((m_flags & (VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.HOVER_GLOBAL_HEIGHT)) != 0) 884 if ((m_flags & (VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.HOVER_GLOBAL_HEIGHT)) != 0)
@@ -646,11 +886,11 @@ namespace OpenSim.Region.Physics.BulletSPlugin
646 // We should hover, get the target height 886 // We should hover, get the target height
647 if ((m_flags & VehicleFlag.HOVER_WATER_ONLY) != 0) 887 if ((m_flags & VehicleFlag.HOVER_WATER_ONLY) != 0)
648 { 888 {
649 m_VhoverTargetHeight = Prim.PhysicsScene.GetWaterLevelAtXYZ(pos) + m_VhoverHeight; 889 m_VhoverTargetHeight = GetWaterLevel(VehiclePosition) + m_VhoverHeight;
650 } 890 }
651 if ((m_flags & VehicleFlag.HOVER_TERRAIN_ONLY) != 0) 891 if ((m_flags & VehicleFlag.HOVER_TERRAIN_ONLY) != 0)
652 { 892 {
653 m_VhoverTargetHeight = terrainHeight + m_VhoverHeight; 893 m_VhoverTargetHeight = GetTerrainHeight(VehiclePosition) + m_VhoverHeight;
654 } 894 }
655 if ((m_flags & VehicleFlag.HOVER_GLOBAL_HEIGHT) != 0) 895 if ((m_flags & VehicleFlag.HOVER_GLOBAL_HEIGHT) != 0)
656 { 896 {
@@ -660,43 +900,47 @@ namespace OpenSim.Region.Physics.BulletSPlugin
660 if ((m_flags & VehicleFlag.HOVER_UP_ONLY) != 0) 900 if ((m_flags & VehicleFlag.HOVER_UP_ONLY) != 0)
661 { 901 {
662 // If body is already heigher, use its height as target height 902 // If body is already heigher, use its height as target height
663 if (pos.Z > m_VhoverTargetHeight) 903 if (VehiclePosition.Z > m_VhoverTargetHeight)
664 m_VhoverTargetHeight = pos.Z; 904 m_VhoverTargetHeight = VehiclePosition.Z;
665 } 905 }
906
666 if ((m_flags & VehicleFlag.LOCK_HOVER_HEIGHT) != 0) 907 if ((m_flags & VehicleFlag.LOCK_HOVER_HEIGHT) != 0)
667 { 908 {
668 if (Math.Abs(pos.Z - m_VhoverTargetHeight) > 0.2f) 909 if (Math.Abs(VehiclePosition.Z - m_VhoverTargetHeight) > 0.2f)
669 { 910 {
911 Vector3 pos = VehiclePosition;
670 pos.Z = m_VhoverTargetHeight; 912 pos.Z = m_VhoverTargetHeight;
671 Prim.ForcePosition = pos; 913 VehiclePosition = pos;
672 } 914 }
673 } 915 }
674 else 916 else
675 { 917 {
676 float verticalError = pos.Z - m_VhoverTargetHeight; 918 // Error is positive if below the target and negative if above.
677 // RA: where does the 50 come from? 919 float verticalError = m_VhoverTargetHeight - VehiclePosition.Z;
678 float verticalCorrectionVelocity = pTimestep * ((verticalError * 50.0f) / m_VhoverTimescale); 920 float verticalCorrectionVelocity = verticalError / m_VhoverTimescale;
679 // Replace Vertical speed with correction figure if significant 921
680 if (verticalError > 0.01f) 922 // TODO: implement m_VhoverEfficiency correctly
681 { 923 if (Math.Abs(verticalError) > m_VhoverEfficiency)
682 hoverContribution = new Vector3(0f, 0f, verticalCorrectionVelocity);
683 //KF: m_VhoverEfficiency is not yet implemented
684 }
685 else if (verticalError < -0.01)
686 { 924 {
687 hoverContribution = new Vector3(0f, 0f, -verticalCorrectionVelocity); 925 ret = new Vector3(0f, 0f, verticalCorrectionVelocity);
688 } 926 }
689 } 927 }
690 928
691 VDetailLog("{0},MoveLinear,hover,pos={1},dir={2},height={3},target={4}", 929 VDetailLog("{0}, MoveLinear,hover,pos={1},ret={2},hoverTS={3},height={4},target={5}",
692 Prim.LocalID, pos, hoverContribution, m_VhoverHeight, m_VhoverTargetHeight); 930 Prim.LocalID, VehiclePosition, ret, m_VhoverTimescale, m_VhoverHeight, m_VhoverTargetHeight);
693 } 931 }
694 932
695 // ================================================================== 933 return ret;
934 }
935
936 public bool ComputeLinearBlockingEndPoint(float pTimestep)
937 {
938 bool changed = false;
939
940 Vector3 pos = VehiclePosition;
696 Vector3 posChange = pos - m_lastPositionVector; 941 Vector3 posChange = pos - m_lastPositionVector;
697 if (m_BlockingEndPoint != Vector3.Zero) 942 if (m_BlockingEndPoint != Vector3.Zero)
698 { 943 {
699 bool changed = false;
700 if (pos.X >= (m_BlockingEndPoint.X - (float)1)) 944 if (pos.X >= (m_BlockingEndPoint.X - (float)1))
701 { 945 {
702 pos.X -= posChange.X + 1; 946 pos.X -= posChange.X + 1;
@@ -724,240 +968,111 @@ namespace OpenSim.Region.Physics.BulletSPlugin
724 } 968 }
725 if (changed) 969 if (changed)
726 { 970 {
727 Prim.ForcePosition = pos; 971 VehiclePosition = pos;
728 VDetailLog("{0},MoveLinear,blockingEndPoint,block={1},origPos={2},pos={3}", 972 VDetailLog("{0}, MoveLinear,blockingEndPoint,block={1},origPos={2},pos={3}",
729 Prim.LocalID, m_BlockingEndPoint, posChange, pos); 973 Prim.LocalID, m_BlockingEndPoint, posChange, pos);
730 } 974 }
731 } 975 }
976 return changed;
977 }
978
979 // From http://wiki.secondlife.com/wiki/LlSetVehicleFlags :
980 // Prevent ground vehicles from motoring into the sky. This flag has a subtle effect when
981 // used with conjunction with banking: the strength of the banking will decay when the
982 // vehicle no longer experiences collisions. The decay timescale is the same as
983 // VEHICLE_BANKING_TIMESCALE. This is to help prevent ground vehicles from steering
984 // when they are in mid jump.
985 // TODO: this code is wrong. Also, what should it do for boats (height from water)?
986 // This is just using the ground and a general collision check. Should really be using
987 // a downward raycast to find what is below.
988 public Vector3 ComputeLinearMotorUp(float pTimestep)
989 {
990 Vector3 ret = Vector3.Zero;
732 991
733 // ==================================================================
734 Vector3 limitMotorUpContribution = Vector3.Zero;
735 if ((m_flags & (VehicleFlag.LIMIT_MOTOR_UP)) != 0) 992 if ((m_flags & (VehicleFlag.LIMIT_MOTOR_UP)) != 0)
736 { 993 {
737 // If the vehicle is motoring into the sky, get it going back down. 994 // If the vehicle is motoring into the sky, get it going back down.
738 float distanceAboveGround = pos.Z - terrainHeight; 995 float distanceAboveGround = VehiclePosition.Z - GetTerrainHeight(VehiclePosition);
739 if (distanceAboveGround > 1f) 996 // Not colliding if the vehicle is off the ground
997 if (!Prim.IsColliding)
740 { 998 {
741 // downForce = new Vector3(0, 0, (-distanceAboveGround / m_bankingTimescale) * pTimestep); 999 // downForce = new Vector3(0, 0, (-distanceAboveGround / m_bankingTimescale) * pTimestep);
742 // downForce = new Vector3(0, 0, -distanceAboveGround / m_bankingTimescale); 1000 // downForce = new Vector3(0, 0, -distanceAboveGround / m_bankingTimescale);
743 limitMotorUpContribution = new Vector3(0, 0, -distanceAboveGround); 1001 ret = new Vector3(0, 0, -distanceAboveGround);
744 } 1002 }
745 // TODO: this calculation is all wrong. From the description at 1003 // TODO: this calculation is wrong. From the description at
746 // (http://wiki.secondlife.com/wiki/Category:LSL_Vehicle), the downForce 1004 // (http://wiki.secondlife.com/wiki/Category:LSL_Vehicle), the downForce
747 // has a decay factor. This says this force should 1005 // has a decay factor. This says this force should
748 // be computed with a motor. 1006 // be computed with a motor.
749 VDetailLog("{0},MoveLinear,limitMotorUp,distAbove={1},downForce={2}", 1007 // TODO: add interaction with banking.
750 Prim.LocalID, distanceAboveGround, limitMotorUpContribution); 1008 VDetailLog("{0}, MoveLinear,limitMotorUp,distAbove={1},downForce={2}",
751 } 1009 Prim.LocalID, distanceAboveGround, ret);
752
753 // ==================================================================
754 Vector3 newVelocity = linearMotorContribution
755 + terrainHeightContribution
756 + hoverContribution
757 + limitMotorUpContribution;
758
759 // If not changing some axis, reduce out velocity
760 if ((m_flags & (VehicleFlag.NO_X)) != 0)
761 newVelocity.X = 0;
762 if ((m_flags & (VehicleFlag.NO_Y)) != 0)
763 newVelocity.Y = 0;
764 if ((m_flags & (VehicleFlag.NO_Z)) != 0)
765 newVelocity.Z = 0;
766
767 // ==================================================================
768 // Clamp REALLY high or low velocities
769 float newVelocityLengthSq = newVelocity.LengthSquared();
770 if (newVelocityLengthSq > 1e6f)
771 {
772 newVelocity /= newVelocity.Length();
773 newVelocity *= 1000f;
774 }
775 else if (newVelocityLengthSq < 1e-6f)
776 newVelocity = Vector3.Zero;
777
778 // ==================================================================
779 // Stuff new linear velocity into the vehicle
780 Prim.ForceVelocity = newVelocity;
781 // Prim.ApplyForceImpulse((m_newVelocity - Prim.Velocity) * m_vehicleMass, false); // DEBUG DEBUG
782
783 // Other linear forces are applied as forces.
784 Vector3 totalDownForce = grav * m_vehicleMass;
785 if (totalDownForce != Vector3.Zero)
786 {
787 Prim.AddForce(totalDownForce, false);
788 } 1010 }
789 1011 return ret;
790 VDetailLog("{0},MoveLinear,done,lmDir={1},lmVel={2},newVel={3},primVel={4},totalDown={5}", 1012 }
791 Prim.LocalID, m_linearMotorDirection, m_lastLinearVelocityVector,
792 newVelocity, Prim.Velocity, totalDownForce);
793
794 } // end MoveLinear()
795 1013
796 // ======================================================================= 1014 // =======================================================================
797 // ======================================================================= 1015 // =======================================================================
798 // Apply the effect of the angular motor. 1016 // Apply the effect of the angular motor.
1017 // The 'contribution' is how much angular correction velocity each function wants.
1018 // All the contributions are added together and the resulting velocity is
1019 // set directly on the vehicle.
799 private void MoveAngular(float pTimestep) 1020 private void MoveAngular(float pTimestep)
800 { 1021 {
801 // m_angularMotorDirection // angular velocity requested by LSL motor 1022 // The user wants how many radians per second angular change?
802 // m_angularMotorVelocity // current angular motor velocity (ramps up and down)
803 // m_angularMotorTimescale // motor angular velocity ramp up time
804 // m_angularMotorDecayTimescale // motor angular velocity decay rate
805 // m_angularFrictionTimescale // body angular velocity decay rate
806 // m_lastAngularVelocity // what was last applied to body
807
808 if (m_angularMotorDirection.LengthSquared() > 0.0001)
809 {
810 Vector3 origVel = m_angularMotorVelocity;
811 Vector3 origDir = m_angularMotorDirection;
812
813 // new velocity += error / ( time to get there / step interval)
814 // requested direction - current vehicle direction
815 m_angularMotorVelocity += (m_angularMotorDirection - m_angularMotorVelocity) / (m_angularMotorTimescale / pTimestep);
816 // decay requested direction
817 m_angularMotorDirection *= (1.0f - (pTimestep * 1.0f/m_angularMotorDecayTimescale));
818
819 VDetailLog("{0},MoveAngular,angularMotorApply,angTScale={1},timeStep={2},origvel={3},origDir={4},vel={5}",
820 Prim.LocalID, m_angularMotorTimescale, pTimestep, origVel, origDir, m_angularMotorVelocity);
821 }
822 else
823 {
824 m_angularMotorVelocity = Vector3.Zero;
825 }
826
827 Vector3 angularMotorContribution = m_angularMotor.Step(pTimestep); 1023 Vector3 angularMotorContribution = m_angularMotor.Step(pTimestep);
828 1024
829 // ================================================================== 1025 // ==================================================================
830 Vector3 verticalAttractionContribution = Vector3.Zero; 1026 // From http://wiki.secondlife.com/wiki/LlSetVehicleFlags :
831 // If vertical attaction timescale is reasonable and we applied an angular force last time... 1027 // This flag prevents linear deflection parallel to world z-axis. This is useful
832 if (m_verticalAttractionTimescale < 300 && m_lastAngularVelocity != Vector3.Zero) 1028 // for preventing ground vehicles with large linear deflection, like bumper cars,
1029 // from climbing their linear deflection into the sky.
1030 // That is, NO_DEFLECTION_UP says angular motion should not add any pitch or roll movement
1031 if ((m_flags & (VehicleFlag.NO_DEFLECTION_UP)) != 0)
833 { 1032 {
834 float VAservo = pTimestep * 0.2f / m_verticalAttractionTimescale; 1033 angularMotorContribution.X = 0f;
835 if (Prim.IsColliding) 1034 angularMotorContribution.Y = 0f;
836 VAservo = pTimestep * 0.05f / m_verticalAttractionTimescale; 1035 VDetailLog("{0}, MoveAngular,noDeflectionUp,angularMotorContrib={1}", Prim.LocalID, angularMotorContribution);
837 1036 }
838 VAservo *= (m_verticalAttractionEfficiency * m_verticalAttractionEfficiency);
839
840 // Create a vector of the vehicle "up" in world coordinates
841 Vector3 verticalError = Vector3.UnitZ * Prim.ForceOrientation;
842 // verticalError.X and .Y are the World error amounts. They are 0 when there is no
843 // error (Vehicle Body is 'vertical'), and .Z will be 1. As the body leans to its
844 // side |.X| will increase to 1 and .Z fall to 0. As body inverts |.X| will fall
845 // and .Z will go // negative. Similar for tilt and |.Y|. .X and .Y must be
846 // modulated to prevent a stable inverted body.
847
848 // Error is 0 (no error) to +/- 2 (max error)
849 if (verticalError.Z < 0.0f)
850 {
851 verticalError.X = 2.0f - verticalError.X;
852 verticalError.Y = 2.0f - verticalError.Y;
853 }
854 // scale it by VAservo (timestep and timescale)
855 verticalError = verticalError * VAservo;
856 1037
857 // As the body rotates around the X axis, then verticalError.Y increases; Rotated around Y 1038 Vector3 verticalAttractionContribution = ComputeAngularVerticalAttraction();
858 // then .X increases, so change Body angular velocity X based on Y, and Y based on X.
859 // Z is not changed.
860 verticalAttractionContribution.X = verticalError.Y;
861 verticalAttractionContribution.Y = - verticalError.X;
862 verticalAttractionContribution.Z = 0f;
863 1039
864 // scaling appears better usingsquare-law 1040 Vector3 deflectionContribution = ComputeAngularDeflection();
865 Vector3 angularVelocity = Prim.ForceRotationalVelocity;
866 float bounce = 1.0f - (m_verticalAttractionEfficiency * m_verticalAttractionEfficiency);
867 verticalAttractionContribution.X += bounce * angularVelocity.X;
868 verticalAttractionContribution.Y += bounce * angularVelocity.Y;
869 1041
870 VDetailLog("{0},MoveAngular,verticalAttraction,VAservo={1},effic={2},verticalError={3},bounce={4},vertattr={5}", 1042 Vector3 bankingContribution = ComputeAngularBanking();
871 Prim.LocalID, VAservo, m_verticalAttractionEfficiency, verticalError, bounce, verticalAttractionContribution);
872 1043
873 } 1044 // ==================================================================
1045 m_lastVertAttractor = verticalAttractionContribution;
1046
1047 // Sum corrections
1048 m_lastAngularCorrection = angularMotorContribution
1049 + verticalAttractionContribution
1050 + deflectionContribution
1051 + bankingContribution;
874 1052
875 // ================================================================== 1053 // ==================================================================
876 Vector3 deflectionContribution = Vector3.Zero; 1054 // Apply the correction velocity.
877 if (m_angularDeflectionEfficiency != 0) 1055 // TODO: Should this be applied as an angular force (torque)?
1056 if (!m_lastAngularCorrection.ApproxEquals(Vector3.Zero, 0.01f))
878 { 1057 {
879 // Compute a scaled vector that points in the preferred axis (X direction) 1058 Vector3 scaledCorrection = m_lastAngularCorrection * pTimestep;
880 Vector3 scaledDefaultDirection = 1059 VehicleRotationalVelocity = scaledCorrection;
881 new Vector3((pTimestep * 10 * (m_angularDeflectionEfficiency / m_angularDeflectionTimescale)), 0, 0); 1060
882 // Adding the current vehicle orientation and reference frame displaces the orientation to the frame. 1061 VDetailLog("{0}, MoveAngular,done,nonZero,angMotorContrib={1},vertAttrContrib={2},bankContrib={3},deflectContrib={4},totalContrib={5},scaledCorr={6}",
883 // Rotate the scaled default axix relative to the actual vehicle direction giving where it should point. 1062 Prim.LocalID,
884 Vector3 preferredAxisOfMotion = scaledDefaultDirection * Quaternion.Add(Prim.ForceOrientation, m_referenceFrame); 1063 angularMotorContribution, verticalAttractionContribution,
885 1064 bankingContribution, deflectionContribution,
886 // Scale by efficiency and timescale 1065 m_lastAngularCorrection, scaledCorrection
887 deflectionContribution = (preferredAxisOfMotion * (m_angularDeflectionEfficiency) / m_angularDeflectionTimescale) * pTimestep; 1066 );
888
889 VDetailLog("{0},MoveAngular,Deflection,perfAxis={1},deflection={2}",
890 Prim.LocalID, preferredAxisOfMotion, deflectionContribution);
891 // This deflection computation is not correct.
892 deflectionContribution = Vector3.Zero;
893 } 1067 }
894 1068 else
895 // ==================================================================
896 Vector3 bankingContribution = Vector3.Zero;
897 if (m_bankingEfficiency != 0)
898 { 1069 {
899 Vector3 dir = Vector3.One * Prim.ForceOrientation; 1070 // The vehicle is not adding anything angular wise.
900 float mult = (m_bankingMix*m_bankingMix)*-1*(m_bankingMix < 0 ? -1 : 1); 1071 VehicleRotationalVelocity = Vector3.Zero;
901 //Changes which way it banks in and out of turns 1072 VDetailLog("{0}, MoveAngular,done,zero", Prim.LocalID);
902
903 //Use the square of the efficiency, as it looks much more how SL banking works
904 float effSquared = (m_bankingEfficiency*m_bankingEfficiency);
905 if (m_bankingEfficiency < 0)
906 effSquared *= -1; //Keep the negative!
907
908 float mix = Math.Abs(m_bankingMix);
909 if (m_angularMotorVelocity.X == 0)
910 {
911 // The vehicle is stopped
912 /*if (!parent.Orientation.ApproxEquals(this.m_referenceFrame, 0.25f))
913 {
914 Vector3 axisAngle;
915 float angle;
916 parent.Orientation.GetAxisAngle(out axisAngle, out angle);
917 Vector3 rotatedVel = parent.Velocity * parent.Orientation;
918 if ((rotatedVel.X < 0 && axisAngle.Y > 0) || (rotatedVel.X > 0 && axisAngle.Y < 0))
919 m_angularMotorVelocity.X += (effSquared * (mult * mix)) * (1f) * 10;
920 else
921 m_angularMotorVelocity.X += (effSquared * (mult * mix)) * (-1f) * 10;
922 }*/
923 }
924 else
925 {
926 bankingContribution.Z += (effSquared * (mult * mix)) * (m_angularMotorVelocity.X) * 4;
927 }
928
929 //If they are colliding, we probably shouldn't shove the prim around... probably
930 if (!Prim.IsColliding && Math.Abs(m_angularMotorVelocity.X) > mix)
931 {
932 float angVelZ = m_angularMotorVelocity.X*-1;
933 /*if(angVelZ > mix)
934 angVelZ = mix;
935 else if(angVelZ < -mix)
936 angVelZ = -mix;*/
937 //This controls how fast and how far the banking occurs
938 Vector3 bankingRot = new Vector3(angVelZ*(effSquared*mult), 0, 0);
939 if (bankingRot.X > 3)
940 bankingRot.X = 3;
941 else if (bankingRot.X < -3)
942 bankingRot.X = -3;
943 bankingRot *= Prim.ForceOrientation;
944 bankingContribution += bankingRot;
945 }
946 m_angularMotorVelocity.X *= m_bankingEfficiency == 1 ? 0.0f : 1 - m_bankingEfficiency;
947 VDetailLog("{0},MoveAngular,Banking,bEff={1},angMotVel={2},effSq={3},mult={4},mix={5},banking={6}",
948 Prim.LocalID, m_bankingEfficiency, m_angularMotorVelocity, effSquared, mult, mix, bankingContribution);
949 } 1073 }
950 1074
951 // ================================================================== 1075 // ==================================================================
952 m_lastVertAttractor = verticalAttractionContribution;
953
954 // Sum velocities
955 m_lastAngularVelocity = angularMotorContribution
956 + verticalAttractionContribution
957 + bankingContribution
958 + deflectionContribution;
959
960 // ==================================================================
961 //Offset section 1076 //Offset section
962 if (m_linearMotorOffset != Vector3.Zero) 1077 if (m_linearMotorOffset != Vector3.Zero)
963 { 1078 {
@@ -983,41 +1098,166 @@ namespace OpenSim.Region.Physics.BulletSPlugin
983 torqueFromOffset.Y = 0; 1098 torqueFromOffset.Y = 0;
984 if (float.IsNaN(torqueFromOffset.Z)) 1099 if (float.IsNaN(torqueFromOffset.Z))
985 torqueFromOffset.Z = 0; 1100 torqueFromOffset.Z = 0;
986 torqueFromOffset *= m_vehicleMass; 1101
987 Prim.ApplyTorqueImpulse(torqueFromOffset, true); 1102 VehicleAddAngularForce(torqueFromOffset * m_vehicleMass);
988 VDetailLog("{0},BSDynamic.MoveAngular,motorOffset,applyTorqueImpulse={1}", Prim.LocalID, torqueFromOffset); 1103 VDetailLog("{0}, BSDynamic.MoveAngular,motorOffset,applyTorqueImpulse={1}", Prim.LocalID, torqueFromOffset);
989 } 1104 }
990 1105
991 // ================================================================== 1106 }
992 // NO_DEFLECTION_UP says angular motion should not add any pitch or roll movement 1107 // From http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial:
993 if ((m_flags & (VehicleFlag.NO_DEFLECTION_UP)) != 0) 1108 // Some vehicles, like boats, should always keep their up-side up. This can be done by
1109 // enabling the "vertical attractor" behavior that springs the vehicle's local z-axis to
1110 // the world z-axis (a.k.a. "up"). To take advantage of this feature you would set the
1111 // VEHICLE_VERTICAL_ATTRACTION_TIMESCALE to control the period of the spring frequency,
1112 // and then set the VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY to control the damping. An
1113 // efficiency of 0.0 will cause the spring to wobble around its equilibrium, while an
1114 // efficiency of 1.0 will cause the spring to reach its equilibrium with exponential decay.
1115 public Vector3 ComputeAngularVerticalAttraction()
1116 {
1117 Vector3 ret = Vector3.Zero;
1118
1119 // If vertical attaction timescale is reasonable and we applied an angular force last time...
1120 if (m_verticalAttractionTimescale < m_verticalAttractionCutoff)
994 { 1121 {
995 m_lastAngularVelocity.X = 0; 1122 // Take a vector pointing up and convert it from world to vehicle relative coords.
996 m_lastAngularVelocity.Y = 0; 1123 Vector3 verticalError = Vector3.UnitZ * VehicleOrientation;
997 VDetailLog("{0},MoveAngular,noDeflectionUp,lastAngular={1}", Prim.LocalID, m_lastAngularVelocity); 1124 verticalError.Normalize();
1125
1126 // If vertical attraction correction is needed, the vector that was pointing up (UnitZ)
1127 // is now leaning to one side (rotated around the X axis) and the Y value will
1128 // go from zero (nearly straight up) to one (completely to the side) or leaning
1129 // front-to-back (rotated around the Y axis) and the value of X will be between
1130 // zero and one.
1131 // The value of Z is how far the rotation is off with 1 meaning none and 0 being 90 degrees.
1132
1133 // If verticalError.Z is negative, the vehicle is upside down. Add additional push.
1134 if (verticalError.Z < 0f)
1135 {
1136 verticalError.X = 2f - verticalError.X;
1137 verticalError.Y = 2f - verticalError.Y;
1138 }
1139
1140 // Y error means needed rotation around X axis and visa versa.
1141 ret.X = verticalError.Y;
1142 ret.Y = - verticalError.X;
1143 ret.Z = 0f;
1144
1145 // Scale the correction force by how far we're off from vertical.
1146 // Z error of one says little error. As Z gets smaller, the vehicle is leaning farther over.
1147 float clampedSqrZError = ClampInRange(0.01f, verticalError.Z * verticalError.Z, 1f);
1148 float vertForce = 1f / clampedSqrZError;
1149
1150 ret *= vertForce;
1151
1152 // Correction happens over a number of seconds.
1153 Vector3 unscaledContrib = ret;
1154 ret /= m_verticalAttractionTimescale;
1155
1156 VDetailLog("{0}, MoveAngular,verticalAttraction,,verticalError={1},unscaled={2},vertForce={3},eff={4},vertAttr={5}",
1157 Prim.LocalID, verticalError, unscaledContrib, vertForce, m_verticalAttractionEfficiency, ret);
998 } 1158 }
1159 return ret;
1160 }
999 1161
1000 // ================================================================== 1162 // Return the angular correction to correct the direction the vehicle is pointing to be
1001 if (m_lastAngularVelocity.ApproxEquals(Vector3.Zero, 0.01f)) 1163 // the direction is should want to be pointing.
1164 // The vehicle is moving in some direction and correct its orientation to it is pointing
1165 // in that direction.
1166 // TODO: implement reference frame.
1167 public Vector3 ComputeAngularDeflection()
1168 {
1169 Vector3 ret = Vector3.Zero;
1170 return ret; // DEBUG DEBUG DEBUG debug one force at a time
1171
1172 if (m_angularDeflectionEfficiency != 0)
1002 { 1173 {
1003 m_lastAngularVelocity = Vector3.Zero; // Reduce small value to zero. 1174 // The direction the vehicle is moving
1004 Prim.ZeroAngularMotion(true); 1175 Vector3 movingDirection = VehicleVelocity;
1005 VDetailLog("{0},MoveAngular,zeroAngularMotion,lastAngular={1}", Prim.LocalID, m_lastAngularVelocity); 1176 movingDirection.Normalize();
1177
1178 // The direction the vehicle is pointing
1179 Vector3 pointingDirection = Vector3.UnitX * VehicleOrientation;
1180 pointingDirection.Normalize();
1181
1182 // The difference between what is and what should be
1183 Vector3 deflectionError = movingDirection - pointingDirection;
1184
1185 // Scale the correction by recovery timescale and efficiency
1186 ret = (-deflectionError * VehicleForwardSpeed) * m_angularDeflectionEfficiency;
1187 ret /= m_angularDeflectionTimescale;
1188
1189 VDetailLog("{0}, MoveAngular,Deflection,movingDir={1},pointingDir={2},deflectError={3},ret={4}",
1190 Prim.LocalID, movingDirection, pointingDirection, deflectionError, ret);
1006 } 1191 }
1007 else 1192 return ret;
1193 }
1194
1195 // Return an angular change to rotate the vehicle around the Z axis when the vehicle
1196 // is tipped around the X axis.
1197 // From http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial:
1198 // The vertical attractor feature must be enabled in order for the banking behavior to
1199 // function. The way banking works is this: a rotation around the vehicle's roll-axis will
1200 // produce a angular velocity around the yaw-axis, causing the vehicle to turn. The magnitude
1201 // of the yaw effect will be proportional to the
1202 // VEHICLE_BANKING_EFFICIENCY, the angle of the roll rotation, and sometimes the vehicle's
1203 // velocity along its preferred axis of motion.
1204 // The VEHICLE_BANKING_EFFICIENCY can vary between -1 and +1. When it is positive then any
1205 // positive rotation (by the right-hand rule) about the roll-axis will effect a
1206 // (negative) torque around the yaw-axis, making it turn to the right--that is the
1207 // vehicle will lean into the turn, which is how real airplanes and motorcycle's work.
1208 // Negating the banking coefficient will make it so that the vehicle leans to the
1209 // outside of the turn (not very "physical" but might allow interesting vehicles so why not?).
1210 // The VEHICLE_BANKING_MIX is a fake (i.e. non-physical) parameter that is useful for making
1211 // banking vehicles do what you want rather than what the laws of physics allow.
1212 // For example, consider a real motorcycle...it must be moving forward in order for
1213 // it to turn while banking, however video-game motorcycles are often configured
1214 // to turn in place when at a dead stop--because they are often easier to control
1215 // that way using the limited interface of the keyboard or game controller. The
1216 // VEHICLE_BANKING_MIX enables combinations of both realistic and non-realistic
1217 // banking by functioning as a slider between a banking that is correspondingly
1218 // totally static (0.0) and totally dynamic (1.0). By "static" we mean that the
1219 // banking effect depends only on the vehicle's rotation about its roll-axis compared
1220 // to "dynamic" where the banking is also proportional to its velocity along its
1221 // roll-axis. Finding the best value of the "mixture" will probably require trial and error.
1222 // The time it takes for the banking behavior to defeat a preexisting angular velocity about the
1223 // world z-axis is determined by the VEHICLE_BANKING_TIMESCALE. So if you want the vehicle to
1224 // bank quickly then give it a banking timescale of about a second or less, otherwise you can
1225 // make a sluggish vehicle by giving it a timescale of several seconds.
1226 public Vector3 ComputeAngularBanking()
1227 {
1228 Vector3 ret = Vector3.Zero;
1229
1230 if (m_bankingEfficiency != 0 && m_verticalAttractionTimescale < m_verticalAttractionCutoff)
1008 { 1231 {
1009 // Apply to the body. 1232 // This works by rotating a unit vector to the orientation of the vehicle. The
1010 // The above calculates the absolute angular velocity needed. Angular velocity is massless. 1233 // roll (tilt) will be Y component of a tilting Z vector (zero for no tilt
1011 // Since we are stuffing the angular velocity directly into the object, the computed 1234 // up to one for full over).
1012 // velocity needs to be scaled by the timestep. 1235 Vector3 rollComponents = Vector3.UnitZ * VehicleOrientation;
1013 // Also remove any motion that is on the object so added motion is only from vehicle. 1236
1014 Vector3 applyAngularForce = ((m_lastAngularVelocity * pTimestep) 1237 // Figure out the yaw value for this much roll.
1015 - Prim.ForceRotationalVelocity); 1238 float turnComponent = rollComponents.Y * rollComponents.Y * m_bankingEfficiency;
1016 Prim.ForceRotationalVelocity = applyAngularForce; 1239 // Keep the sign
1017 1240 if (rollComponents.Y < 0f)
1018 VDetailLog("{0},MoveAngular,done,newRotVel={1},lastAngular={2}", 1241 turnComponent = -turnComponent;
1019 Prim.LocalID, applyAngularForce, m_lastAngularVelocity); 1242
1243 // TODO: there must be a better computation of the banking force.
1244 float bankingTurnForce = turnComponent;
1245
1246 // actual error = static turn error + dynamic turn error
1247 float mixedBankingError = bankingTurnForce * (1f - m_bankingMix) + bankingTurnForce * m_bankingMix * VehicleForwardSpeed;
1248 // TODO: the banking effect should not go to infinity but what to limit it to?
1249 mixedBankingError = ClampInRange(-20f, mixedBankingError, 20f);
1250
1251 // Build the force vector to change rotation from what it is to what it should be
1252 ret.Z = -mixedBankingError;
1253
1254 // Don't do it all at once.
1255 ret /= m_bankingTimescale;
1256
1257 VDetailLog("{0}, MoveAngular,Banking,rollComp={1},speed={2},turnComp={3},bankErr={4},mixedBankErr={5},ret={6}",
1258 Prim.LocalID, rollComponents, VehicleForwardSpeed, turnComponent, bankingTurnForce, mixedBankingError, ret);
1020 } 1259 }
1260 return ret;
1021 } 1261 }
1022 1262
1023 // This is from previous instantiations of XXXDynamics.cs. 1263 // This is from previous instantiations of XXXDynamics.cs.
@@ -1026,7 +1266,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
1026 // Should this be in MoveAngular()? 1266 // Should this be in MoveAngular()?
1027 internal void LimitRotation(float timestep) 1267 internal void LimitRotation(float timestep)
1028 { 1268 {
1029 Quaternion rotq = Prim.ForceOrientation; 1269 Quaternion rotq = VehicleOrientation;
1030 Quaternion m_rot = rotq; 1270 Quaternion m_rot = rotq;
1031 if (m_RollreferenceFrame != Quaternion.Identity) 1271 if (m_RollreferenceFrame != Quaternion.Identity)
1032 { 1272 {
@@ -1054,12 +1294,17 @@ namespace OpenSim.Region.Physics.BulletSPlugin
1054 } 1294 }
1055 if (rotq != m_rot) 1295 if (rotq != m_rot)
1056 { 1296 {
1057 Prim.ForceOrientation = m_rot; 1297 VehicleOrientation = m_rot;
1058 VDetailLog("{0},LimitRotation,done,orig={1},new={2}", Prim.LocalID, rotq, m_rot); 1298 VDetailLog("{0}, LimitRotation,done,orig={1},new={2}", Prim.LocalID, rotq, m_rot);
1059 } 1299 }
1060 1300
1061 } 1301 }
1062 1302
1303 private float ClampInRange(float low, float val, float high)
1304 {
1305 return Math.Max(low, Math.Min(val, high));
1306 }
1307
1063 // Invoke the detailed logger and output something if it's enabled. 1308 // Invoke the detailed logger and output something if it's enabled.
1064 private void VDetailLog(string msg, params Object[] args) 1309 private void VDetailLog(string msg, params Object[] args)
1065 { 1310 {