diff options
Diffstat (limited to 'OpenSim/Region/PhysicsModules/BulletS/BSDynamics.cs')
-rw-r--r-- | OpenSim/Region/PhysicsModules/BulletS/BSDynamics.cs | 1800 |
1 files changed, 1800 insertions, 0 deletions
diff --git a/OpenSim/Region/PhysicsModules/BulletS/BSDynamics.cs b/OpenSim/Region/PhysicsModules/BulletS/BSDynamics.cs new file mode 100644 index 0000000..0fc5577 --- /dev/null +++ b/OpenSim/Region/PhysicsModules/BulletS/BSDynamics.cs | |||
@@ -0,0 +1,1800 @@ | |||
1 | /* | ||
2 | * Copyright (c) Contributors, http://opensimulator.org/ | ||
3 | * See CONTRIBUTORS.TXT for a full list of copyright holders. | ||
4 | * | ||
5 | * Redistribution and use in source and binary forms, with or without | ||
6 | * modification, are permitted provided that the following conditions are met: | ||
7 | * * Redistributions of source code must retain the above copyright | ||
8 | * notice, this list of conditions and the following disclaimer. | ||
9 | * * Redistributions in binary form must reproduce the above copyright | ||
10 | * notice, this list of conditions and the following disclaimer in the | ||
11 | * documentation and/or other materials provided with the distribution. | ||
12 | * * Neither the name of the OpenSimulator Project nor the | ||
13 | * names of its contributors may be used to endorse or promote products | ||
14 | * derived from this software without specific prior written permission. | ||
15 | * | ||
16 | * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY | ||
17 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED | ||
18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE | ||
19 | * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY | ||
20 | * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES | ||
21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | ||
22 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND | ||
23 | * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | ||
24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS | ||
25 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | ||
26 | * | ||
27 | * The quotations from http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial | ||
28 | * are Copyright (c) 2009 Linden Research, Inc and are used under their license | ||
29 | * of Creative Commons Attribution-Share Alike 3.0 | ||
30 | * (http://creativecommons.org/licenses/by-sa/3.0/). | ||
31 | */ | ||
32 | |||
33 | using System; | ||
34 | using System.Collections.Generic; | ||
35 | using System.Reflection; | ||
36 | using System.Runtime.InteropServices; | ||
37 | using OpenMetaverse; | ||
38 | using OpenSim.Framework; | ||
39 | using OpenSim.Region.PhysicsModules.SharedBase; | ||
40 | |||
41 | namespace OpenSim.Region.PhysicsModule.BulletS | ||
42 | { | ||
43 | public sealed class BSDynamics : BSActor | ||
44 | { | ||
45 | #pragma warning disable 414 | ||
46 | private static string LogHeader = "[BULLETSIM VEHICLE]"; | ||
47 | #pragma warning restore 414 | ||
48 | |||
49 | // the prim this dynamic controller belongs to | ||
50 | private BSPrimLinkable ControllingPrim { get; set; } | ||
51 | |||
52 | private bool m_haveRegisteredForSceneEvents; | ||
53 | |||
54 | // mass of the vehicle fetched each time we're calles | ||
55 | private float m_vehicleMass; | ||
56 | |||
57 | // Vehicle properties | ||
58 | public Vehicle Type { get; set; } | ||
59 | |||
60 | // private Quaternion m_referenceFrame = Quaternion.Identity; // Axis modifier | ||
61 | private VehicleFlag m_flags = (VehicleFlag) 0; // Boolean settings: | ||
62 | // HOVER_TERRAIN_ONLY | ||
63 | // HOVER_GLOBAL_HEIGHT | ||
64 | // NO_DEFLECTION_UP | ||
65 | // HOVER_WATER_ONLY | ||
66 | // HOVER_UP_ONLY | ||
67 | // LIMIT_MOTOR_UP | ||
68 | // LIMIT_ROLL_ONLY | ||
69 | private Vector3 m_BlockingEndPoint = Vector3.Zero; | ||
70 | private Quaternion m_RollreferenceFrame = Quaternion.Identity; | ||
71 | private Quaternion m_referenceFrame = Quaternion.Identity; | ||
72 | |||
73 | // Linear properties | ||
74 | private BSVMotor m_linearMotor = new BSVMotor("LinearMotor"); | ||
75 | private Vector3 m_linearMotorDirection = Vector3.Zero; // velocity requested by LSL, decayed by time | ||
76 | private Vector3 m_linearMotorOffset = Vector3.Zero; // the point of force can be offset from the center | ||
77 | private Vector3 m_linearMotorDirectionLASTSET = Vector3.Zero; // velocity requested by LSL | ||
78 | private Vector3 m_linearFrictionTimescale = Vector3.Zero; | ||
79 | private float m_linearMotorDecayTimescale = 1; | ||
80 | private float m_linearMotorTimescale = 1; | ||
81 | private Vector3 m_lastLinearVelocityVector = Vector3.Zero; | ||
82 | private Vector3 m_lastPositionVector = Vector3.Zero; | ||
83 | // private bool m_LinearMotorSetLastFrame = false; | ||
84 | // private Vector3 m_linearMotorOffset = Vector3.Zero; | ||
85 | |||
86 | //Angular properties | ||
87 | private BSVMotor m_angularMotor = new BSVMotor("AngularMotor"); | ||
88 | private Vector3 m_angularMotorDirection = Vector3.Zero; // angular velocity requested by LSL motor | ||
89 | // private int m_angularMotorApply = 0; // application frame counter | ||
90 | private Vector3 m_angularMotorVelocity = Vector3.Zero; // current angular motor velocity | ||
91 | private float m_angularMotorTimescale = 1; // motor angular velocity ramp up rate | ||
92 | private float m_angularMotorDecayTimescale = 1; // motor angular velocity decay rate | ||
93 | private Vector3 m_angularFrictionTimescale = Vector3.Zero; // body angular velocity decay rate | ||
94 | private Vector3 m_lastAngularVelocity = Vector3.Zero; | ||
95 | private Vector3 m_lastVertAttractor = Vector3.Zero; // what VA was last applied to body | ||
96 | |||
97 | //Deflection properties | ||
98 | private BSVMotor m_angularDeflectionMotor = new BSVMotor("AngularDeflection"); | ||
99 | private float m_angularDeflectionEfficiency = 0; | ||
100 | private float m_angularDeflectionTimescale = 0; | ||
101 | private float m_linearDeflectionEfficiency = 0; | ||
102 | private float m_linearDeflectionTimescale = 0; | ||
103 | |||
104 | //Banking properties | ||
105 | private float m_bankingEfficiency = 0; | ||
106 | private float m_bankingMix = 1; | ||
107 | private float m_bankingTimescale = 0; | ||
108 | |||
109 | //Hover and Buoyancy properties | ||
110 | private BSVMotor m_hoverMotor = new BSVMotor("Hover"); | ||
111 | private float m_VhoverHeight = 0f; | ||
112 | private float m_VhoverEfficiency = 0f; | ||
113 | private float m_VhoverTimescale = 0f; | ||
114 | private float m_VhoverTargetHeight = -1.0f; // if <0 then no hover, else its the current target height | ||
115 | // Modifies gravity. Slider between -1 (double-gravity) and 1 (full anti-gravity) | ||
116 | private float m_VehicleBuoyancy = 0f; | ||
117 | private Vector3 m_VehicleGravity = Vector3.Zero; // Gravity computed when buoyancy set | ||
118 | |||
119 | //Attractor properties | ||
120 | private BSVMotor m_verticalAttractionMotor = new BSVMotor("VerticalAttraction"); | ||
121 | private float m_verticalAttractionEfficiency = 1.0f; // damped | ||
122 | private float m_verticalAttractionCutoff = 500f; // per the documentation | ||
123 | // Timescale > cutoff means no vert attractor. | ||
124 | private float m_verticalAttractionTimescale = 510f; | ||
125 | |||
126 | // Just some recomputed constants: | ||
127 | #pragma warning disable 414 | ||
128 | static readonly float TwoPI = ((float)Math.PI) * 2f; | ||
129 | static readonly float FourPI = ((float)Math.PI) * 4f; | ||
130 | static readonly float PIOverFour = ((float)Math.PI) / 4f; | ||
131 | static readonly float PIOverTwo = ((float)Math.PI) / 2f; | ||
132 | #pragma warning restore 414 | ||
133 | |||
134 | public BSDynamics(BSScene myScene, BSPrim myPrim, string actorName) | ||
135 | : base(myScene, myPrim, actorName) | ||
136 | { | ||
137 | Type = Vehicle.TYPE_NONE; | ||
138 | m_haveRegisteredForSceneEvents = false; | ||
139 | |||
140 | ControllingPrim = myPrim as BSPrimLinkable; | ||
141 | if (ControllingPrim == null) | ||
142 | { | ||
143 | // THIS CANNOT HAPPEN!! | ||
144 | } | ||
145 | VDetailLog("{0},Creation", ControllingPrim.LocalID); | ||
146 | } | ||
147 | |||
148 | // Return 'true' if this vehicle is doing vehicle things | ||
149 | public bool IsActive | ||
150 | { | ||
151 | get { return (Type != Vehicle.TYPE_NONE && ControllingPrim.IsPhysicallyActive); } | ||
152 | } | ||
153 | |||
154 | // Return 'true' if this a vehicle that should be sitting on the ground | ||
155 | public bool IsGroundVehicle | ||
156 | { | ||
157 | get { return (Type == Vehicle.TYPE_CAR || Type == Vehicle.TYPE_SLED); } | ||
158 | } | ||
159 | |||
160 | #region Vehicle parameter setting | ||
161 | public void ProcessFloatVehicleParam(Vehicle pParam, float pValue) | ||
162 | { | ||
163 | VDetailLog("{0},ProcessFloatVehicleParam,param={1},val={2}", ControllingPrim.LocalID, pParam, pValue); | ||
164 | float clampTemp; | ||
165 | |||
166 | switch (pParam) | ||
167 | { | ||
168 | case Vehicle.ANGULAR_DEFLECTION_EFFICIENCY: | ||
169 | m_angularDeflectionEfficiency = ClampInRange(0f, pValue, 1f); | ||
170 | break; | ||
171 | case Vehicle.ANGULAR_DEFLECTION_TIMESCALE: | ||
172 | m_angularDeflectionTimescale = ClampInRange(0.25f, pValue, 120); | ||
173 | break; | ||
174 | case Vehicle.ANGULAR_MOTOR_DECAY_TIMESCALE: | ||
175 | m_angularMotorDecayTimescale = ClampInRange(0.25f, pValue, 120); | ||
176 | m_angularMotor.TargetValueDecayTimeScale = m_angularMotorDecayTimescale; | ||
177 | break; | ||
178 | case Vehicle.ANGULAR_MOTOR_TIMESCALE: | ||
179 | m_angularMotorTimescale = ClampInRange(0.25f, pValue, 120); | ||
180 | m_angularMotor.TimeScale = m_angularMotorTimescale; | ||
181 | break; | ||
182 | case Vehicle.BANKING_EFFICIENCY: | ||
183 | m_bankingEfficiency = ClampInRange(-1f, pValue, 1f); | ||
184 | break; | ||
185 | case Vehicle.BANKING_MIX: | ||
186 | m_bankingMix = ClampInRange(0.01f, pValue, 1); | ||
187 | break; | ||
188 | case Vehicle.BANKING_TIMESCALE: | ||
189 | m_bankingTimescale = ClampInRange(0.25f, pValue, 120); | ||
190 | break; | ||
191 | case Vehicle.BUOYANCY: | ||
192 | m_VehicleBuoyancy = ClampInRange(-1f, pValue, 1f); | ||
193 | m_VehicleGravity = ControllingPrim.ComputeGravity(m_VehicleBuoyancy); | ||
194 | break; | ||
195 | case Vehicle.HOVER_EFFICIENCY: | ||
196 | m_VhoverEfficiency = ClampInRange(0.01f, pValue, 1f); | ||
197 | break; | ||
198 | case Vehicle.HOVER_HEIGHT: | ||
199 | m_VhoverHeight = ClampInRange(0f, pValue, 1000000f); | ||
200 | break; | ||
201 | case Vehicle.HOVER_TIMESCALE: | ||
202 | m_VhoverTimescale = ClampInRange(0.01f, pValue, 120); | ||
203 | break; | ||
204 | case Vehicle.LINEAR_DEFLECTION_EFFICIENCY: | ||
205 | m_linearDeflectionEfficiency = ClampInRange(0f, pValue, 1f); | ||
206 | break; | ||
207 | case Vehicle.LINEAR_DEFLECTION_TIMESCALE: | ||
208 | m_linearDeflectionTimescale = ClampInRange(0.01f, pValue, 120); | ||
209 | break; | ||
210 | case Vehicle.LINEAR_MOTOR_DECAY_TIMESCALE: | ||
211 | m_linearMotorDecayTimescale = ClampInRange(0.01f, pValue, 120); | ||
212 | m_linearMotor.TargetValueDecayTimeScale = m_linearMotorDecayTimescale; | ||
213 | break; | ||
214 | case Vehicle.LINEAR_MOTOR_TIMESCALE: | ||
215 | m_linearMotorTimescale = ClampInRange(0.01f, pValue, 120); | ||
216 | m_linearMotor.TimeScale = m_linearMotorTimescale; | ||
217 | break; | ||
218 | case Vehicle.VERTICAL_ATTRACTION_EFFICIENCY: | ||
219 | m_verticalAttractionEfficiency = ClampInRange(0.1f, pValue, 1f); | ||
220 | m_verticalAttractionMotor.Efficiency = m_verticalAttractionEfficiency; | ||
221 | break; | ||
222 | case Vehicle.VERTICAL_ATTRACTION_TIMESCALE: | ||
223 | m_verticalAttractionTimescale = ClampInRange(0.01f, pValue, 120); | ||
224 | m_verticalAttractionMotor.TimeScale = m_verticalAttractionTimescale; | ||
225 | break; | ||
226 | |||
227 | // These are vector properties but the engine lets you use a single float value to | ||
228 | // set all of the components to the same value | ||
229 | case Vehicle.ANGULAR_FRICTION_TIMESCALE: | ||
230 | clampTemp = ClampInRange(0.01f, pValue, 120); | ||
231 | m_angularFrictionTimescale = new Vector3(clampTemp, clampTemp, clampTemp); | ||
232 | break; | ||
233 | case Vehicle.ANGULAR_MOTOR_DIRECTION: | ||
234 | clampTemp = ClampInRange(-TwoPI, pValue, TwoPI); | ||
235 | m_angularMotorDirection = new Vector3(clampTemp, clampTemp, clampTemp); | ||
236 | m_angularMotor.Zero(); | ||
237 | m_angularMotor.SetTarget(m_angularMotorDirection); | ||
238 | break; | ||
239 | case Vehicle.LINEAR_FRICTION_TIMESCALE: | ||
240 | clampTemp = ClampInRange(0.01f, pValue, 120); | ||
241 | m_linearFrictionTimescale = new Vector3(clampTemp, clampTemp, clampTemp); | ||
242 | break; | ||
243 | case Vehicle.LINEAR_MOTOR_DIRECTION: | ||
244 | clampTemp = ClampInRange(-BSParam.MaxLinearVelocity, pValue, BSParam.MaxLinearVelocity); | ||
245 | m_linearMotorDirection = new Vector3(clampTemp, clampTemp, clampTemp); | ||
246 | m_linearMotorDirectionLASTSET = new Vector3(clampTemp, clampTemp, clampTemp); | ||
247 | m_linearMotor.SetTarget(m_linearMotorDirection); | ||
248 | break; | ||
249 | case Vehicle.LINEAR_MOTOR_OFFSET: | ||
250 | clampTemp = ClampInRange(-1000, pValue, 1000); | ||
251 | m_linearMotorOffset = new Vector3(clampTemp, clampTemp, clampTemp); | ||
252 | break; | ||
253 | |||
254 | } | ||
255 | }//end ProcessFloatVehicleParam | ||
256 | |||
257 | internal void ProcessVectorVehicleParam(Vehicle pParam, Vector3 pValue) | ||
258 | { | ||
259 | VDetailLog("{0},ProcessVectorVehicleParam,param={1},val={2}", ControllingPrim.LocalID, pParam, pValue); | ||
260 | switch (pParam) | ||
261 | { | ||
262 | case Vehicle.ANGULAR_FRICTION_TIMESCALE: | ||
263 | pValue.X = ClampInRange(0.25f, pValue.X, 120); | ||
264 | pValue.Y = ClampInRange(0.25f, pValue.Y, 120); | ||
265 | pValue.Z = ClampInRange(0.25f, pValue.Z, 120); | ||
266 | m_angularFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z); | ||
267 | break; | ||
268 | case Vehicle.ANGULAR_MOTOR_DIRECTION: | ||
269 | // Limit requested angular speed to 2 rps= 4 pi rads/sec | ||
270 | pValue.X = ClampInRange(-FourPI, pValue.X, FourPI); | ||
271 | pValue.Y = ClampInRange(-FourPI, pValue.Y, FourPI); | ||
272 | pValue.Z = ClampInRange(-FourPI, pValue.Z, FourPI); | ||
273 | m_angularMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z); | ||
274 | m_angularMotor.Zero(); | ||
275 | m_angularMotor.SetTarget(m_angularMotorDirection); | ||
276 | break; | ||
277 | case Vehicle.LINEAR_FRICTION_TIMESCALE: | ||
278 | pValue.X = ClampInRange(0.25f, pValue.X, 120); | ||
279 | pValue.Y = ClampInRange(0.25f, pValue.Y, 120); | ||
280 | pValue.Z = ClampInRange(0.25f, pValue.Z, 120); | ||
281 | m_linearFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z); | ||
282 | break; | ||
283 | case Vehicle.LINEAR_MOTOR_DIRECTION: | ||
284 | pValue.X = ClampInRange(-BSParam.MaxLinearVelocity, pValue.X, BSParam.MaxLinearVelocity); | ||
285 | pValue.Y = ClampInRange(-BSParam.MaxLinearVelocity, pValue.Y, BSParam.MaxLinearVelocity); | ||
286 | pValue.Z = ClampInRange(-BSParam.MaxLinearVelocity, pValue.Z, BSParam.MaxLinearVelocity); | ||
287 | m_linearMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z); | ||
288 | m_linearMotorDirectionLASTSET = new Vector3(pValue.X, pValue.Y, pValue.Z); | ||
289 | m_linearMotor.SetTarget(m_linearMotorDirection); | ||
290 | break; | ||
291 | case Vehicle.LINEAR_MOTOR_OFFSET: | ||
292 | // Not sure the correct range to limit this variable | ||
293 | pValue.X = ClampInRange(-1000, pValue.X, 1000); | ||
294 | pValue.Y = ClampInRange(-1000, pValue.Y, 1000); | ||
295 | pValue.Z = ClampInRange(-1000, pValue.Z, 1000); | ||
296 | m_linearMotorOffset = new Vector3(pValue.X, pValue.Y, pValue.Z); | ||
297 | break; | ||
298 | case Vehicle.BLOCK_EXIT: | ||
299 | // Not sure the correct range to limit this variable | ||
300 | pValue.X = ClampInRange(-10000, pValue.X, 10000); | ||
301 | pValue.Y = ClampInRange(-10000, pValue.Y, 10000); | ||
302 | pValue.Z = ClampInRange(-10000, pValue.Z, 10000); | ||
303 | m_BlockingEndPoint = new Vector3(pValue.X, pValue.Y, pValue.Z); | ||
304 | break; | ||
305 | } | ||
306 | }//end ProcessVectorVehicleParam | ||
307 | |||
308 | internal void ProcessRotationVehicleParam(Vehicle pParam, Quaternion pValue) | ||
309 | { | ||
310 | VDetailLog("{0},ProcessRotationalVehicleParam,param={1},val={2}", ControllingPrim.LocalID, pParam, pValue); | ||
311 | switch (pParam) | ||
312 | { | ||
313 | case Vehicle.REFERENCE_FRAME: | ||
314 | m_referenceFrame = pValue; | ||
315 | break; | ||
316 | case Vehicle.ROLL_FRAME: | ||
317 | m_RollreferenceFrame = pValue; | ||
318 | break; | ||
319 | } | ||
320 | }//end ProcessRotationVehicleParam | ||
321 | |||
322 | internal void ProcessVehicleFlags(int pParam, bool remove) | ||
323 | { | ||
324 | VDetailLog("{0},ProcessVehicleFlags,param={1},remove={2}", ControllingPrim.LocalID, pParam, remove); | ||
325 | VehicleFlag parm = (VehicleFlag)pParam; | ||
326 | if (pParam == -1) | ||
327 | m_flags = (VehicleFlag)0; | ||
328 | else | ||
329 | { | ||
330 | if (remove) | ||
331 | m_flags &= ~parm; | ||
332 | else | ||
333 | m_flags |= parm; | ||
334 | } | ||
335 | } | ||
336 | |||
337 | public void ProcessTypeChange(Vehicle pType) | ||
338 | { | ||
339 | VDetailLog("{0},ProcessTypeChange,type={1}", ControllingPrim.LocalID, pType); | ||
340 | // Set Defaults For Type | ||
341 | Type = pType; | ||
342 | switch (pType) | ||
343 | { | ||
344 | case Vehicle.TYPE_NONE: | ||
345 | m_linearMotorDirection = Vector3.Zero; | ||
346 | m_linearMotorTimescale = 0; | ||
347 | m_linearMotorDecayTimescale = 0; | ||
348 | m_linearFrictionTimescale = new Vector3(0, 0, 0); | ||
349 | |||
350 | m_angularMotorDirection = Vector3.Zero; | ||
351 | m_angularMotorDecayTimescale = 0; | ||
352 | m_angularMotorTimescale = 0; | ||
353 | m_angularFrictionTimescale = new Vector3(0, 0, 0); | ||
354 | |||
355 | m_VhoverHeight = 0; | ||
356 | m_VhoverEfficiency = 0; | ||
357 | m_VhoverTimescale = 0; | ||
358 | m_VehicleBuoyancy = 0; | ||
359 | |||
360 | m_linearDeflectionEfficiency = 1; | ||
361 | m_linearDeflectionTimescale = 1; | ||
362 | |||
363 | m_angularDeflectionEfficiency = 0; | ||
364 | m_angularDeflectionTimescale = 1000; | ||
365 | |||
366 | m_verticalAttractionEfficiency = 0; | ||
367 | m_verticalAttractionTimescale = 0; | ||
368 | |||
369 | m_bankingEfficiency = 0; | ||
370 | m_bankingTimescale = 1000; | ||
371 | m_bankingMix = 1; | ||
372 | |||
373 | m_referenceFrame = Quaternion.Identity; | ||
374 | m_flags = (VehicleFlag)0; | ||
375 | |||
376 | break; | ||
377 | |||
378 | case Vehicle.TYPE_SLED: | ||
379 | m_linearMotorDirection = Vector3.Zero; | ||
380 | m_linearMotorTimescale = 1000; | ||
381 | m_linearMotorDecayTimescale = 120; | ||
382 | m_linearFrictionTimescale = new Vector3(30, 1, 1000); | ||
383 | |||
384 | m_angularMotorDirection = Vector3.Zero; | ||
385 | m_angularMotorTimescale = 1000; | ||
386 | m_angularMotorDecayTimescale = 120; | ||
387 | m_angularFrictionTimescale = new Vector3(1000, 1000, 1000); | ||
388 | |||
389 | m_VhoverHeight = 0; | ||
390 | m_VhoverEfficiency = 10; // TODO: this looks wrong!! | ||
391 | m_VhoverTimescale = 10; | ||
392 | m_VehicleBuoyancy = 0; | ||
393 | |||
394 | m_linearDeflectionEfficiency = 1; | ||
395 | m_linearDeflectionTimescale = 1; | ||
396 | |||
397 | m_angularDeflectionEfficiency = 1; | ||
398 | m_angularDeflectionTimescale = 1000; | ||
399 | |||
400 | m_verticalAttractionEfficiency = 0; | ||
401 | m_verticalAttractionTimescale = 0; | ||
402 | |||
403 | m_bankingEfficiency = 0; | ||
404 | m_bankingTimescale = 10; | ||
405 | m_bankingMix = 1; | ||
406 | |||
407 | m_referenceFrame = Quaternion.Identity; | ||
408 | m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY | ||
409 | | VehicleFlag.HOVER_TERRAIN_ONLY | ||
410 | | VehicleFlag.HOVER_GLOBAL_HEIGHT | ||
411 | | VehicleFlag.HOVER_UP_ONLY); | ||
412 | m_flags |= (VehicleFlag.NO_DEFLECTION_UP | ||
413 | | VehicleFlag.LIMIT_ROLL_ONLY | ||
414 | | VehicleFlag.LIMIT_MOTOR_UP); | ||
415 | |||
416 | break; | ||
417 | case Vehicle.TYPE_CAR: | ||
418 | m_linearMotorDirection = Vector3.Zero; | ||
419 | m_linearMotorTimescale = 1; | ||
420 | m_linearMotorDecayTimescale = 60; | ||
421 | m_linearFrictionTimescale = new Vector3(100, 2, 1000); | ||
422 | |||
423 | m_angularMotorDirection = Vector3.Zero; | ||
424 | m_angularMotorTimescale = 1; | ||
425 | m_angularMotorDecayTimescale = 0.8f; | ||
426 | m_angularFrictionTimescale = new Vector3(1000, 1000, 1000); | ||
427 | |||
428 | m_VhoverHeight = 0; | ||
429 | m_VhoverEfficiency = 0; | ||
430 | m_VhoverTimescale = 1000; | ||
431 | m_VehicleBuoyancy = 0; | ||
432 | |||
433 | m_linearDeflectionEfficiency = 1; | ||
434 | m_linearDeflectionTimescale = 2; | ||
435 | |||
436 | m_angularDeflectionEfficiency = 0; | ||
437 | m_angularDeflectionTimescale = 10; | ||
438 | |||
439 | m_verticalAttractionEfficiency = 1f; | ||
440 | m_verticalAttractionTimescale = 10f; | ||
441 | |||
442 | m_bankingEfficiency = -0.2f; | ||
443 | m_bankingMix = 1; | ||
444 | m_bankingTimescale = 1; | ||
445 | |||
446 | m_referenceFrame = Quaternion.Identity; | ||
447 | m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY | ||
448 | | VehicleFlag.HOVER_TERRAIN_ONLY | ||
449 | | VehicleFlag.HOVER_GLOBAL_HEIGHT); | ||
450 | m_flags |= (VehicleFlag.NO_DEFLECTION_UP | ||
451 | | VehicleFlag.LIMIT_ROLL_ONLY | ||
452 | | VehicleFlag.LIMIT_MOTOR_UP | ||
453 | | VehicleFlag.HOVER_UP_ONLY); | ||
454 | break; | ||
455 | case Vehicle.TYPE_BOAT: | ||
456 | m_linearMotorDirection = Vector3.Zero; | ||
457 | m_linearMotorTimescale = 5; | ||
458 | m_linearMotorDecayTimescale = 60; | ||
459 | m_linearFrictionTimescale = new Vector3(10, 3, 2); | ||
460 | |||
461 | m_angularMotorDirection = Vector3.Zero; | ||
462 | m_angularMotorTimescale = 4; | ||
463 | m_angularMotorDecayTimescale = 4; | ||
464 | m_angularFrictionTimescale = new Vector3(10,10,10); | ||
465 | |||
466 | m_VhoverHeight = 0; | ||
467 | m_VhoverEfficiency = 0.5f; | ||
468 | m_VhoverTimescale = 2; | ||
469 | m_VehicleBuoyancy = 1; | ||
470 | |||
471 | m_linearDeflectionEfficiency = 0.5f; | ||
472 | m_linearDeflectionTimescale = 3; | ||
473 | |||
474 | m_angularDeflectionEfficiency = 0.5f; | ||
475 | m_angularDeflectionTimescale = 5; | ||
476 | |||
477 | m_verticalAttractionEfficiency = 0.5f; | ||
478 | m_verticalAttractionTimescale = 5f; | ||
479 | |||
480 | m_bankingEfficiency = -0.3f; | ||
481 | m_bankingMix = 0.8f; | ||
482 | m_bankingTimescale = 1; | ||
483 | |||
484 | m_referenceFrame = Quaternion.Identity; | ||
485 | m_flags &= ~(VehicleFlag.HOVER_TERRAIN_ONLY | ||
486 | | VehicleFlag.HOVER_GLOBAL_HEIGHT | ||
487 | | VehicleFlag.LIMIT_ROLL_ONLY | ||
488 | | VehicleFlag.HOVER_UP_ONLY); | ||
489 | m_flags |= (VehicleFlag.NO_DEFLECTION_UP | ||
490 | | VehicleFlag.LIMIT_MOTOR_UP | ||
491 | | VehicleFlag.HOVER_WATER_ONLY); | ||
492 | break; | ||
493 | case Vehicle.TYPE_AIRPLANE: | ||
494 | m_linearMotorDirection = Vector3.Zero; | ||
495 | m_linearMotorTimescale = 2; | ||
496 | m_linearMotorDecayTimescale = 60; | ||
497 | m_linearFrictionTimescale = new Vector3(200, 10, 5); | ||
498 | |||
499 | m_angularMotorDirection = Vector3.Zero; | ||
500 | m_angularMotorTimescale = 4; | ||
501 | m_angularMotorDecayTimescale = 4; | ||
502 | m_angularFrictionTimescale = new Vector3(20, 20, 20); | ||
503 | |||
504 | m_VhoverHeight = 0; | ||
505 | m_VhoverEfficiency = 0.5f; | ||
506 | m_VhoverTimescale = 1000; | ||
507 | m_VehicleBuoyancy = 0; | ||
508 | |||
509 | m_linearDeflectionEfficiency = 0.5f; | ||
510 | m_linearDeflectionTimescale = 3; | ||
511 | |||
512 | m_angularDeflectionEfficiency = 1; | ||
513 | m_angularDeflectionTimescale = 2; | ||
514 | |||
515 | m_verticalAttractionEfficiency = 0.9f; | ||
516 | m_verticalAttractionTimescale = 2f; | ||
517 | |||
518 | m_bankingEfficiency = 1; | ||
519 | m_bankingMix = 0.7f; | ||
520 | m_bankingTimescale = 2; | ||
521 | |||
522 | m_referenceFrame = Quaternion.Identity; | ||
523 | m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY | ||
524 | | VehicleFlag.HOVER_TERRAIN_ONLY | ||
525 | | VehicleFlag.HOVER_GLOBAL_HEIGHT | ||
526 | | VehicleFlag.HOVER_UP_ONLY | ||
527 | | VehicleFlag.NO_DEFLECTION_UP | ||
528 | | VehicleFlag.LIMIT_MOTOR_UP); | ||
529 | m_flags |= (VehicleFlag.LIMIT_ROLL_ONLY); | ||
530 | break; | ||
531 | case Vehicle.TYPE_BALLOON: | ||
532 | m_linearMotorDirection = Vector3.Zero; | ||
533 | m_linearMotorTimescale = 5; | ||
534 | m_linearFrictionTimescale = new Vector3(5, 5, 5); | ||
535 | m_linearMotorDecayTimescale = 60; | ||
536 | |||
537 | m_angularMotorDirection = Vector3.Zero; | ||
538 | m_angularMotorTimescale = 6; | ||
539 | m_angularFrictionTimescale = new Vector3(10, 10, 10); | ||
540 | m_angularMotorDecayTimescale = 10; | ||
541 | |||
542 | m_VhoverHeight = 5; | ||
543 | m_VhoverEfficiency = 0.8f; | ||
544 | m_VhoverTimescale = 10; | ||
545 | m_VehicleBuoyancy = 1; | ||
546 | |||
547 | m_linearDeflectionEfficiency = 0; | ||
548 | m_linearDeflectionTimescale = 5; | ||
549 | |||
550 | m_angularDeflectionEfficiency = 0; | ||
551 | m_angularDeflectionTimescale = 5; | ||
552 | |||
553 | m_verticalAttractionEfficiency = 1f; | ||
554 | m_verticalAttractionTimescale = 100f; | ||
555 | |||
556 | m_bankingEfficiency = 0; | ||
557 | m_bankingMix = 0.7f; | ||
558 | m_bankingTimescale = 5; | ||
559 | |||
560 | m_referenceFrame = Quaternion.Identity; | ||
561 | |||
562 | m_referenceFrame = Quaternion.Identity; | ||
563 | m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY | ||
564 | | VehicleFlag.HOVER_TERRAIN_ONLY | ||
565 | | VehicleFlag.HOVER_UP_ONLY | ||
566 | | VehicleFlag.NO_DEFLECTION_UP | ||
567 | | VehicleFlag.LIMIT_MOTOR_UP); | ||
568 | m_flags |= (VehicleFlag.LIMIT_ROLL_ONLY | ||
569 | | VehicleFlag.HOVER_GLOBAL_HEIGHT); | ||
570 | break; | ||
571 | } | ||
572 | |||
573 | m_linearMotor = new BSVMotor("LinearMotor", m_linearMotorTimescale, m_linearMotorDecayTimescale, 1f); | ||
574 | // m_linearMotor.PhysicsScene = m_physicsScene; // DEBUG DEBUG DEBUG (enables detail logging) | ||
575 | |||
576 | m_angularMotor = new BSVMotor("AngularMotor", m_angularMotorTimescale, m_angularMotorDecayTimescale, 1f); | ||
577 | // m_angularMotor.PhysicsScene = m_physicsScene; // DEBUG DEBUG DEBUG (enables detail logging) | ||
578 | |||
579 | /* Not implemented | ||
580 | m_verticalAttractionMotor = new BSVMotor("VerticalAttraction", m_verticalAttractionTimescale, | ||
581 | BSMotor.Infinite, BSMotor.InfiniteVector, | ||
582 | m_verticalAttractionEfficiency); | ||
583 | // Z goes away and we keep X and Y | ||
584 | m_verticalAttractionMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG DEBUG (enables detail logging) | ||
585 | */ | ||
586 | |||
587 | if (this.Type == Vehicle.TYPE_NONE) | ||
588 | { | ||
589 | UnregisterForSceneEvents(); | ||
590 | } | ||
591 | else | ||
592 | { | ||
593 | RegisterForSceneEvents(); | ||
594 | } | ||
595 | |||
596 | // Update any physical parameters based on this type. | ||
597 | Refresh(); | ||
598 | } | ||
599 | #endregion // Vehicle parameter setting | ||
600 | |||
601 | // BSActor.Refresh() | ||
602 | public override void Refresh() | ||
603 | { | ||
604 | // If asking for a refresh, reset the physical parameters before the next simulation step. | ||
605 | // Called whether active or not since the active state may be updated before the next step. | ||
606 | m_physicsScene.PostTaintObject("BSDynamics.Refresh", ControllingPrim.LocalID, delegate() | ||
607 | { | ||
608 | SetPhysicalParameters(); | ||
609 | }); | ||
610 | } | ||
611 | |||
612 | // Some of the properties of this prim may have changed. | ||
613 | // Do any updating needed for a vehicle | ||
614 | private void SetPhysicalParameters() | ||
615 | { | ||
616 | if (IsActive) | ||
617 | { | ||
618 | // Remember the mass so we don't have to fetch it every step | ||
619 | m_vehicleMass = ControllingPrim.TotalMass; | ||
620 | |||
621 | // Friction affects are handled by this vehicle code | ||
622 | // m_physicsScene.PE.SetFriction(ControllingPrim.PhysBody, BSParam.VehicleFriction); | ||
623 | // m_physicsScene.PE.SetRestitution(ControllingPrim.PhysBody, BSParam.VehicleRestitution); | ||
624 | ControllingPrim.Linkset.SetPhysicalFriction(BSParam.VehicleFriction); | ||
625 | ControllingPrim.Linkset.SetPhysicalRestitution(BSParam.VehicleRestitution); | ||
626 | |||
627 | // Moderate angular movement introduced by Bullet. | ||
628 | // TODO: possibly set AngularFactor and LinearFactor for the type of vehicle. | ||
629 | // Maybe compute linear and angular factor and damping from params. | ||
630 | m_physicsScene.PE.SetAngularDamping(ControllingPrim.PhysBody, BSParam.VehicleAngularDamping); | ||
631 | m_physicsScene.PE.SetLinearFactor(ControllingPrim.PhysBody, BSParam.VehicleLinearFactor); | ||
632 | m_physicsScene.PE.SetAngularFactorV(ControllingPrim.PhysBody, BSParam.VehicleAngularFactor); | ||
633 | |||
634 | // Vehicles report collision events so we know when it's on the ground | ||
635 | // m_physicsScene.PE.AddToCollisionFlags(ControllingPrim.PhysBody, CollisionFlags.BS_VEHICLE_COLLISIONS); | ||
636 | ControllingPrim.Linkset.AddToPhysicalCollisionFlags(CollisionFlags.BS_VEHICLE_COLLISIONS); | ||
637 | |||
638 | // Vector3 inertia = m_physicsScene.PE.CalculateLocalInertia(ControllingPrim.PhysShape.physShapeInfo, m_vehicleMass); | ||
639 | // ControllingPrim.Inertia = inertia * BSParam.VehicleInertiaFactor; | ||
640 | // m_physicsScene.PE.SetMassProps(ControllingPrim.PhysBody, m_vehicleMass, ControllingPrim.Inertia); | ||
641 | // m_physicsScene.PE.UpdateInertiaTensor(ControllingPrim.PhysBody); | ||
642 | ControllingPrim.Linkset.ComputeAndSetLocalInertia(BSParam.VehicleInertiaFactor, m_vehicleMass); | ||
643 | |||
644 | // Set the gravity for the vehicle depending on the buoyancy | ||
645 | // TODO: what should be done if prim and vehicle buoyancy differ? | ||
646 | m_VehicleGravity = ControllingPrim.ComputeGravity(m_VehicleBuoyancy); | ||
647 | // The actual vehicle gravity is set to zero in Bullet so we can do all the application of same. | ||
648 | // m_physicsScene.PE.SetGravity(ControllingPrim.PhysBody, Vector3.Zero); | ||
649 | ControllingPrim.Linkset.SetPhysicalGravity(Vector3.Zero); | ||
650 | |||
651 | VDetailLog("{0},BSDynamics.SetPhysicalParameters,mass={1},inert={2},vehGrav={3},aDamp={4},frict={5},rest={6},lFact={7},aFact={8}", | ||
652 | ControllingPrim.LocalID, m_vehicleMass, ControllingPrim.Inertia, m_VehicleGravity, | ||
653 | BSParam.VehicleAngularDamping, BSParam.VehicleFriction, BSParam.VehicleRestitution, | ||
654 | BSParam.VehicleLinearFactor, BSParam.VehicleAngularFactor | ||
655 | ); | ||
656 | } | ||
657 | else | ||
658 | { | ||
659 | if (ControllingPrim.PhysBody.HasPhysicalBody) | ||
660 | m_physicsScene.PE.RemoveFromCollisionFlags(ControllingPrim.PhysBody, CollisionFlags.BS_VEHICLE_COLLISIONS); | ||
661 | // ControllingPrim.Linkset.RemoveFromPhysicalCollisionFlags(CollisionFlags.BS_VEHICLE_COLLISIONS); | ||
662 | } | ||
663 | } | ||
664 | |||
665 | // BSActor.RemoveBodyDependencies | ||
666 | public override void RemoveDependencies() | ||
667 | { | ||
668 | Refresh(); | ||
669 | } | ||
670 | |||
671 | // BSActor.Release() | ||
672 | public override void Dispose() | ||
673 | { | ||
674 | VDetailLog("{0},Dispose", ControllingPrim.LocalID); | ||
675 | UnregisterForSceneEvents(); | ||
676 | Type = Vehicle.TYPE_NONE; | ||
677 | Enabled = false; | ||
678 | return; | ||
679 | } | ||
680 | |||
681 | private void RegisterForSceneEvents() | ||
682 | { | ||
683 | if (!m_haveRegisteredForSceneEvents) | ||
684 | { | ||
685 | m_physicsScene.BeforeStep += this.Step; | ||
686 | m_physicsScene.AfterStep += this.PostStep; | ||
687 | ControllingPrim.OnPreUpdateProperty += this.PreUpdateProperty; | ||
688 | m_haveRegisteredForSceneEvents = true; | ||
689 | } | ||
690 | } | ||
691 | |||
692 | private void UnregisterForSceneEvents() | ||
693 | { | ||
694 | if (m_haveRegisteredForSceneEvents) | ||
695 | { | ||
696 | m_physicsScene.BeforeStep -= this.Step; | ||
697 | m_physicsScene.AfterStep -= this.PostStep; | ||
698 | ControllingPrim.OnPreUpdateProperty -= this.PreUpdateProperty; | ||
699 | m_haveRegisteredForSceneEvents = false; | ||
700 | } | ||
701 | } | ||
702 | |||
703 | private void PreUpdateProperty(ref EntityProperties entprop) | ||
704 | { | ||
705 | // A temporary kludge to suppress the rotational effects introduced on vehicles by Bullet | ||
706 | // TODO: handle physics introduced by Bullet with computed vehicle physics. | ||
707 | if (IsActive) | ||
708 | { | ||
709 | entprop.RotationalVelocity = Vector3.Zero; | ||
710 | } | ||
711 | } | ||
712 | |||
713 | #region Known vehicle value functions | ||
714 | // Vehicle physical parameters that we buffer from constant getting and setting. | ||
715 | // The "m_known*" values are unknown until they are fetched and the m_knownHas flag is set. | ||
716 | // Changing is remembered and the parameter is stored back into the physics engine only if updated. | ||
717 | // This does two things: 1) saves continuious calls into unmanaged code, and | ||
718 | // 2) signals when a physics property update must happen back to the simulator | ||
719 | // to update values modified for the vehicle. | ||
720 | private int m_knownChanged; | ||
721 | private int m_knownHas; | ||
722 | private float m_knownTerrainHeight; | ||
723 | private float m_knownWaterLevel; | ||
724 | private Vector3 m_knownPosition; | ||
725 | private Vector3 m_knownVelocity; | ||
726 | private Vector3 m_knownForce; | ||
727 | private Vector3 m_knownForceImpulse; | ||
728 | private Quaternion m_knownOrientation; | ||
729 | private Vector3 m_knownRotationalVelocity; | ||
730 | private Vector3 m_knownRotationalForce; | ||
731 | private Vector3 m_knownRotationalImpulse; | ||
732 | |||
733 | private const int m_knownChangedPosition = 1 << 0; | ||
734 | private const int m_knownChangedVelocity = 1 << 1; | ||
735 | private const int m_knownChangedForce = 1 << 2; | ||
736 | private const int m_knownChangedForceImpulse = 1 << 3; | ||
737 | private const int m_knownChangedOrientation = 1 << 4; | ||
738 | private const int m_knownChangedRotationalVelocity = 1 << 5; | ||
739 | private const int m_knownChangedRotationalForce = 1 << 6; | ||
740 | private const int m_knownChangedRotationalImpulse = 1 << 7; | ||
741 | private const int m_knownChangedTerrainHeight = 1 << 8; | ||
742 | private const int m_knownChangedWaterLevel = 1 << 9; | ||
743 | |||
744 | public void ForgetKnownVehicleProperties() | ||
745 | { | ||
746 | m_knownHas = 0; | ||
747 | m_knownChanged = 0; | ||
748 | } | ||
749 | // Push all the changed values back into the physics engine | ||
750 | public void PushKnownChanged() | ||
751 | { | ||
752 | if (m_knownChanged != 0) | ||
753 | { | ||
754 | if ((m_knownChanged & m_knownChangedPosition) != 0) | ||
755 | ControllingPrim.ForcePosition = m_knownPosition; | ||
756 | |||
757 | if ((m_knownChanged & m_knownChangedOrientation) != 0) | ||
758 | ControllingPrim.ForceOrientation = m_knownOrientation; | ||
759 | |||
760 | if ((m_knownChanged & m_knownChangedVelocity) != 0) | ||
761 | { | ||
762 | ControllingPrim.ForceVelocity = m_knownVelocity; | ||
763 | // Fake out Bullet by making it think the velocity is the same as last time. | ||
764 | // Bullet does a bunch of smoothing for changing parameters. | ||
765 | // Since the vehicle is demanding this setting, we override Bullet's smoothing | ||
766 | // by telling Bullet the value was the same last time. | ||
767 | // PhysicsScene.PE.SetInterpolationLinearVelocity(Prim.PhysBody, m_knownVelocity); | ||
768 | } | ||
769 | |||
770 | if ((m_knownChanged & m_knownChangedForce) != 0) | ||
771 | ControllingPrim.AddForce((Vector3)m_knownForce, false /*pushForce*/, true /*inTaintTime*/); | ||
772 | |||
773 | if ((m_knownChanged & m_knownChangedForceImpulse) != 0) | ||
774 | ControllingPrim.AddForceImpulse((Vector3)m_knownForceImpulse, false /*pushforce*/, true /*inTaintTime*/); | ||
775 | |||
776 | if ((m_knownChanged & m_knownChangedRotationalVelocity) != 0) | ||
777 | { | ||
778 | ControllingPrim.ForceRotationalVelocity = m_knownRotationalVelocity; | ||
779 | // PhysicsScene.PE.SetInterpolationAngularVelocity(Prim.PhysBody, m_knownRotationalVelocity); | ||
780 | } | ||
781 | |||
782 | if ((m_knownChanged & m_knownChangedRotationalImpulse) != 0) | ||
783 | ControllingPrim.ApplyTorqueImpulse((Vector3)m_knownRotationalImpulse, true /*inTaintTime*/); | ||
784 | |||
785 | if ((m_knownChanged & m_knownChangedRotationalForce) != 0) | ||
786 | { | ||
787 | ControllingPrim.AddAngularForce((Vector3)m_knownRotationalForce, false /*pushForce*/, true /*inTaintTime*/); | ||
788 | } | ||
789 | |||
790 | // If we set one of the values (ie, the physics engine didn't do it) we must force | ||
791 | // an UpdateProperties event to send the changes up to the simulator. | ||
792 | m_physicsScene.PE.PushUpdate(ControllingPrim.PhysBody); | ||
793 | } | ||
794 | m_knownChanged = 0; | ||
795 | } | ||
796 | |||
797 | // Since the computation of terrain height can be a little involved, this routine | ||
798 | // is used to fetch the height only once for each vehicle simulation step. | ||
799 | Vector3 lastRememberedHeightPos = new Vector3(-1, -1, -1); | ||
800 | private float GetTerrainHeight(Vector3 pos) | ||
801 | { | ||
802 | if ((m_knownHas & m_knownChangedTerrainHeight) == 0 || pos != lastRememberedHeightPos) | ||
803 | { | ||
804 | lastRememberedHeightPos = pos; | ||
805 | m_knownTerrainHeight = ControllingPrim.PhysScene.TerrainManager.GetTerrainHeightAtXYZ(pos); | ||
806 | m_knownHas |= m_knownChangedTerrainHeight; | ||
807 | } | ||
808 | return m_knownTerrainHeight; | ||
809 | } | ||
810 | |||
811 | // Since the computation of water level can be a little involved, this routine | ||
812 | // is used ot fetch the level only once for each vehicle simulation step. | ||
813 | Vector3 lastRememberedWaterHeightPos = new Vector3(-1, -1, -1); | ||
814 | private float GetWaterLevel(Vector3 pos) | ||
815 | { | ||
816 | if ((m_knownHas & m_knownChangedWaterLevel) == 0 || pos != lastRememberedWaterHeightPos) | ||
817 | { | ||
818 | lastRememberedWaterHeightPos = pos; | ||
819 | m_knownWaterLevel = ControllingPrim.PhysScene.TerrainManager.GetWaterLevelAtXYZ(pos); | ||
820 | m_knownHas |= m_knownChangedWaterLevel; | ||
821 | } | ||
822 | return m_knownWaterLevel; | ||
823 | } | ||
824 | |||
825 | private Vector3 VehiclePosition | ||
826 | { | ||
827 | get | ||
828 | { | ||
829 | if ((m_knownHas & m_knownChangedPosition) == 0) | ||
830 | { | ||
831 | m_knownPosition = ControllingPrim.ForcePosition; | ||
832 | m_knownHas |= m_knownChangedPosition; | ||
833 | } | ||
834 | return m_knownPosition; | ||
835 | } | ||
836 | set | ||
837 | { | ||
838 | m_knownPosition = value; | ||
839 | m_knownChanged |= m_knownChangedPosition; | ||
840 | m_knownHas |= m_knownChangedPosition; | ||
841 | } | ||
842 | } | ||
843 | |||
844 | private Quaternion VehicleOrientation | ||
845 | { | ||
846 | get | ||
847 | { | ||
848 | if ((m_knownHas & m_knownChangedOrientation) == 0) | ||
849 | { | ||
850 | m_knownOrientation = ControllingPrim.ForceOrientation; | ||
851 | m_knownHas |= m_knownChangedOrientation; | ||
852 | } | ||
853 | return m_knownOrientation; | ||
854 | } | ||
855 | set | ||
856 | { | ||
857 | m_knownOrientation = value; | ||
858 | m_knownChanged |= m_knownChangedOrientation; | ||
859 | m_knownHas |= m_knownChangedOrientation; | ||
860 | } | ||
861 | } | ||
862 | |||
863 | private Vector3 VehicleVelocity | ||
864 | { | ||
865 | get | ||
866 | { | ||
867 | if ((m_knownHas & m_knownChangedVelocity) == 0) | ||
868 | { | ||
869 | m_knownVelocity = ControllingPrim.ForceVelocity; | ||
870 | m_knownHas |= m_knownChangedVelocity; | ||
871 | } | ||
872 | return m_knownVelocity; | ||
873 | } | ||
874 | set | ||
875 | { | ||
876 | m_knownVelocity = value; | ||
877 | m_knownChanged |= m_knownChangedVelocity; | ||
878 | m_knownHas |= m_knownChangedVelocity; | ||
879 | } | ||
880 | } | ||
881 | |||
882 | private void VehicleAddForce(Vector3 pForce) | ||
883 | { | ||
884 | if ((m_knownHas & m_knownChangedForce) == 0) | ||
885 | { | ||
886 | m_knownForce = Vector3.Zero; | ||
887 | m_knownHas |= m_knownChangedForce; | ||
888 | } | ||
889 | m_knownForce += pForce; | ||
890 | m_knownChanged |= m_knownChangedForce; | ||
891 | } | ||
892 | |||
893 | private void VehicleAddForceImpulse(Vector3 pImpulse) | ||
894 | { | ||
895 | if ((m_knownHas & m_knownChangedForceImpulse) == 0) | ||
896 | { | ||
897 | m_knownForceImpulse = Vector3.Zero; | ||
898 | m_knownHas |= m_knownChangedForceImpulse; | ||
899 | } | ||
900 | m_knownForceImpulse += pImpulse; | ||
901 | m_knownChanged |= m_knownChangedForceImpulse; | ||
902 | } | ||
903 | |||
904 | private Vector3 VehicleRotationalVelocity | ||
905 | { | ||
906 | get | ||
907 | { | ||
908 | if ((m_knownHas & m_knownChangedRotationalVelocity) == 0) | ||
909 | { | ||
910 | m_knownRotationalVelocity = ControllingPrim.ForceRotationalVelocity; | ||
911 | m_knownHas |= m_knownChangedRotationalVelocity; | ||
912 | } | ||
913 | return (Vector3)m_knownRotationalVelocity; | ||
914 | } | ||
915 | set | ||
916 | { | ||
917 | m_knownRotationalVelocity = value; | ||
918 | m_knownChanged |= m_knownChangedRotationalVelocity; | ||
919 | m_knownHas |= m_knownChangedRotationalVelocity; | ||
920 | } | ||
921 | } | ||
922 | private void VehicleAddAngularForce(Vector3 aForce) | ||
923 | { | ||
924 | if ((m_knownHas & m_knownChangedRotationalForce) == 0) | ||
925 | { | ||
926 | m_knownRotationalForce = Vector3.Zero; | ||
927 | } | ||
928 | m_knownRotationalForce += aForce; | ||
929 | m_knownChanged |= m_knownChangedRotationalForce; | ||
930 | m_knownHas |= m_knownChangedRotationalForce; | ||
931 | } | ||
932 | private void VehicleAddRotationalImpulse(Vector3 pImpulse) | ||
933 | { | ||
934 | if ((m_knownHas & m_knownChangedRotationalImpulse) == 0) | ||
935 | { | ||
936 | m_knownRotationalImpulse = Vector3.Zero; | ||
937 | m_knownHas |= m_knownChangedRotationalImpulse; | ||
938 | } | ||
939 | m_knownRotationalImpulse += pImpulse; | ||
940 | m_knownChanged |= m_knownChangedRotationalImpulse; | ||
941 | } | ||
942 | |||
943 | // Vehicle relative forward velocity | ||
944 | private Vector3 VehicleForwardVelocity | ||
945 | { | ||
946 | get | ||
947 | { | ||
948 | return VehicleVelocity * Quaternion.Inverse(Quaternion.Normalize(VehicleFrameOrientation)); | ||
949 | } | ||
950 | } | ||
951 | |||
952 | private float VehicleForwardSpeed | ||
953 | { | ||
954 | get | ||
955 | { | ||
956 | return VehicleForwardVelocity.X; | ||
957 | } | ||
958 | } | ||
959 | private Quaternion VehicleFrameOrientation | ||
960 | { | ||
961 | get | ||
962 | { | ||
963 | return VehicleOrientation * m_referenceFrame; | ||
964 | } | ||
965 | } | ||
966 | |||
967 | #endregion // Known vehicle value functions | ||
968 | |||
969 | // One step of the vehicle properties for the next 'pTimestep' seconds. | ||
970 | internal void Step(float pTimestep) | ||
971 | { | ||
972 | if (!IsActive) return; | ||
973 | |||
974 | ForgetKnownVehicleProperties(); | ||
975 | |||
976 | MoveLinear(pTimestep); | ||
977 | MoveAngular(pTimestep); | ||
978 | |||
979 | LimitRotation(pTimestep); | ||
980 | |||
981 | // remember the position so next step we can limit absolute movement effects | ||
982 | m_lastPositionVector = VehiclePosition; | ||
983 | |||
984 | // If we forced the changing of some vehicle parameters, update the values and | ||
985 | // for the physics engine to note the changes so an UpdateProperties event will happen. | ||
986 | PushKnownChanged(); | ||
987 | |||
988 | if (m_physicsScene.VehiclePhysicalLoggingEnabled) | ||
989 | m_physicsScene.PE.DumpRigidBody(m_physicsScene.World, ControllingPrim.PhysBody); | ||
990 | |||
991 | VDetailLog("{0},BSDynamics.Step,done,pos={1}, force={2},velocity={3},angvel={4}", | ||
992 | ControllingPrim.LocalID, VehiclePosition, m_knownForce, VehicleVelocity, VehicleRotationalVelocity); | ||
993 | } | ||
994 | |||
995 | // Called after the simulation step | ||
996 | internal void PostStep(float pTimestep) | ||
997 | { | ||
998 | if (!IsActive) return; | ||
999 | |||
1000 | if (m_physicsScene.VehiclePhysicalLoggingEnabled) | ||
1001 | m_physicsScene.PE.DumpRigidBody(m_physicsScene.World, ControllingPrim.PhysBody); | ||
1002 | } | ||
1003 | |||
1004 | // Apply the effect of the linear motor and other linear motions (like hover and float). | ||
1005 | private void MoveLinear(float pTimestep) | ||
1006 | { | ||
1007 | ComputeLinearVelocity(pTimestep); | ||
1008 | |||
1009 | ComputeLinearDeflection(pTimestep); | ||
1010 | |||
1011 | ComputeLinearTerrainHeightCorrection(pTimestep); | ||
1012 | |||
1013 | ComputeLinearHover(pTimestep); | ||
1014 | |||
1015 | ComputeLinearBlockingEndPoint(pTimestep); | ||
1016 | |||
1017 | ComputeLinearMotorUp(pTimestep); | ||
1018 | |||
1019 | ApplyGravity(pTimestep); | ||
1020 | |||
1021 | // If not changing some axis, reduce out velocity | ||
1022 | if ((m_flags & (VehicleFlag.NO_X | VehicleFlag.NO_Y | VehicleFlag.NO_Z)) != 0) | ||
1023 | { | ||
1024 | Vector3 vel = VehicleVelocity; | ||
1025 | if ((m_flags & (VehicleFlag.NO_X)) != 0) | ||
1026 | { | ||
1027 | vel.X = 0; | ||
1028 | } | ||
1029 | if ((m_flags & (VehicleFlag.NO_Y)) != 0) | ||
1030 | { | ||
1031 | vel.Y = 0; | ||
1032 | } | ||
1033 | if ((m_flags & (VehicleFlag.NO_Z)) != 0) | ||
1034 | { | ||
1035 | vel.Z = 0; | ||
1036 | } | ||
1037 | VehicleVelocity = vel; | ||
1038 | } | ||
1039 | |||
1040 | // ================================================================== | ||
1041 | // Clamp high or low velocities | ||
1042 | float newVelocityLengthSq = VehicleVelocity.LengthSquared(); | ||
1043 | if (newVelocityLengthSq > BSParam.VehicleMaxLinearVelocitySquared) | ||
1044 | { | ||
1045 | Vector3 origVelW = VehicleVelocity; // DEBUG DEBUG | ||
1046 | VehicleVelocity /= VehicleVelocity.Length(); | ||
1047 | VehicleVelocity *= BSParam.VehicleMaxLinearVelocity; | ||
1048 | VDetailLog("{0}, MoveLinear,clampMax,origVelW={1},lenSq={2},maxVelSq={3},,newVelW={4}", | ||
1049 | ControllingPrim.LocalID, origVelW, newVelocityLengthSq, BSParam.VehicleMaxLinearVelocitySquared, VehicleVelocity); | ||
1050 | } | ||
1051 | else if (newVelocityLengthSq < BSParam.VehicleMinLinearVelocitySquared) | ||
1052 | { | ||
1053 | Vector3 origVelW = VehicleVelocity; // DEBUG DEBUG | ||
1054 | VDetailLog("{0}, MoveLinear,clampMin,origVelW={1},lenSq={2}", | ||
1055 | ControllingPrim.LocalID, origVelW, newVelocityLengthSq); | ||
1056 | VehicleVelocity = Vector3.Zero; | ||
1057 | } | ||
1058 | |||
1059 | VDetailLog("{0}, MoveLinear,done,isColl={1},newVel={2}", ControllingPrim.LocalID, ControllingPrim.HasSomeCollision, VehicleVelocity ); | ||
1060 | |||
1061 | } // end MoveLinear() | ||
1062 | |||
1063 | public void ComputeLinearVelocity(float pTimestep) | ||
1064 | { | ||
1065 | // Step the motor from the current value. Get the correction needed this step. | ||
1066 | Vector3 origVelW = VehicleVelocity; // DEBUG | ||
1067 | Vector3 currentVelV = VehicleForwardVelocity; | ||
1068 | Vector3 linearMotorCorrectionV = m_linearMotor.Step(pTimestep, currentVelV); | ||
1069 | |||
1070 | // Friction reduces vehicle motion based on absolute speed. Slow vehicle down by friction. | ||
1071 | Vector3 frictionFactorV = ComputeFrictionFactor(m_linearFrictionTimescale, pTimestep); | ||
1072 | linearMotorCorrectionV -= (currentVelV * frictionFactorV); | ||
1073 | |||
1074 | // Motor is vehicle coordinates. Rotate it to world coordinates | ||
1075 | Vector3 linearMotorVelocityW = linearMotorCorrectionV * VehicleFrameOrientation; | ||
1076 | |||
1077 | // If we're a ground vehicle, don't add any upward Z movement | ||
1078 | if ((m_flags & VehicleFlag.LIMIT_MOTOR_UP) != 0) | ||
1079 | { | ||
1080 | if (linearMotorVelocityW.Z > 0f) | ||
1081 | linearMotorVelocityW.Z = 0f; | ||
1082 | } | ||
1083 | |||
1084 | // Add this correction to the velocity to make it faster/slower. | ||
1085 | VehicleVelocity += linearMotorVelocityW; | ||
1086 | |||
1087 | VDetailLog("{0}, MoveLinear,velocity,origVelW={1},velV={2},tgt={3},correctV={4},correctW={5},newVelW={6},fricFact={7}", | ||
1088 | ControllingPrim.LocalID, origVelW, currentVelV, m_linearMotor.TargetValue, linearMotorCorrectionV, | ||
1089 | linearMotorVelocityW, VehicleVelocity, frictionFactorV); | ||
1090 | } | ||
1091 | |||
1092 | //Given a Deflection Effiency and a Velocity, Returns a Velocity that is Partially Deflected onto the X Axis | ||
1093 | //Clamped so that a DeflectionTimescale of less then 1 does not increase force over original velocity | ||
1094 | private void ComputeLinearDeflection(float pTimestep) | ||
1095 | { | ||
1096 | Vector3 linearDeflectionV = Vector3.Zero; | ||
1097 | Vector3 velocityV = VehicleForwardVelocity; | ||
1098 | |||
1099 | if (BSParam.VehicleEnableLinearDeflection) | ||
1100 | { | ||
1101 | // Velocity in Y and Z dimensions is movement to the side or turning. | ||
1102 | // Compute deflection factor from the to the side and rotational velocity | ||
1103 | linearDeflectionV.Y = SortedClampInRange(0, (velocityV.Y * m_linearDeflectionEfficiency) / m_linearDeflectionTimescale, velocityV.Y); | ||
1104 | linearDeflectionV.Z = SortedClampInRange(0, (velocityV.Z * m_linearDeflectionEfficiency) / m_linearDeflectionTimescale, velocityV.Z); | ||
1105 | |||
1106 | // Velocity to the side and around is corrected and moved into the forward direction | ||
1107 | linearDeflectionV.X += Math.Abs(linearDeflectionV.Y); | ||
1108 | linearDeflectionV.X += Math.Abs(linearDeflectionV.Z); | ||
1109 | |||
1110 | // Scale the deflection to the fractional simulation time | ||
1111 | linearDeflectionV *= pTimestep; | ||
1112 | |||
1113 | // Subtract the sideways and rotational velocity deflection factors while adding the correction forward | ||
1114 | linearDeflectionV *= new Vector3(1, -1, -1); | ||
1115 | |||
1116 | // Correction is vehicle relative. Convert to world coordinates. | ||
1117 | Vector3 linearDeflectionW = linearDeflectionV * VehicleFrameOrientation; | ||
1118 | |||
1119 | // Optionally, if not colliding, don't effect world downward velocity. Let falling things fall. | ||
1120 | if (BSParam.VehicleLinearDeflectionNotCollidingNoZ && !m_controllingPrim.HasSomeCollision) | ||
1121 | { | ||
1122 | linearDeflectionW.Z = 0f; | ||
1123 | } | ||
1124 | |||
1125 | VehicleVelocity += linearDeflectionW; | ||
1126 | |||
1127 | VDetailLog("{0}, MoveLinear,LinearDeflection,linDefEff={1},linDefTS={2},linDeflectionV={3}", | ||
1128 | ControllingPrim.LocalID, m_linearDeflectionEfficiency, m_linearDeflectionTimescale, linearDeflectionV); | ||
1129 | } | ||
1130 | } | ||
1131 | |||
1132 | public void ComputeLinearTerrainHeightCorrection(float pTimestep) | ||
1133 | { | ||
1134 | // If below the terrain, move us above the ground a little. | ||
1135 | // TODO: Consider taking the rotated size of the object or possibly casting a ray. | ||
1136 | if (VehiclePosition.Z < GetTerrainHeight(VehiclePosition)) | ||
1137 | { | ||
1138 | // Force position because applying force won't get the vehicle through the terrain | ||
1139 | Vector3 newPosition = VehiclePosition; | ||
1140 | newPosition.Z = GetTerrainHeight(VehiclePosition) + 1f; | ||
1141 | VehiclePosition = newPosition; | ||
1142 | VDetailLog("{0}, MoveLinear,terrainHeight,terrainHeight={1},pos={2}", | ||
1143 | ControllingPrim.LocalID, GetTerrainHeight(VehiclePosition), VehiclePosition); | ||
1144 | } | ||
1145 | } | ||
1146 | |||
1147 | public void ComputeLinearHover(float pTimestep) | ||
1148 | { | ||
1149 | // m_VhoverEfficiency: 0=bouncy, 1=totally damped | ||
1150 | // m_VhoverTimescale: time to achieve height | ||
1151 | if ((m_flags & (VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.HOVER_GLOBAL_HEIGHT)) != 0 && (m_VhoverHeight > 0) && (m_VhoverTimescale < 300)) | ||
1152 | { | ||
1153 | // We should hover, get the target height | ||
1154 | if ((m_flags & VehicleFlag.HOVER_WATER_ONLY) != 0) | ||
1155 | { | ||
1156 | m_VhoverTargetHeight = GetWaterLevel(VehiclePosition) + m_VhoverHeight; | ||
1157 | } | ||
1158 | if ((m_flags & VehicleFlag.HOVER_TERRAIN_ONLY) != 0) | ||
1159 | { | ||
1160 | m_VhoverTargetHeight = GetTerrainHeight(VehiclePosition) + m_VhoverHeight; | ||
1161 | } | ||
1162 | if ((m_flags & VehicleFlag.HOVER_GLOBAL_HEIGHT) != 0) | ||
1163 | { | ||
1164 | m_VhoverTargetHeight = m_VhoverHeight; | ||
1165 | } | ||
1166 | if ((m_flags & VehicleFlag.HOVER_UP_ONLY) != 0) | ||
1167 | { | ||
1168 | // If body is already heigher, use its height as target height | ||
1169 | if (VehiclePosition.Z > m_VhoverTargetHeight) | ||
1170 | { | ||
1171 | m_VhoverTargetHeight = VehiclePosition.Z; | ||
1172 | |||
1173 | // A 'misfeature' of this flag is that if the vehicle is above it's hover height, | ||
1174 | // the vehicle's buoyancy goes away. This is an SL bug that got used by so many | ||
1175 | // scripts that it could not be changed. | ||
1176 | // So, if above the height, reapply gravity if buoyancy had it turned off. | ||
1177 | if (m_VehicleBuoyancy != 0) | ||
1178 | { | ||
1179 | Vector3 appliedGravity = ControllingPrim.ComputeGravity(ControllingPrim.Buoyancy) * m_vehicleMass; | ||
1180 | VehicleAddForce(appliedGravity); | ||
1181 | } | ||
1182 | } | ||
1183 | } | ||
1184 | |||
1185 | if ((m_flags & VehicleFlag.LOCK_HOVER_HEIGHT) != 0) | ||
1186 | { | ||
1187 | if (Math.Abs(VehiclePosition.Z - m_VhoverTargetHeight) > 0.2f) | ||
1188 | { | ||
1189 | Vector3 pos = VehiclePosition; | ||
1190 | pos.Z = m_VhoverTargetHeight; | ||
1191 | VehiclePosition = pos; | ||
1192 | |||
1193 | VDetailLog("{0}, MoveLinear,hover,pos={1},lockHoverHeight", ControllingPrim.LocalID, pos); | ||
1194 | } | ||
1195 | } | ||
1196 | else | ||
1197 | { | ||
1198 | // Error is positive if below the target and negative if above. | ||
1199 | Vector3 hpos = VehiclePosition; | ||
1200 | float verticalError = m_VhoverTargetHeight - hpos.Z; | ||
1201 | float verticalCorrection = verticalError / m_VhoverTimescale; | ||
1202 | verticalCorrection *= m_VhoverEfficiency; | ||
1203 | |||
1204 | hpos.Z += verticalCorrection; | ||
1205 | VehiclePosition = hpos; | ||
1206 | |||
1207 | // Since we are hovering, we need to do the opposite of falling -- get rid of world Z | ||
1208 | Vector3 vel = VehicleVelocity; | ||
1209 | vel.Z = 0f; | ||
1210 | VehicleVelocity = vel; | ||
1211 | |||
1212 | /* | ||
1213 | float verticalCorrectionVelocity = verticalError / m_VhoverTimescale; | ||
1214 | Vector3 verticalCorrection = new Vector3(0f, 0f, verticalCorrectionVelocity); | ||
1215 | verticalCorrection *= m_vehicleMass; | ||
1216 | |||
1217 | // TODO: implement m_VhoverEfficiency correctly | ||
1218 | VehicleAddForceImpulse(verticalCorrection); | ||
1219 | */ | ||
1220 | |||
1221 | VDetailLog("{0}, MoveLinear,hover,pos={1},eff={2},hoverTS={3},height={4},target={5},err={6},corr={7}", | ||
1222 | ControllingPrim.LocalID, VehiclePosition, m_VhoverEfficiency, | ||
1223 | m_VhoverTimescale, m_VhoverHeight, m_VhoverTargetHeight, | ||
1224 | verticalError, verticalCorrection); | ||
1225 | } | ||
1226 | } | ||
1227 | } | ||
1228 | |||
1229 | public bool ComputeLinearBlockingEndPoint(float pTimestep) | ||
1230 | { | ||
1231 | bool changed = false; | ||
1232 | |||
1233 | Vector3 pos = VehiclePosition; | ||
1234 | Vector3 posChange = pos - m_lastPositionVector; | ||
1235 | if (m_BlockingEndPoint != Vector3.Zero) | ||
1236 | { | ||
1237 | if (pos.X >= (m_BlockingEndPoint.X - (float)1)) | ||
1238 | { | ||
1239 | pos.X -= posChange.X + 1; | ||
1240 | changed = true; | ||
1241 | } | ||
1242 | if (pos.Y >= (m_BlockingEndPoint.Y - (float)1)) | ||
1243 | { | ||
1244 | pos.Y -= posChange.Y + 1; | ||
1245 | changed = true; | ||
1246 | } | ||
1247 | if (pos.Z >= (m_BlockingEndPoint.Z - (float)1)) | ||
1248 | { | ||
1249 | pos.Z -= posChange.Z + 1; | ||
1250 | changed = true; | ||
1251 | } | ||
1252 | if (pos.X <= 0) | ||
1253 | { | ||
1254 | pos.X += posChange.X + 1; | ||
1255 | changed = true; | ||
1256 | } | ||
1257 | if (pos.Y <= 0) | ||
1258 | { | ||
1259 | pos.Y += posChange.Y + 1; | ||
1260 | changed = true; | ||
1261 | } | ||
1262 | if (changed) | ||
1263 | { | ||
1264 | VehiclePosition = pos; | ||
1265 | VDetailLog("{0}, MoveLinear,blockingEndPoint,block={1},origPos={2},pos={3}", | ||
1266 | ControllingPrim.LocalID, m_BlockingEndPoint, posChange, pos); | ||
1267 | } | ||
1268 | } | ||
1269 | return changed; | ||
1270 | } | ||
1271 | |||
1272 | // From http://wiki.secondlife.com/wiki/LlSetVehicleFlags : | ||
1273 | // Prevent ground vehicles from motoring into the sky. This flag has a subtle effect when | ||
1274 | // used with conjunction with banking: the strength of the banking will decay when the | ||
1275 | // vehicle no longer experiences collisions. The decay timescale is the same as | ||
1276 | // VEHICLE_BANKING_TIMESCALE. This is to help prevent ground vehicles from steering | ||
1277 | // when they are in mid jump. | ||
1278 | // TODO: this code is wrong. Also, what should it do for boats (height from water)? | ||
1279 | // This is just using the ground and a general collision check. Should really be using | ||
1280 | // a downward raycast to find what is below. | ||
1281 | public void ComputeLinearMotorUp(float pTimestep) | ||
1282 | { | ||
1283 | if ((m_flags & (VehicleFlag.LIMIT_MOTOR_UP)) != 0) | ||
1284 | { | ||
1285 | // This code tries to decide if the object is not on the ground and then pushing down | ||
1286 | /* | ||
1287 | float targetHeight = Type == Vehicle.TYPE_BOAT ? GetWaterLevel(VehiclePosition) : GetTerrainHeight(VehiclePosition); | ||
1288 | distanceAboveGround = VehiclePosition.Z - targetHeight; | ||
1289 | // Not colliding if the vehicle is off the ground | ||
1290 | if (!Prim.HasSomeCollision) | ||
1291 | { | ||
1292 | // downForce = new Vector3(0, 0, -distanceAboveGround / m_bankingTimescale); | ||
1293 | VehicleVelocity += new Vector3(0, 0, -distanceAboveGround); | ||
1294 | } | ||
1295 | // TODO: this calculation is wrong. From the description at | ||
1296 | // (http://wiki.secondlife.com/wiki/Category:LSL_Vehicle), the downForce | ||
1297 | // has a decay factor. This says this force should | ||
1298 | // be computed with a motor. | ||
1299 | // TODO: add interaction with banking. | ||
1300 | VDetailLog("{0}, MoveLinear,limitMotorUp,distAbove={1},colliding={2},ret={3}", | ||
1301 | Prim.LocalID, distanceAboveGround, Prim.HasSomeCollision, ret); | ||
1302 | */ | ||
1303 | |||
1304 | // Another approach is to measure if we're going up. If going up and not colliding, | ||
1305 | // the vehicle is in the air. Fix that by pushing down. | ||
1306 | if (!ControllingPrim.HasSomeCollision && VehicleVelocity.Z > 0.1) | ||
1307 | { | ||
1308 | // Get rid of any of the velocity vector that is pushing us up. | ||
1309 | float upVelocity = VehicleVelocity.Z; | ||
1310 | VehicleVelocity += new Vector3(0, 0, -upVelocity); | ||
1311 | |||
1312 | /* | ||
1313 | // If we're pointed up into the air, we should nose down | ||
1314 | Vector3 pointingDirection = Vector3.UnitX * VehicleOrientation; | ||
1315 | // The rotation around the Y axis is pitch up or down | ||
1316 | if (pointingDirection.Y > 0.01f) | ||
1317 | { | ||
1318 | float angularCorrectionForce = -(float)Math.Asin(pointingDirection.Y); | ||
1319 | Vector3 angularCorrectionVector = new Vector3(0f, angularCorrectionForce, 0f); | ||
1320 | // Rotate into world coordinates and apply to vehicle | ||
1321 | angularCorrectionVector *= VehicleOrientation; | ||
1322 | VehicleAddAngularForce(angularCorrectionVector); | ||
1323 | VDetailLog("{0}, MoveLinear,limitMotorUp,newVel={1},pntDir={2},corrFrc={3},aCorr={4}", | ||
1324 | Prim.LocalID, VehicleVelocity, pointingDirection, angularCorrectionForce, angularCorrectionVector); | ||
1325 | } | ||
1326 | */ | ||
1327 | VDetailLog("{0}, MoveLinear,limitMotorUp,collide={1},upVel={2},newVel={3}", | ||
1328 | ControllingPrim.LocalID, ControllingPrim.HasSomeCollision, upVelocity, VehicleVelocity); | ||
1329 | } | ||
1330 | } | ||
1331 | } | ||
1332 | |||
1333 | private void ApplyGravity(float pTimeStep) | ||
1334 | { | ||
1335 | Vector3 appliedGravity = m_VehicleGravity * m_vehicleMass; | ||
1336 | |||
1337 | // Hack to reduce downward force if the vehicle is probably sitting on the ground | ||
1338 | if (ControllingPrim.HasSomeCollision && IsGroundVehicle) | ||
1339 | appliedGravity *= BSParam.VehicleGroundGravityFudge; | ||
1340 | |||
1341 | VehicleAddForce(appliedGravity); | ||
1342 | |||
1343 | VDetailLog("{0}, MoveLinear,applyGravity,vehGrav={1},collid={2},fudge={3},mass={4},appliedForce={5}", | ||
1344 | ControllingPrim.LocalID, m_VehicleGravity, | ||
1345 | ControllingPrim.HasSomeCollision, BSParam.VehicleGroundGravityFudge, m_vehicleMass, appliedGravity); | ||
1346 | } | ||
1347 | |||
1348 | // ======================================================================= | ||
1349 | // ======================================================================= | ||
1350 | // Apply the effect of the angular motor. | ||
1351 | // The 'contribution' is how much angular correction velocity each function wants. | ||
1352 | // All the contributions are added together and the resulting velocity is | ||
1353 | // set directly on the vehicle. | ||
1354 | private void MoveAngular(float pTimestep) | ||
1355 | { | ||
1356 | ComputeAngularTurning(pTimestep); | ||
1357 | |||
1358 | ComputeAngularVerticalAttraction(); | ||
1359 | |||
1360 | ComputeAngularDeflection(); | ||
1361 | |||
1362 | ComputeAngularBanking(); | ||
1363 | |||
1364 | // ================================================================== | ||
1365 | if (VehicleRotationalVelocity.ApproxEquals(Vector3.Zero, 0.0001f)) | ||
1366 | { | ||
1367 | // The vehicle is not adding anything angular wise. | ||
1368 | VehicleRotationalVelocity = Vector3.Zero; | ||
1369 | VDetailLog("{0}, MoveAngular,done,zero", ControllingPrim.LocalID); | ||
1370 | } | ||
1371 | else | ||
1372 | { | ||
1373 | VDetailLog("{0}, MoveAngular,done,nonZero,angVel={1}", ControllingPrim.LocalID, VehicleRotationalVelocity); | ||
1374 | } | ||
1375 | |||
1376 | // ================================================================== | ||
1377 | //Offset section | ||
1378 | if (m_linearMotorOffset != Vector3.Zero) | ||
1379 | { | ||
1380 | //Offset of linear velocity doesn't change the linear velocity, | ||
1381 | // but causes a torque to be applied, for example... | ||
1382 | // | ||
1383 | // IIIII >>> IIIII | ||
1384 | // IIIII >>> IIIII | ||
1385 | // IIIII >>> IIIII | ||
1386 | // ^ | ||
1387 | // | Applying a force at the arrow will cause the object to move forward, but also rotate | ||
1388 | // | ||
1389 | // | ||
1390 | // The torque created is the linear velocity crossed with the offset | ||
1391 | |||
1392 | // TODO: this computation should be in the linear section | ||
1393 | // because that is where we know the impulse being applied. | ||
1394 | Vector3 torqueFromOffset = Vector3.Zero; | ||
1395 | // torqueFromOffset = Vector3.Cross(m_linearMotorOffset, appliedImpulse); | ||
1396 | if (float.IsNaN(torqueFromOffset.X)) | ||
1397 | torqueFromOffset.X = 0; | ||
1398 | if (float.IsNaN(torqueFromOffset.Y)) | ||
1399 | torqueFromOffset.Y = 0; | ||
1400 | if (float.IsNaN(torqueFromOffset.Z)) | ||
1401 | torqueFromOffset.Z = 0; | ||
1402 | |||
1403 | VehicleAddAngularForce(torqueFromOffset * m_vehicleMass); | ||
1404 | VDetailLog("{0}, BSDynamic.MoveAngular,motorOffset,applyTorqueImpulse={1}", ControllingPrim.LocalID, torqueFromOffset); | ||
1405 | } | ||
1406 | |||
1407 | } | ||
1408 | |||
1409 | private void ComputeAngularTurning(float pTimestep) | ||
1410 | { | ||
1411 | // The user wants this many radians per second angular change? | ||
1412 | Vector3 origVehicleRotationalVelocity = VehicleRotationalVelocity; // DEBUG DEBUG | ||
1413 | Vector3 currentAngularV = VehicleRotationalVelocity * Quaternion.Inverse(VehicleFrameOrientation); | ||
1414 | Vector3 angularMotorContributionV = m_angularMotor.Step(pTimestep, currentAngularV); | ||
1415 | |||
1416 | // ================================================================== | ||
1417 | // From http://wiki.secondlife.com/wiki/LlSetVehicleFlags : | ||
1418 | // This flag prevents linear deflection parallel to world z-axis. This is useful | ||
1419 | // for preventing ground vehicles with large linear deflection, like bumper cars, | ||
1420 | // from climbing their linear deflection into the sky. | ||
1421 | // That is, NO_DEFLECTION_UP says angular motion should not add any pitch or roll movement | ||
1422 | // TODO: This is here because this is where ODE put it but documentation says it | ||
1423 | // is a linear effect. Where should this check go? | ||
1424 | //if ((m_flags & (VehicleFlag.NO_DEFLECTION_UP)) != 0) | ||
1425 | // { | ||
1426 | // angularMotorContributionV.X = 0f; | ||
1427 | // angularMotorContributionV.Y = 0f; | ||
1428 | // } | ||
1429 | |||
1430 | // Reduce any velocity by friction. | ||
1431 | Vector3 frictionFactorW = ComputeFrictionFactor(m_angularFrictionTimescale, pTimestep); | ||
1432 | angularMotorContributionV -= (currentAngularV * frictionFactorW); | ||
1433 | |||
1434 | Vector3 angularMotorContributionW = angularMotorContributionV * VehicleFrameOrientation; | ||
1435 | VehicleRotationalVelocity += angularMotorContributionW; | ||
1436 | |||
1437 | VDetailLog("{0}, MoveAngular,angularTurning,curAngVelV={1},origVehRotVel={2},vehRotVel={3},frictFact={4}, angContribV={5},angContribW={6}", | ||
1438 | ControllingPrim.LocalID, currentAngularV, origVehicleRotationalVelocity, VehicleRotationalVelocity, frictionFactorW, angularMotorContributionV, angularMotorContributionW); | ||
1439 | } | ||
1440 | |||
1441 | // From http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial: | ||
1442 | // Some vehicles, like boats, should always keep their up-side up. This can be done by | ||
1443 | // enabling the "vertical attractor" behavior that springs the vehicle's local z-axis to | ||
1444 | // the world z-axis (a.k.a. "up"). To take advantage of this feature you would set the | ||
1445 | // VEHICLE_VERTICAL_ATTRACTION_TIMESCALE to control the period of the spring frequency, | ||
1446 | // and then set the VEHICLE_VERTICAL_ATTRACTION_EFFICIENCY to control the damping. An | ||
1447 | // efficiency of 0.0 will cause the spring to wobble around its equilibrium, while an | ||
1448 | // efficiency of 1.0 will cause the spring to reach its equilibrium with exponential decay. | ||
1449 | public void ComputeAngularVerticalAttraction() | ||
1450 | { | ||
1451 | |||
1452 | // If vertical attaction timescale is reasonable | ||
1453 | if (BSParam.VehicleEnableAngularVerticalAttraction && m_verticalAttractionTimescale < m_verticalAttractionCutoff) | ||
1454 | { | ||
1455 | Vector3 vehicleUpAxis = Vector3.UnitZ * VehicleFrameOrientation; | ||
1456 | switch (BSParam.VehicleAngularVerticalAttractionAlgorithm) | ||
1457 | { | ||
1458 | case 0: | ||
1459 | { | ||
1460 | //Another formula to try got from : | ||
1461 | //http://answers.unity3d.com/questions/10425/how-to-stabilize-angular-motion-alignment-of-hover.html | ||
1462 | |||
1463 | // Flipping what was originally a timescale into a speed variable and then multiplying it by 2 | ||
1464 | // since only computing half the distance between the angles. | ||
1465 | float verticalAttractionSpeed = (1 / m_verticalAttractionTimescale) * 2.0f; | ||
1466 | |||
1467 | // Make a prediction of where the up axis will be when this is applied rather then where it is now as | ||
1468 | // this makes for a smoother adjustment and less fighting between the various forces. | ||
1469 | Vector3 predictedUp = vehicleUpAxis * Quaternion.CreateFromAxisAngle(VehicleRotationalVelocity, 0f); | ||
1470 | |||
1471 | // This is only half the distance to the target so it will take 2 seconds to complete the turn. | ||
1472 | Vector3 torqueVector = Vector3.Cross(predictedUp, Vector3.UnitZ); | ||
1473 | |||
1474 | if ((m_flags & VehicleFlag.LIMIT_ROLL_ONLY) != 0) | ||
1475 | { | ||
1476 | Vector3 vehicleForwardAxis = Vector3.UnitX * VehicleFrameOrientation; | ||
1477 | torqueVector = ProjectVector(torqueVector, vehicleForwardAxis); | ||
1478 | } | ||
1479 | |||
1480 | // Scale vector by our timescale since it is an acceleration it is r/s^2 or radians a timescale squared | ||
1481 | Vector3 vertContributionV = torqueVector * verticalAttractionSpeed * verticalAttractionSpeed; | ||
1482 | |||
1483 | VehicleRotationalVelocity += vertContributionV; | ||
1484 | |||
1485 | VDetailLog("{0}, MoveAngular,verticalAttraction,vertAttrSpeed={1},upAxis={2},PredictedUp={3},torqueVector={4},contrib={5}", | ||
1486 | ControllingPrim.LocalID, | ||
1487 | verticalAttractionSpeed, | ||
1488 | vehicleUpAxis, | ||
1489 | predictedUp, | ||
1490 | torqueVector, | ||
1491 | vertContributionV); | ||
1492 | break; | ||
1493 | } | ||
1494 | case 1: | ||
1495 | { | ||
1496 | // Possible solution derived from a discussion at: | ||
1497 | // http://stackoverflow.com/questions/14939657/computing-vector-from-quaternion-works-computing-quaternion-from-vector-does-no | ||
1498 | |||
1499 | // Create a rotation that is only the vehicle's rotation around Z | ||
1500 | Vector3 currentEulerW = Vector3.Zero; | ||
1501 | VehicleFrameOrientation.GetEulerAngles(out currentEulerW.X, out currentEulerW.Y, out currentEulerW.Z); | ||
1502 | Quaternion justZOrientation = Quaternion.CreateFromAxisAngle(Vector3.UnitZ, currentEulerW.Z); | ||
1503 | |||
1504 | // Create the axis that is perpendicular to the up vector and the rotated up vector. | ||
1505 | Vector3 differenceAxisW = Vector3.Cross(Vector3.UnitZ * justZOrientation, Vector3.UnitZ * VehicleFrameOrientation); | ||
1506 | // Compute the angle between those to vectors. | ||
1507 | double differenceAngle = Math.Acos((double)Vector3.Dot(Vector3.UnitZ, Vector3.Normalize(Vector3.UnitZ * VehicleFrameOrientation))); | ||
1508 | // 'differenceAngle' is the angle to rotate and 'differenceAxis' is the plane to rotate in to get the vehicle vertical | ||
1509 | |||
1510 | // Reduce the change by the time period it is to change in. Timestep is handled when velocity is applied. | ||
1511 | // TODO: add 'efficiency'. | ||
1512 | // differenceAngle /= m_verticalAttractionTimescale; | ||
1513 | |||
1514 | // Create the quaterian representing the correction angle | ||
1515 | Quaternion correctionRotationW = Quaternion.CreateFromAxisAngle(differenceAxisW, (float)differenceAngle); | ||
1516 | |||
1517 | // Turn that quaternion into Euler values to make it into velocities to apply. | ||
1518 | Vector3 vertContributionW = Vector3.Zero; | ||
1519 | correctionRotationW.GetEulerAngles(out vertContributionW.X, out vertContributionW.Y, out vertContributionW.Z); | ||
1520 | vertContributionW *= -1f; | ||
1521 | vertContributionW /= m_verticalAttractionTimescale; | ||
1522 | |||
1523 | VehicleRotationalVelocity += vertContributionW; | ||
1524 | |||
1525 | VDetailLog("{0}, MoveAngular,verticalAttraction,upAxis={1},diffAxis={2},diffAng={3},corrRot={4},contrib={5}", | ||
1526 | ControllingPrim.LocalID, | ||
1527 | vehicleUpAxis, | ||
1528 | differenceAxisW, | ||
1529 | differenceAngle, | ||
1530 | correctionRotationW, | ||
1531 | vertContributionW); | ||
1532 | break; | ||
1533 | } | ||
1534 | case 2: | ||
1535 | { | ||
1536 | Vector3 vertContributionV = Vector3.Zero; | ||
1537 | Vector3 origRotVelW = VehicleRotationalVelocity; // DEBUG DEBUG | ||
1538 | |||
1539 | // Take a vector pointing up and convert it from world to vehicle relative coords. | ||
1540 | Vector3 verticalError = Vector3.Normalize(Vector3.UnitZ * VehicleFrameOrientation); | ||
1541 | |||
1542 | // If vertical attraction correction is needed, the vector that was pointing up (UnitZ) | ||
1543 | // is now: | ||
1544 | // leaning to one side: rotated around the X axis with the Y value going | ||
1545 | // from zero (nearly straight up) to one (completely to the side)) or | ||
1546 | // leaning front-to-back: rotated around the Y axis with the value of X being between | ||
1547 | // zero and one. | ||
1548 | // The value of Z is how far the rotation is off with 1 meaning none and 0 being 90 degrees. | ||
1549 | |||
1550 | // Y error means needed rotation around X axis and visa versa. | ||
1551 | // Since the error goes from zero to one, the asin is the corresponding angle. | ||
1552 | vertContributionV.X = (float)Math.Asin(verticalError.Y); | ||
1553 | // (Tilt forward (positive X) needs to tilt back (rotate negative) around Y axis.) | ||
1554 | vertContributionV.Y = -(float)Math.Asin(verticalError.X); | ||
1555 | |||
1556 | // If verticalError.Z is negative, the vehicle is upside down. Add additional push. | ||
1557 | if (verticalError.Z < 0f) | ||
1558 | { | ||
1559 | vertContributionV.X += Math.Sign(vertContributionV.X) * PIOverFour; | ||
1560 | // vertContribution.Y -= PIOverFour; | ||
1561 | } | ||
1562 | |||
1563 | // 'vertContrbution' is now the necessary angular correction to correct tilt in one second. | ||
1564 | // Correction happens over a number of seconds. | ||
1565 | Vector3 unscaledContribVerticalErrorV = vertContributionV; // DEBUG DEBUG | ||
1566 | |||
1567 | // The correction happens over the user's time period | ||
1568 | vertContributionV /= m_verticalAttractionTimescale; | ||
1569 | |||
1570 | // Rotate the vehicle rotation to the world coordinates. | ||
1571 | VehicleRotationalVelocity += (vertContributionV * VehicleFrameOrientation); | ||
1572 | |||
1573 | VDetailLog("{0}, MoveAngular,verticalAttraction,,upAxis={1},origRotVW={2},vertError={3},unscaledV={4},eff={5},ts={6},vertContribV={7}", | ||
1574 | ControllingPrim.LocalID, | ||
1575 | vehicleUpAxis, | ||
1576 | origRotVelW, | ||
1577 | verticalError, | ||
1578 | unscaledContribVerticalErrorV, | ||
1579 | m_verticalAttractionEfficiency, | ||
1580 | m_verticalAttractionTimescale, | ||
1581 | vertContributionV); | ||
1582 | break; | ||
1583 | } | ||
1584 | default: | ||
1585 | { | ||
1586 | break; | ||
1587 | } | ||
1588 | } | ||
1589 | } | ||
1590 | } | ||
1591 | |||
1592 | // Angular correction to correct the direction the vehicle is pointing to be | ||
1593 | // the direction is should want to be pointing. | ||
1594 | // The vehicle is moving in some direction and correct its orientation to it is pointing | ||
1595 | // in that direction. | ||
1596 | // TODO: implement reference frame. | ||
1597 | public void ComputeAngularDeflection() | ||
1598 | { | ||
1599 | |||
1600 | if (BSParam.VehicleEnableAngularDeflection && m_angularDeflectionEfficiency != 0 && VehicleForwardSpeed > 0.2) | ||
1601 | { | ||
1602 | Vector3 deflectContributionV = Vector3.Zero; | ||
1603 | |||
1604 | // The direction the vehicle is moving | ||
1605 | Vector3 movingDirection = VehicleVelocity; | ||
1606 | movingDirection.Normalize(); | ||
1607 | |||
1608 | // If the vehicle is going backward, it is still pointing forward | ||
1609 | movingDirection *= Math.Sign(VehicleForwardSpeed); | ||
1610 | |||
1611 | // The direction the vehicle is pointing | ||
1612 | Vector3 pointingDirection = Vector3.UnitX * VehicleFrameOrientation; | ||
1613 | //Predict where the Vehicle will be pointing after AngularVelocity change is applied. This will keep | ||
1614 | // from overshooting and allow this correction to merge with the Vertical Attraction peacefully. | ||
1615 | Vector3 predictedPointingDirection = pointingDirection * Quaternion.CreateFromAxisAngle(VehicleRotationalVelocity, 0f); | ||
1616 | predictedPointingDirection.Normalize(); | ||
1617 | |||
1618 | // The difference between what is and what should be. | ||
1619 | // Vector3 deflectionError = movingDirection - predictedPointingDirection; | ||
1620 | Vector3 deflectionError = Vector3.Cross(movingDirection, predictedPointingDirection); | ||
1621 | |||
1622 | // Don't try to correct very large errors (not our job) | ||
1623 | // if (Math.Abs(deflectionError.X) > PIOverFour) deflectionError.X = PIOverTwo * Math.Sign(deflectionError.X); | ||
1624 | // if (Math.Abs(deflectionError.Y) > PIOverFour) deflectionError.Y = PIOverTwo * Math.Sign(deflectionError.Y); | ||
1625 | // if (Math.Abs(deflectionError.Z) > PIOverFour) deflectionError.Z = PIOverTwo * Math.Sign(deflectionError.Z); | ||
1626 | if (Math.Abs(deflectionError.X) > PIOverFour) deflectionError.X = 0f; | ||
1627 | if (Math.Abs(deflectionError.Y) > PIOverFour) deflectionError.Y = 0f; | ||
1628 | if (Math.Abs(deflectionError.Z) > PIOverFour) deflectionError.Z = 0f; | ||
1629 | |||
1630 | // ret = m_angularDeflectionCorrectionMotor(1f, deflectionError); | ||
1631 | |||
1632 | // Scale the correction by recovery timescale and efficiency | ||
1633 | // Not modeling a spring so clamp the scale to no more then the arc | ||
1634 | deflectContributionV = (-deflectionError) * ClampInRange(0, m_angularDeflectionEfficiency/m_angularDeflectionTimescale,1f); | ||
1635 | //deflectContributionV /= m_angularDeflectionTimescale; | ||
1636 | |||
1637 | VehicleRotationalVelocity += deflectContributionV; | ||
1638 | VDetailLog("{0}, MoveAngular,Deflection,movingDir={1},pointingDir={2},deflectError={3},ret={4}", | ||
1639 | ControllingPrim.LocalID, movingDirection, pointingDirection, deflectionError, deflectContributionV); | ||
1640 | VDetailLog("{0}, MoveAngular,Deflection,fwdSpd={1},defEff={2},defTS={3},PredictedPointingDir={4}", | ||
1641 | ControllingPrim.LocalID, VehicleForwardSpeed, m_angularDeflectionEfficiency, m_angularDeflectionTimescale, predictedPointingDirection); | ||
1642 | } | ||
1643 | } | ||
1644 | |||
1645 | // Angular change to rotate the vehicle around the Z axis when the vehicle | ||
1646 | // is tipped around the X axis. | ||
1647 | // From http://wiki.secondlife.com/wiki/Linden_Vehicle_Tutorial: | ||
1648 | // The vertical attractor feature must be enabled in order for the banking behavior to | ||
1649 | // function. The way banking works is this: a rotation around the vehicle's roll-axis will | ||
1650 | // produce a angular velocity around the yaw-axis, causing the vehicle to turn. The magnitude | ||
1651 | // of the yaw effect will be proportional to the | ||
1652 | // VEHICLE_BANKING_EFFICIENCY, the angle of the roll rotation, and sometimes the vehicle's | ||
1653 | // velocity along its preferred axis of motion. | ||
1654 | // The VEHICLE_BANKING_EFFICIENCY can vary between -1 and +1. When it is positive then any | ||
1655 | // positive rotation (by the right-hand rule) about the roll-axis will effect a | ||
1656 | // (negative) torque around the yaw-axis, making it turn to the right--that is the | ||
1657 | // vehicle will lean into the turn, which is how real airplanes and motorcycle's work. | ||
1658 | // Negating the banking coefficient will make it so that the vehicle leans to the | ||
1659 | // outside of the turn (not very "physical" but might allow interesting vehicles so why not?). | ||
1660 | // The VEHICLE_BANKING_MIX is a fake (i.e. non-physical) parameter that is useful for making | ||
1661 | // banking vehicles do what you want rather than what the laws of physics allow. | ||
1662 | // For example, consider a real motorcycle...it must be moving forward in order for | ||
1663 | // it to turn while banking, however video-game motorcycles are often configured | ||
1664 | // to turn in place when at a dead stop--because they are often easier to control | ||
1665 | // that way using the limited interface of the keyboard or game controller. The | ||
1666 | // VEHICLE_BANKING_MIX enables combinations of both realistic and non-realistic | ||
1667 | // banking by functioning as a slider between a banking that is correspondingly | ||
1668 | // totally static (0.0) and totally dynamic (1.0). By "static" we mean that the | ||
1669 | // banking effect depends only on the vehicle's rotation about its roll-axis compared | ||
1670 | // to "dynamic" where the banking is also proportional to its velocity along its | ||
1671 | // roll-axis. Finding the best value of the "mixture" will probably require trial and error. | ||
1672 | // The time it takes for the banking behavior to defeat a preexisting angular velocity about the | ||
1673 | // world z-axis is determined by the VEHICLE_BANKING_TIMESCALE. So if you want the vehicle to | ||
1674 | // bank quickly then give it a banking timescale of about a second or less, otherwise you can | ||
1675 | // make a sluggish vehicle by giving it a timescale of several seconds. | ||
1676 | public void ComputeAngularBanking() | ||
1677 | { | ||
1678 | if (BSParam.VehicleEnableAngularBanking && m_bankingEfficiency != 0 && m_verticalAttractionTimescale < m_verticalAttractionCutoff) | ||
1679 | { | ||
1680 | Vector3 bankingContributionV = Vector3.Zero; | ||
1681 | |||
1682 | // Rotate a UnitZ vector (pointing up) to how the vehicle is oriented. | ||
1683 | // As the vehicle rolls to the right or left, the Y value will increase from | ||
1684 | // zero (straight up) to 1 or -1 (full tilt right or left) | ||
1685 | Vector3 rollComponents = Vector3.UnitZ * VehicleFrameOrientation; | ||
1686 | |||
1687 | // Figure out the yaw value for this much roll. | ||
1688 | float yawAngle = m_angularMotorDirection.X * m_bankingEfficiency; | ||
1689 | // actual error = static turn error + dynamic turn error | ||
1690 | float mixedYawAngle =(yawAngle * (1f - m_bankingMix)) + ((yawAngle * m_bankingMix) * VehicleForwardSpeed); | ||
1691 | |||
1692 | // TODO: the banking effect should not go to infinity but what to limit it to? | ||
1693 | // And what should happen when this is being added to a user defined yaw that is already PI*4? | ||
1694 | mixedYawAngle = ClampInRange(-FourPI, mixedYawAngle, FourPI); | ||
1695 | |||
1696 | // Build the force vector to change rotation from what it is to what it should be | ||
1697 | bankingContributionV.Z = -mixedYawAngle; | ||
1698 | |||
1699 | // Don't do it all at once. Fudge because 1 second is too fast with most user defined roll as PI*4. | ||
1700 | bankingContributionV /= m_bankingTimescale * BSParam.VehicleAngularBankingTimescaleFudge; | ||
1701 | |||
1702 | VehicleRotationalVelocity += bankingContributionV; | ||
1703 | |||
1704 | |||
1705 | VDetailLog("{0}, MoveAngular,Banking,rollComp={1},speed={2},rollComp={3},yAng={4},mYAng={5},ret={6}", | ||
1706 | ControllingPrim.LocalID, rollComponents, VehicleForwardSpeed, rollComponents, yawAngle, mixedYawAngle, bankingContributionV); | ||
1707 | } | ||
1708 | } | ||
1709 | |||
1710 | // This is from previous instantiations of XXXDynamics.cs. | ||
1711 | // Applies roll reference frame. | ||
1712 | // TODO: is this the right way to separate the code to do this operation? | ||
1713 | // Should this be in MoveAngular()? | ||
1714 | internal void LimitRotation(float timestep) | ||
1715 | { | ||
1716 | Quaternion rotq = VehicleOrientation; | ||
1717 | Quaternion m_rot = rotq; | ||
1718 | if (m_RollreferenceFrame != Quaternion.Identity) | ||
1719 | { | ||
1720 | if (rotq.X >= m_RollreferenceFrame.X) | ||
1721 | { | ||
1722 | m_rot.X = rotq.X - (m_RollreferenceFrame.X / 2); | ||
1723 | } | ||
1724 | if (rotq.Y >= m_RollreferenceFrame.Y) | ||
1725 | { | ||
1726 | m_rot.Y = rotq.Y - (m_RollreferenceFrame.Y / 2); | ||
1727 | } | ||
1728 | if (rotq.X <= -m_RollreferenceFrame.X) | ||
1729 | { | ||
1730 | m_rot.X = rotq.X + (m_RollreferenceFrame.X / 2); | ||
1731 | } | ||
1732 | if (rotq.Y <= -m_RollreferenceFrame.Y) | ||
1733 | { | ||
1734 | m_rot.Y = rotq.Y + (m_RollreferenceFrame.Y / 2); | ||
1735 | } | ||
1736 | } | ||
1737 | if ((m_flags & VehicleFlag.LOCK_ROTATION) != 0) | ||
1738 | { | ||
1739 | m_rot.X = 0; | ||
1740 | m_rot.Y = 0; | ||
1741 | } | ||
1742 | if (rotq != m_rot) | ||
1743 | { | ||
1744 | VehicleOrientation = m_rot; | ||
1745 | VDetailLog("{0}, LimitRotation,done,orig={1},new={2}", ControllingPrim.LocalID, rotq, m_rot); | ||
1746 | } | ||
1747 | |||
1748 | } | ||
1749 | |||
1750 | // Given a friction vector (reduction in seconds) and a timestep, return the factor to reduce | ||
1751 | // some value by to apply this friction. | ||
1752 | private Vector3 ComputeFrictionFactor(Vector3 friction, float pTimestep) | ||
1753 | { | ||
1754 | Vector3 frictionFactor = Vector3.Zero; | ||
1755 | if (friction != BSMotor.InfiniteVector) | ||
1756 | { | ||
1757 | // frictionFactor = (Vector3.One / FrictionTimescale) * timeStep; | ||
1758 | // Individual friction components can be 'infinite' so compute each separately. | ||
1759 | frictionFactor.X = (friction.X == BSMotor.Infinite) ? 0f : (1f / friction.X); | ||
1760 | frictionFactor.Y = (friction.Y == BSMotor.Infinite) ? 0f : (1f / friction.Y); | ||
1761 | frictionFactor.Z = (friction.Z == BSMotor.Infinite) ? 0f : (1f / friction.Z); | ||
1762 | frictionFactor *= pTimestep; | ||
1763 | } | ||
1764 | return frictionFactor; | ||
1765 | } | ||
1766 | |||
1767 | private float SortedClampInRange(float clampa, float val, float clampb) | ||
1768 | { | ||
1769 | if (clampa > clampb) | ||
1770 | { | ||
1771 | float temp = clampa; | ||
1772 | clampa = clampb; | ||
1773 | clampb = temp; | ||
1774 | } | ||
1775 | return ClampInRange(clampa, val, clampb); | ||
1776 | |||
1777 | } | ||
1778 | |||
1779 | //Given a Vector and a unit vector will return the amount of the vector is on the same axis as the unit. | ||
1780 | private Vector3 ProjectVector(Vector3 vector, Vector3 onNormal) | ||
1781 | { | ||
1782 | float vectorDot = Vector3.Dot(vector, onNormal); | ||
1783 | return onNormal * vectorDot; | ||
1784 | |||
1785 | } | ||
1786 | |||
1787 | private float ClampInRange(float low, float val, float high) | ||
1788 | { | ||
1789 | return Math.Max(low, Math.Min(val, high)); | ||
1790 | // return Utils.Clamp(val, low, high); | ||
1791 | } | ||
1792 | |||
1793 | // Invoke the detailed logger and output something if it's enabled. | ||
1794 | private void VDetailLog(string msg, params Object[] args) | ||
1795 | { | ||
1796 | if (ControllingPrim.PhysScene.VehicleLoggingEnabled) | ||
1797 | ControllingPrim.PhysScene.DetailLog(msg, args); | ||
1798 | } | ||
1799 | } | ||
1800 | } | ||