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