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