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.cs287
1 files changed, 181 insertions, 106 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs
index be8a502..fa3110c 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs
@@ -24,21 +24,10 @@
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;
@@ -111,7 +100,6 @@ namespace OpenSim.Region.Physics.BulletSPlugin
111 private float m_bankingEfficiency = 0; 100 private float m_bankingEfficiency = 0;
112 private float m_bankingMix = 0; 101 private float m_bankingMix = 0;
113 private float m_bankingTimescale = 0; 102 private float m_bankingTimescale = 0;
114 private Vector3 m_lastBanking = Vector3.Zero;
115 103
116 //Hover and Buoyancy properties 104 //Hover and Buoyancy properties
117 private float m_VhoverHeight = 0f; 105 private float m_VhoverHeight = 0f;
@@ -125,8 +113,10 @@ namespace OpenSim.Region.Physics.BulletSPlugin
125 113
126 //Attractor properties 114 //Attractor properties
127 private BSVMotor m_verticalAttractionMotor = new BSVMotor("VerticalAttraction"); 115 private BSVMotor m_verticalAttractionMotor = new BSVMotor("VerticalAttraction");
128 private float m_verticalAttractionEfficiency = 1.0f; // damped 116 private float m_verticalAttractionEfficiency = 1.0f; // damped
129 private float m_verticalAttractionTimescale = 600f; // Timescale > 500 means no vert attractor. 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 {
@@ -329,7 +319,6 @@ namespace OpenSim.Region.Physics.BulletSPlugin
329 m_bankingEfficiency = 0; 319 m_bankingEfficiency = 0;
330 m_bankingTimescale = 1000; 320 m_bankingTimescale = 1000;
331 m_bankingMix = 1; 321 m_bankingMix = 1;
332 m_lastBanking = Vector3.Zero;
333 322
334 m_referenceFrame = Quaternion.Identity; 323 m_referenceFrame = Quaternion.Identity;
335 m_flags = (VehicleFlag)0; 324 m_flags = (VehicleFlag)0;
@@ -364,7 +353,6 @@ namespace OpenSim.Region.Physics.BulletSPlugin
364 m_bankingEfficiency = 0; 353 m_bankingEfficiency = 0;
365 m_bankingTimescale = 10; 354 m_bankingTimescale = 10;
366 m_bankingMix = 1; 355 m_bankingMix = 1;
367 m_lastBanking = Vector3.Zero;
368 356
369 m_referenceFrame = Quaternion.Identity; 357 m_referenceFrame = Quaternion.Identity;
370 m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY 358 m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY
@@ -374,6 +362,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
374 m_flags |= (VehicleFlag.NO_DEFLECTION_UP 362 m_flags |= (VehicleFlag.NO_DEFLECTION_UP
375 | VehicleFlag.LIMIT_ROLL_ONLY 363 | VehicleFlag.LIMIT_ROLL_ONLY
376 | VehicleFlag.LIMIT_MOTOR_UP); 364 | VehicleFlag.LIMIT_MOTOR_UP);
365
377 break; 366 break;
378 case Vehicle.TYPE_CAR: 367 case Vehicle.TYPE_CAR:
379 m_linearMotorDirection = Vector3.Zero; 368 m_linearMotorDirection = Vector3.Zero;
@@ -403,7 +392,6 @@ namespace OpenSim.Region.Physics.BulletSPlugin
403 m_bankingEfficiency = -0.2f; 392 m_bankingEfficiency = -0.2f;
404 m_bankingMix = 1; 393 m_bankingMix = 1;
405 m_bankingTimescale = 1; 394 m_bankingTimescale = 1;
406 m_lastBanking = Vector3.Zero;
407 395
408 m_referenceFrame = Quaternion.Identity; 396 m_referenceFrame = Quaternion.Identity;
409 m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY 397 m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY
@@ -442,7 +430,6 @@ namespace OpenSim.Region.Physics.BulletSPlugin
442 m_bankingEfficiency = -0.3f; 430 m_bankingEfficiency = -0.3f;
443 m_bankingMix = 0.8f; 431 m_bankingMix = 0.8f;
444 m_bankingTimescale = 1; 432 m_bankingTimescale = 1;
445 m_lastBanking = Vector3.Zero;
446 433
447 m_referenceFrame = Quaternion.Identity; 434 m_referenceFrame = Quaternion.Identity;
448 m_flags &= ~(VehicleFlag.HOVER_TERRAIN_ONLY 435 m_flags &= ~(VehicleFlag.HOVER_TERRAIN_ONLY
@@ -481,7 +468,6 @@ namespace OpenSim.Region.Physics.BulletSPlugin
481 m_bankingEfficiency = 1; 468 m_bankingEfficiency = 1;
482 m_bankingMix = 0.7f; 469 m_bankingMix = 0.7f;
483 m_bankingTimescale = 2; 470 m_bankingTimescale = 2;
484 m_lastBanking = Vector3.Zero;
485 471
486 m_referenceFrame = Quaternion.Identity; 472 m_referenceFrame = Quaternion.Identity;
487 m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY 473 m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY
@@ -520,7 +506,6 @@ namespace OpenSim.Region.Physics.BulletSPlugin
520 m_bankingEfficiency = 0; 506 m_bankingEfficiency = 0;
521 m_bankingMix = 0.7f; 507 m_bankingMix = 0.7f;
522 m_bankingTimescale = 5; 508 m_bankingTimescale = 5;
523 m_lastBanking = Vector3.Zero;
524 509
525 m_referenceFrame = Quaternion.Identity; 510 m_referenceFrame = Quaternion.Identity;
526 511
@@ -554,8 +539,6 @@ namespace OpenSim.Region.Physics.BulletSPlugin
554 // Z goes away and we keep X and Y 539 // Z goes away and we keep X and Y
555 m_verticalAttractionMotor.FrictionTimescale = new Vector3(BSMotor.Infinite, BSMotor.Infinite, 0.1f); 540 m_verticalAttractionMotor.FrictionTimescale = new Vector3(BSMotor.Infinite, BSMotor.Infinite, 0.1f);
556 m_verticalAttractionMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG DEBUG (enables detail logging) 541 m_verticalAttractionMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG DEBUG (enables detail logging)
557
558 // m_bankingMotor = new BSVMotor("BankingMotor", ...);
559 } 542 }
560 543
561 // Some of the properties of this prim may have changed. 544 // Some of the properties of this prim may have changed.
@@ -577,15 +560,23 @@ namespace OpenSim.Region.Physics.BulletSPlugin
577 float angularDamping = PhysicsScene.Params.vehicleAngularDamping; 560 float angularDamping = PhysicsScene.Params.vehicleAngularDamping;
578 BulletSimAPI.SetAngularDamping2(Prim.PhysBody.ptr, angularDamping); 561 BulletSimAPI.SetAngularDamping2(Prim.PhysBody.ptr, angularDamping);
579 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
580 // 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
581 // Vector3 localInertia = new Vector3(1f, 1f, 1f); 567 // Vector3 localInertia = new Vector3(1f, 1f, 1f);
582 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);
583 BulletSimAPI.SetMassProps2(Prim.PhysBody.ptr, m_vehicleMass, localInertia); 570 BulletSimAPI.SetMassProps2(Prim.PhysBody.ptr, m_vehicleMass, localInertia);
584 BulletSimAPI.UpdateInertiaTensor2(Prim.PhysBody.ptr); 571 BulletSimAPI.UpdateInertiaTensor2(Prim.PhysBody.ptr);
585 572
586 VDetailLog("{0},BSDynamics.Refresh,frict={1},inert={2},aDamp={3}", 573 VDetailLog("{0},BSDynamics.Refresh,frict={1},inert={2},aDamp={3}",
587 Prim.LocalID, friction, localInertia, angularDamping); 574 Prim.LocalID, friction, localInertia, angularDamping);
588 } 575 }
576 else
577 {
578 BulletSimAPI.RemoveFromCollisionFlags2(Prim.PhysBody.ptr, CollisionFlags.BS_VEHICLE_COLLISIONS);
579 }
589 } 580 }
590 581
591 public bool RemoveBodyDependencies(BSPhysObject prim) 582 public bool RemoveBodyDependencies(BSPhysObject prim)
@@ -618,13 +609,18 @@ namespace OpenSim.Region.Physics.BulletSPlugin
618 private float? m_knownWaterLevel; 609 private float? m_knownWaterLevel;
619 private Vector3? m_knownPosition; 610 private Vector3? m_knownPosition;
620 private Vector3? m_knownVelocity; 611 private Vector3? m_knownVelocity;
612 private Vector3 m_knownForce;
621 private Quaternion? m_knownOrientation; 613 private Quaternion? m_knownOrientation;
622 private Vector3? m_knownRotationalVelocity; 614 private Vector3? m_knownRotationalVelocity;
615 private Vector3 m_knownRotationalForce;
616 private float? m_knownForwardSpeed;
623 617
624 private const int m_knownChangedPosition = 1 << 0; 618 private const int m_knownChangedPosition = 1 << 0;
625 private const int m_knownChangedVelocity = 1 << 1; 619 private const int m_knownChangedVelocity = 1 << 1;
626 private const int m_knownChangedOrientation = 1 << 2; 620 private const int m_knownChangedForce = 1 << 2;
627 private const int m_knownChangedRotationalVelocity = 1 << 3; 621 private const int m_knownChangedOrientation = 1 << 3;
622 private const int m_knownChangedRotationalVelocity = 1 << 4;
623 private const int m_knownChangedRotationalForce = 1 << 5;
628 624
629 private void ForgetKnownVehicleProperties() 625 private void ForgetKnownVehicleProperties()
630 { 626 {
@@ -632,8 +628,11 @@ namespace OpenSim.Region.Physics.BulletSPlugin
632 m_knownWaterLevel = null; 628 m_knownWaterLevel = null;
633 m_knownPosition = null; 629 m_knownPosition = null;
634 m_knownVelocity = null; 630 m_knownVelocity = null;
631 m_knownForce = Vector3.Zero;
635 m_knownOrientation = null; 632 m_knownOrientation = null;
636 m_knownRotationalVelocity = null; 633 m_knownRotationalVelocity = null;
634 m_knownRotationalForce = Vector3.Zero;
635 m_knownForwardSpeed = null;
637 m_knownChanged = 0; 636 m_knownChanged = 0;
638 } 637 }
639 private void PushKnownChanged() 638 private void PushKnownChanged()
@@ -645,12 +644,22 @@ namespace OpenSim.Region.Physics.BulletSPlugin
645 if ((m_knownChanged & m_knownChangedOrientation) != 0) 644 if ((m_knownChanged & m_knownChangedOrientation) != 0)
646 Prim.ForceOrientation = VehicleOrientation; 645 Prim.ForceOrientation = VehicleOrientation;
647 if ((m_knownChanged & m_knownChangedVelocity) != 0) 646 if ((m_knownChanged & m_knownChangedVelocity) != 0)
647 {
648 Prim.ForceVelocity = VehicleVelocity; 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
649 if ((m_knownChanged & m_knownChangedRotationalVelocity) != 0) 654 if ((m_knownChanged & m_knownChangedRotationalVelocity) != 0)
650 { 655 {
651 Prim.ForceRotationalVelocity = VehicleRotationalVelocity; 656 Prim.ForceRotationalVelocity = VehicleRotationalVelocity;
657 // Fake out Bullet by making it think the velocity is the same as last time.
652 BulletSimAPI.SetInterpolationAngularVelocity2(Prim.PhysBody.ptr, VehicleRotationalVelocity); 658 BulletSimAPI.SetInterpolationAngularVelocity2(Prim.PhysBody.ptr, VehicleRotationalVelocity);
653 } 659 }
660 if ((m_knownChanged & m_knownChangedRotationalForce) != 0)
661 Prim.AddAngularForce((Vector3)m_knownRotationalForce, false, true);
662
654 // If we set one of the values (ie, the physics engine didn't do it) we must force 663 // If we set one of the values (ie, the physics engine didn't do it) we must force
655 // an UpdateProperties event to send the changes up to the simulator. 664 // an UpdateProperties event to send the changes up to the simulator.
656 BulletSimAPI.PushUpdate2(Prim.PhysBody.ptr); 665 BulletSimAPI.PushUpdate2(Prim.PhysBody.ptr);
@@ -720,6 +729,12 @@ namespace OpenSim.Region.Physics.BulletSPlugin
720 } 729 }
721 } 730 }
722 731
732 private void VehicleAddForce(Vector3 aForce)
733 {
734 m_knownForce += aForce;
735 m_knownChanged |= m_knownChangedForce;
736 }
737
723 private Vector3 VehicleRotationalVelocity 738 private Vector3 VehicleRotationalVelocity
724 { 739 {
725 get 740 get
@@ -734,6 +749,21 @@ namespace OpenSim.Region.Physics.BulletSPlugin
734 m_knownChanged |= m_knownChangedRotationalVelocity; 749 m_knownChanged |= m_knownChangedRotationalVelocity;
735 } 750 }
736 } 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
737 #endregion // Known vehicle value functions 767 #endregion // Known vehicle value functions
738 768
739 // One step of the vehicle properties for the next 'pTimestep' seconds. 769 // One step of the vehicle properties for the next 'pTimestep' seconds.
@@ -769,10 +799,10 @@ namespace OpenSim.Region.Physics.BulletSPlugin
769 linearMotorContribution *= VehicleOrientation; 799 linearMotorContribution *= VehicleOrientation;
770 800
771 // ================================================================== 801 // ==================================================================
772 // Gravity and Buoyancy 802 // Buoyancy: force to overcome gravity.
773 // There is some gravity, make a gravity force vector that is applied after object velocity.
774 // m_VehicleBuoyancy: -1=2g; 0=1g; 1=0g; 803 // m_VehicleBuoyancy: -1=2g; 0=1g; 1=0g;
775 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;
776 806
777 Vector3 terrainHeightContribution = ComputeLinearTerrainHeightCorrection(pTimestep); 807 Vector3 terrainHeightContribution = ComputeLinearTerrainHeightCorrection(pTimestep);
778 808
@@ -797,14 +827,16 @@ namespace OpenSim.Region.Physics.BulletSPlugin
797 newVelocity.Z = 0; 827 newVelocity.Z = 0;
798 828
799 // ================================================================== 829 // ==================================================================
800 // Clamp REALLY high or low velocities 830 // Clamp high or low velocities
801 float newVelocityLengthSq = newVelocity.LengthSquared(); 831 float newVelocityLengthSq = newVelocity.LengthSquared();
802 if (newVelocityLengthSq > 1e6f) 832 // if (newVelocityLengthSq > 1e6f)
833 if (newVelocityLengthSq > 1000f)
803 { 834 {
804 newVelocity /= newVelocity.Length(); 835 newVelocity /= newVelocity.Length();
805 newVelocity *= 1000f; 836 newVelocity *= 1000f;
806 } 837 }
807 else if (newVelocityLengthSq < 1e-6f) 838 // else if (newVelocityLengthSq < 1e-6f)
839 else if (newVelocityLengthSq < 0.001f)
808 newVelocity = Vector3.Zero; 840 newVelocity = Vector3.Zero;
809 841
810 // ================================================================== 842 // ==================================================================
@@ -813,15 +845,18 @@ namespace OpenSim.Region.Physics.BulletSPlugin
813 VehicleVelocity = newVelocity; 845 VehicleVelocity = newVelocity;
814 846
815 // Other linear forces are applied as forces. 847 // Other linear forces are applied as forces.
816 Vector3 totalDownForce = grav * m_vehicleMass * pTimestep; 848 Vector3 totalDownForce = buoyancyContribution * m_vehicleMass;
817 if (totalDownForce != Vector3.Zero) 849 if (!totalDownForce.ApproxEquals(Vector3.Zero, 0.01f))
818 { 850 {
819 Prim.AddForce(totalDownForce, false); 851 VehicleAddForce(totalDownForce);
820 } 852 }
821 853
822 VDetailLog("{0}, MoveLinear,done,newVel={1},totDown={2},linContrib={3},terrContrib={4},hoverContrib={5},limitContrib={6}", 854 VDetailLog("{0}, MoveLinear,done,newVel={1},totDown={2},IsColliding={3}",
823 Prim.LocalID, newVelocity, totalDownForce, 855 Prim.LocalID, newVelocity, totalDownForce, Prim.IsColliding);
824 linearMotorContribution, terrainHeightContribution, hoverContribution, limitMotorUpContribution 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
825 ); 860 );
826 861
827 } // end MoveLinear() 862 } // end MoveLinear()
@@ -942,21 +977,24 @@ namespace OpenSim.Region.Physics.BulletSPlugin
942 } 977 }
943 978
944 // From http://wiki.secondlife.com/wiki/LlSetVehicleFlags : 979 // From http://wiki.secondlife.com/wiki/LlSetVehicleFlags :
945 // Prevent ground vehicles from motoring into the sky.This flag has a subtle effect when 980 // Prevent ground vehicles from motoring into the sky. This flag has a subtle effect when
946 // used with conjunction with banking: the strength of the banking will decay when the 981 // used with conjunction with banking: the strength of the banking will decay when the
947 // vehicle no longer experiences collisions. The decay timescale is the same as 982 // vehicle no longer experiences collisions. The decay timescale is the same as
948 // VEHICLE_BANKING_TIMESCALE. This is to help prevent ground vehicles from steering 983 // VEHICLE_BANKING_TIMESCALE. This is to help prevent ground vehicles from steering
949 // when they are in mid jump. 984 // when they are in mid jump.
950 // TODO: this code is wrong. Also, what should it do for boats? 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.
951 public Vector3 ComputeLinearMotorUp(float pTimestep) 988 public Vector3 ComputeLinearMotorUp(float pTimestep)
952 { 989 {
953 Vector3 ret = Vector3.Zero; 990 Vector3 ret = Vector3.Zero;
991
954 if ((m_flags & (VehicleFlag.LIMIT_MOTOR_UP)) != 0) 992 if ((m_flags & (VehicleFlag.LIMIT_MOTOR_UP)) != 0)
955 { 993 {
956 // 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.
957 // float distanceAboveGround = pos.Z - Math.Max(GetTerrainHeight(pos), GetWaterLevel(pos));
958 float distanceAboveGround = VehiclePosition.Z - GetTerrainHeight(VehiclePosition); 995 float distanceAboveGround = VehiclePosition.Z - GetTerrainHeight(VehiclePosition);
959 if (distanceAboveGround > 1f) 996 // Not colliding if the vehicle is off the ground
997 if (!Prim.IsColliding)
960 { 998 {
961 // downForce = new Vector3(0, 0, (-distanceAboveGround / m_bankingTimescale) * pTimestep); 999 // downForce = new Vector3(0, 0, (-distanceAboveGround / m_bankingTimescale) * pTimestep);
962 // downForce = new Vector3(0, 0, -distanceAboveGround / m_bankingTimescale); 1000 // downForce = new Vector3(0, 0, -distanceAboveGround / m_bankingTimescale);
@@ -977,8 +1015,8 @@ namespace OpenSim.Region.Physics.BulletSPlugin
977 // ======================================================================= 1015 // =======================================================================
978 // Apply the effect of the angular motor. 1016 // Apply the effect of the angular motor.
979 // The 'contribution' is how much angular correction velocity each function wants. 1017 // The 'contribution' is how much angular correction velocity each function wants.
980 // All the contributions are added together and the orientation of the vehicle 1018 // All the contributions are added together and the resulting velocity is
981 // is changed by all the contributed corrections. 1019 // set directly on the vehicle.
982 private void MoveAngular(float pTimestep) 1020 private void MoveAngular(float pTimestep)
983 { 1021 {
984 // The user wants how many radians per second angular change? 1022 // The user wants how many radians per second angular change?
@@ -1001,7 +1039,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
1001 1039
1002 Vector3 deflectionContribution = ComputeAngularDeflection(); 1040 Vector3 deflectionContribution = ComputeAngularDeflection();
1003 1041
1004 Vector3 bankingContribution = ComputeAngularBanking(angularMotorContribution.Z); 1042 Vector3 bankingContribution = ComputeAngularBanking();
1005 1043
1006 // ================================================================== 1044 // ==================================================================
1007 m_lastVertAttractor = verticalAttractionContribution; 1045 m_lastVertAttractor = verticalAttractionContribution;
@@ -1013,11 +1051,11 @@ namespace OpenSim.Region.Physics.BulletSPlugin
1013 + bankingContribution; 1051 + bankingContribution;
1014 1052
1015 // ================================================================== 1053 // ==================================================================
1016 // The correction is applied to the current orientation. 1054 // Apply the correction velocity.
1055 // TODO: Should this be applied as an angular force (torque)?
1017 if (!m_lastAngularCorrection.ApproxEquals(Vector3.Zero, 0.01f)) 1056 if (!m_lastAngularCorrection.ApproxEquals(Vector3.Zero, 0.01f))
1018 { 1057 {
1019 Vector3 scaledCorrection = m_lastAngularCorrection * pTimestep; 1058 Vector3 scaledCorrection = m_lastAngularCorrection * pTimestep;
1020
1021 VehicleRotationalVelocity = scaledCorrection; 1059 VehicleRotationalVelocity = scaledCorrection;
1022 1060
1023 VDetailLog("{0}, MoveAngular,done,nonZero,angMotorContrib={1},vertAttrContrib={2},bankContrib={3},deflectContrib={4},totalContrib={5},scaledCorr={6}", 1061 VDetailLog("{0}, MoveAngular,done,nonZero,angMotorContrib={1},vertAttrContrib={2},bankContrib={3},deflectContrib={4},totalContrib={5},scaledCorr={6}",
@@ -1029,7 +1067,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
1029 } 1067 }
1030 else 1068 else
1031 { 1069 {
1032 // The vehicle is not adding anything velocity wise. 1070 // The vehicle is not adding anything angular wise.
1033 VehicleRotationalVelocity = Vector3.Zero; 1071 VehicleRotationalVelocity = Vector3.Zero;
1034 VDetailLog("{0}, MoveAngular,done,zero", Prim.LocalID); 1072 VDetailLog("{0}, MoveAngular,done,zero", Prim.LocalID);
1035 } 1073 }
@@ -1060,19 +1098,26 @@ namespace OpenSim.Region.Physics.BulletSPlugin
1060 torqueFromOffset.Y = 0; 1098 torqueFromOffset.Y = 0;
1061 if (float.IsNaN(torqueFromOffset.Z)) 1099 if (float.IsNaN(torqueFromOffset.Z))
1062 torqueFromOffset.Z = 0; 1100 torqueFromOffset.Z = 0;
1063 torqueFromOffset *= m_vehicleMass; 1101
1064 Prim.ApplyTorqueImpulse(torqueFromOffset, true); 1102 VehicleAddAngularForce(torqueFromOffset * m_vehicleMass);
1065 VDetailLog("{0}, BSDynamic.MoveAngular,motorOffset,applyTorqueImpulse={1}", Prim.LocalID, torqueFromOffset); 1103 VDetailLog("{0}, BSDynamic.MoveAngular,motorOffset,applyTorqueImpulse={1}", Prim.LocalID, torqueFromOffset);
1066 } 1104 }
1067 1105
1068 } 1106 }
1069 1107 // From http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial:
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.
1070 public Vector3 ComputeAngularVerticalAttraction() 1115 public Vector3 ComputeAngularVerticalAttraction()
1071 { 1116 {
1072 Vector3 ret = Vector3.Zero; 1117 Vector3 ret = Vector3.Zero;
1073 1118
1074 // If vertical attaction timescale is reasonable and we applied an angular force last time... 1119 // If vertical attaction timescale is reasonable and we applied an angular force last time...
1075 if (m_verticalAttractionTimescale < 500) 1120 if (m_verticalAttractionTimescale < m_verticalAttractionCutoff)
1076 { 1121 {
1077 // Take a vector pointing up and convert it from world to vehicle relative coords. 1122 // Take a vector pointing up and convert it from world to vehicle relative coords.
1078 Vector3 verticalError = Vector3.UnitZ * VehicleOrientation; 1123 Vector3 verticalError = Vector3.UnitZ * VehicleOrientation;
@@ -1097,91 +1142,121 @@ namespace OpenSim.Region.Physics.BulletSPlugin
1097 ret.Y = - verticalError.X; 1142 ret.Y = - verticalError.X;
1098 ret.Z = 0f; 1143 ret.Z = 0f;
1099 1144
1100 // scale by the time scale and timestep 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.
1101 Vector3 unscaledContrib = ret; 1153 Vector3 unscaledContrib = ret;
1102 ret /= m_verticalAttractionTimescale; 1154 ret /= m_verticalAttractionTimescale;
1103 // This returns the angular correction desired. Timestep is added later. 1155
1104 // ret *= pTimestep; 1156 VDetailLog("{0}, MoveAngular,verticalAttraction,,verticalError={1},unscaled={2},vertForce={3},eff={4},vertAttr={5}",
1105 1157 Prim.LocalID, verticalError, unscaledContrib, vertForce, m_verticalAttractionEfficiency, ret);
1106 // apply efficiency
1107 Vector3 preEfficiencyContrib = ret;
1108 // TODO: implement efficiency.
1109 // Effenciency squared seems to give a more realistic effect
1110 float efficencySquared = m_verticalAttractionEfficiency * m_verticalAttractionEfficiency;
1111 // ret *= efficencySquared;
1112
1113 VDetailLog("{0}, MoveAngular,verticalAttraction,,verticalError={1},unscaled={2},preEff={3},eff={4},effSq={5},vertAttr={6}",
1114 Prim.LocalID, verticalError, unscaledContrib, preEfficiencyContrib,
1115 m_verticalAttractionEfficiency, efficencySquared,
1116 ret);
1117 } 1158 }
1118 return ret; 1159 return ret;
1119 } 1160 }
1120 1161
1121 // Return the angular correction to correct the direction the vehicle is pointing to be 1162 // Return the angular correction to correct the direction the vehicle is pointing to be
1122 // the direction is should want to be pointing. 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.
1123 public Vector3 ComputeAngularDeflection() 1167 public Vector3 ComputeAngularDeflection()
1124 { 1168 {
1125 Vector3 ret = Vector3.Zero; 1169 Vector3 ret = Vector3.Zero;
1170 return ret; // DEBUG DEBUG DEBUG debug one force at a time
1126 1171
1127 if (m_angularDeflectionEfficiency != 0) 1172 if (m_angularDeflectionEfficiency != 0)
1128 { 1173 {
1129 // Where the vehicle should want to point relative to the vehicle 1174 // The direction the vehicle is moving
1130 Vector3 preferredDirection = Vector3.UnitX * m_referenceFrame; 1175 Vector3 movingDirection = VehicleVelocity;
1176 movingDirection.Normalize();
1131 1177
1132 // Where the vehicle is pointing relative to the vehicle. 1178 // The direction the vehicle is pointing
1133 Vector3 currentDirection = Vector3.UnitX * Quaternion.Add(VehicleOrientation, m_referenceFrame); 1179 Vector3 pointingDirection = Vector3.UnitX * VehicleOrientation;
1180 pointingDirection.Normalize();
1134 1181
1135 // Difference between where vehicle is pointing and where it should wish to point 1182 // The difference between what is and what should be
1136 Vector3 directionCorrection = preferredDirection - currentDirection; 1183 Vector3 deflectionError = movingDirection - pointingDirection;
1137 1184
1138 // Scale the correction by recovery timescale and efficiency 1185 // Scale the correction by recovery timescale and efficiency
1139 ret = directionCorrection * m_angularDeflectionEfficiency / m_angularDeflectionTimescale; 1186 ret = (-deflectionError * VehicleForwardSpeed) * m_angularDeflectionEfficiency;
1187 ret /= m_angularDeflectionTimescale;
1140 1188
1141 VDetailLog("{0}, MoveAngular,Deflection,perfDir={1},currentDir={2},dirCorrection={3},ret={4}", 1189 VDetailLog("{0}, MoveAngular,Deflection,movingDir={1},pointingDir={2},deflectError={3},ret={4}",
1142 Prim.LocalID, preferredDirection, currentDirection, directionCorrection, ret); 1190 Prim.LocalID, movingDirection, pointingDirection, deflectionError, ret);
1143 } 1191 }
1144 return ret; 1192 return ret;
1145 } 1193 }
1146 1194
1147 // Return an angular change to tip the vehicle (around X axis) when turning (turned around Z). 1195 // Return an angular change to rotate the vehicle around the Z axis when the vehicle
1148 // Remembers the last banking value calculated and returns the difference needed this tick. 1196 // is tipped around the X axis.
1149 // TurningFactor is rate going left or right (pos=left, neg=right, scale=0..1). 1197 // From http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial:
1150 public Vector3 ComputeAngularBanking(float turningFactor) 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()
1151 { 1227 {
1152 Vector3 ret = Vector3.Zero; 1228 Vector3 ret = Vector3.Zero;
1153 Vector3 computedBanking = Vector3.Zero;
1154 1229
1155 if (m_bankingEfficiency != 0) 1230 if (m_bankingEfficiency != 0 && m_verticalAttractionTimescale < m_verticalAttractionCutoff)
1156 { 1231 {
1157 Vector3 currentDirection = Vector3.UnitX * VehicleOrientation; 1232 // This works by rotating a unit vector to the orientation of the vehicle. The
1158 1233 // roll (tilt) will be Y component of a tilting Z vector (zero for no tilt
1159 float mult = (m_bankingMix * m_bankingMix) * -1 * (m_bankingMix < 0 ? -1 : 1); 1234 // up to one for full over).
1235 Vector3 rollComponents = Vector3.UnitZ * VehicleOrientation;
1160 1236
1161 //Use the square of the efficiency, as it looks much more how SL banking works 1237 // Figure out the yaw value for this much roll.
1162 float effSquared = (m_bankingEfficiency * m_bankingEfficiency); 1238 float turnComponent = rollComponents.Y * rollComponents.Y * m_bankingEfficiency;
1163 if (m_bankingEfficiency < 0) 1239 // Keep the sign
1164 effSquared *= -1; //Keep the negative! 1240 if (rollComponents.Y < 0f)
1241 turnComponent = -turnComponent;
1165 1242
1166 float mix = Math.Abs(m_bankingMix); 1243 // TODO: there must be a better computation of the banking force.
1167 // TODO: Must include reference frame. 1244 float bankingTurnForce = turnComponent;
1168 float forwardSpeed = VehicleVelocity.X;
1169 1245
1170 if (!Prim.IsColliding && forwardSpeed > mix) 1246 // actual error = static turn error + dynamic turn error
1171 { 1247 float mixedBankingError = bankingTurnForce * (1f - m_bankingMix) + bankingTurnForce * m_bankingMix * VehicleForwardSpeed;
1172 computedBanking.X = ClampInRange(-3f, turningFactor * (effSquared * mult), 3f); 1248 // TODO: the banking effect should not go to infinity but what to limit it to?
1173 } 1249 mixedBankingError = ClampInRange(-20f, mixedBankingError, 20f);
1174 1250
1175 // 'computedBanking' is now how much banking that should be happening. 1251 // Build the force vector to change rotation from what it is to what it should be
1176 ret = computedBanking - m_lastBanking; 1252 ret.Z = -mixedBankingError;
1177 1253
1178 // Scale the correction by timescale and efficiency 1254 // Don't do it all at once.
1179 ret /= m_bankingTimescale * m_bankingEfficiency; 1255 ret /= m_bankingTimescale;
1180 1256
1181 VDetailLog("{0}, MoveAngular,Banking,computedB={1},lastB={2},bEff={3},effSq={4},mult={5},mix={6},banking={7}", 1257 VDetailLog("{0}, MoveAngular,Banking,rollComp={1},speed={2},turnComp={3},bankErr={4},mixedBankErr={5},ret={6}",
1182 Prim.LocalID, computedBanking, m_lastBanking, m_bankingEfficiency, effSquared, mult, mix, ret); 1258 Prim.LocalID, rollComponents, VehicleForwardSpeed, turnComponent, bankingTurnForce, mixedBankingError, ret);
1183 } 1259 }
1184 m_lastBanking = computedBanking;
1185 return ret; 1260 return ret;
1186 } 1261 }
1187 1262