aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Physics/ChOdePlugin/ODEPrim.cs
diff options
context:
space:
mode:
Diffstat (limited to 'OpenSim/Region/Physics/ChOdePlugin/ODEPrim.cs')
-rw-r--r--OpenSim/Region/Physics/ChOdePlugin/ODEPrim.cs3861
1 files changed, 3861 insertions, 0 deletions
diff --git a/OpenSim/Region/Physics/ChOdePlugin/ODEPrim.cs b/OpenSim/Region/Physics/ChOdePlugin/ODEPrim.cs
new file mode 100644
index 0000000..8402082
--- /dev/null
+++ b/OpenSim/Region/Physics/ChOdePlugin/ODEPrim.cs
@@ -0,0 +1,3861 @@
1/* Copyright (c) Contributors, http://opensimulator.org/
2 * See CONTRIBUTORS.TXT for a full list of copyright holders.
3 * Redistribution and use in source and binary forms, with or without
4 * modification, are permitted provided that the following conditions are met:
5 * * Redistributions of source code must retain the above copyright
6 * notice, this list of conditions and the following disclaimer.
7 * * Redistributions in binary form must reproduce the above copyright
8 * notice, this list of conditions and the following disclaimer in the
9 * documentation and/or other materials provided with the distribution.
10 * * Neither the name of the OpenSimulator Project nor the
11 * names of its contributors may be used to endorse or promote products
12 * derived from this software without specific prior written permission.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
17 * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
18 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
19 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
20 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
21 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
23 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 *
25 * Revised March 5th 2010 by Kitto Flora. ODEDynamics.cs
26 * rolled into ODEPrim.cs
27 */
28
29using System;
30using System.Collections.Generic;
31using System.Reflection;
32using System.Runtime.InteropServices;
33using System.Threading;
34using log4net;
35using OpenMetaverse;
36using Ode.NET;
37using OpenSim.Framework;
38using OpenSim.Region.Physics.Manager;
39
40
41namespace OpenSim.Region.Physics.OdePlugin
42{
43 /// <summary>
44 /// Various properties that ODE uses for AMotors but isn't exposed in ODE.NET so we must define them ourselves.
45 /// </summary>
46
47 public class OdePrim : PhysicsActor
48 {
49 private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
50
51 private Vector3 _position;
52 private Vector3 _velocity;
53 private Vector3 _torque;
54 private Vector3 m_lastVelocity;
55 private Vector3 m_lastposition;
56 private Quaternion m_lastorientation = new Quaternion();
57 private Vector3 m_rotationalVelocity;
58 private Vector3 _size;
59 private Vector3 _acceleration;
60 // private d.Vector3 _zeroPosition = new d.Vector3(0.0f, 0.0f, 0.0f);
61 private Quaternion _orientation;
62 private Vector3 m_taintposition;
63 private Vector3 m_taintsize;
64 private Vector3 m_taintVelocity;
65 private Vector3 m_taintTorque;
66 private Quaternion m_taintrot;
67 private Vector3 m_rotateEnable = Vector3.One; // Current setting
68 private Vector3 m_rotateEnableRequest = Vector3.One; // Request from LSL
69 private bool m_rotateEnableUpdate = false;
70 private Vector3 m_lockX;
71 private Vector3 m_lockY;
72 private Vector3 m_lockZ;
73 private IntPtr Amotor = IntPtr.Zero;
74 private IntPtr AmotorX = IntPtr.Zero;
75 private IntPtr AmotorY = IntPtr.Zero;
76 private IntPtr AmotorZ = IntPtr.Zero;
77
78 private Vector3 m_PIDTarget;
79 private float m_PIDTau;
80 private float PID_D = 35f;
81 private float PID_G = 25f;
82 private bool m_usePID = false;
83
84 private Quaternion m_APIDTarget = new Quaternion();
85 private float m_APIDStrength = 0.5f;
86 private float m_APIDDamping = 0.5f;
87 private bool m_useAPID = false;
88 private float m_APIDdamper = 1.0f;
89
90 // These next 7 params apply to llSetHoverHeight(float height, integer water, float tau),
91 // do not confuse with VEHICLE HOVER
92
93 private float m_PIDHoverHeight;
94 private float m_PIDHoverTau;
95 private bool m_useHoverPID;
96 private PIDHoverType m_PIDHoverType = PIDHoverType.Ground;
97 private float m_targetHoverHeight;
98 private float m_groundHeight;
99 private float m_waterHeight;
100 private float m_buoyancy; //m_buoyancy set by llSetBuoyancy()
101
102 // private float m_tensor = 5f;
103 private int body_autodisable_frames = 20;
104
105
106 private const CollisionCategories m_default_collisionFlags = (CollisionCategories.Geom
107 | CollisionCategories.Space
108 | CollisionCategories.Body
109 | CollisionCategories.Character
110 );
111 private bool m_taintshape;
112 private bool m_taintPhysics;
113 private bool m_collidesLand = true;
114 private bool m_collidesWater;
115 public bool m_returnCollisions;
116
117 // Default we're a Geometry
118 private CollisionCategories m_collisionCategories = (CollisionCategories.Geom);
119
120 // Default, Collide with Other Geometries, spaces and Bodies
121 private CollisionCategories m_collisionFlags = m_default_collisionFlags;
122
123 public bool m_taintremove;
124 public bool m_taintdisable;
125 public bool m_disabled;
126 public bool m_taintadd;
127 public bool m_taintselected;
128 public bool m_taintCollidesWater;
129
130 public uint m_localID;
131
132 //public GCHandle gc;
133 private CollisionLocker ode;
134
135 private bool m_meshfailed = false;
136 private bool m_taintforce = false;
137 private bool m_taintaddangularforce = false;
138 private Vector3 m_force;
139 private List<Vector3> m_forcelist = new List<Vector3>();
140 private List<Vector3> m_angularforcelist = new List<Vector3>();
141
142 private IMesh _mesh;
143 private PrimitiveBaseShape _pbs;
144 private OdeScene _parent_scene;
145 public IntPtr m_targetSpace = IntPtr.Zero;
146 public IntPtr prim_geom;
147// public IntPtr prev_geom;
148 public IntPtr _triMeshData;
149
150 private IntPtr _linkJointGroup = IntPtr.Zero;
151 private PhysicsActor _parent;
152 private PhysicsActor m_taintparent;
153
154 private List<OdePrim> childrenPrim = new List<OdePrim>();
155
156 private bool iscolliding;
157 private bool m_isphysical;
158 private bool m_isSelected;
159
160 internal bool m_isVolumeDetect; // If true, this prim only detects collisions but doesn't collide actively
161
162 private bool m_throttleUpdates;
163 private int throttleCounter;
164 public int m_interpenetrationcount;
165 public float m_collisionscore;
166 public int m_roundsUnderMotionThreshold;
167 private int m_crossingfailures;
168
169 public bool outofBounds;
170 private float m_density = 10.000006836f; // Aluminum g/cm3;
171
172 public bool _zeroFlag; // if body has been stopped
173 private bool m_lastUpdateSent;
174
175 public IntPtr Body = IntPtr.Zero;
176 public String m_primName;
177 private Vector3 _target_velocity;
178 public d.Mass pMass;
179
180 public int m_eventsubscription;
181 private CollisionEventUpdate CollisionEventsThisFrame;
182
183 private IntPtr m_linkJoint = IntPtr.Zero;
184
185 public volatile bool childPrim;
186
187 internal int m_material = (int)Material.Wood;
188
189 private int frcount = 0; // Used to limit dynamics debug output to
190 private int revcount = 0; // Reverse motion while > 0
191
192 private IntPtr m_body = IntPtr.Zero;
193
194 // Vehicle properties ============================================================================================
195 private Vehicle m_type = Vehicle.TYPE_NONE; // If a 'VEHICLE', and what kind
196 // private Quaternion m_referenceFrame = Quaternion.Identity; // Axis modifier
197 private VehicleFlag m_flags = (VehicleFlag) 0; // Bit settings:
198 // HOVER_TERRAIN_ONLY
199 // HOVER_GLOBAL_HEIGHT
200 // NO_DEFLECTION_UP
201 // HOVER_WATER_ONLY
202 // HOVER_UP_ONLY
203 // LIMIT_MOTOR_UP
204 // LIMIT_ROLL_ONLY
205
206 // Linear properties
207 private Vector3 m_linearMotorDirection = Vector3.Zero; // (was m_linearMotorDirectionLASTSET) the (local) Velocity
208 //requested by LSL
209 private float m_linearMotorTimescale = 0; // Motor Attack rate set by LSL
210 private float m_linearMotorDecayTimescale = 0; // Motor Decay rate set by LSL
211 private Vector3 m_linearFrictionTimescale = Vector3.Zero; // General Friction set by LSL
212
213 private Vector3 m_lLinMotorDVel = Vector3.Zero; // decayed motor
214 private Vector3 m_lLinObjectVel = Vector3.Zero; // local frame object velocity
215 private Vector3 m_wLinObjectVel = Vector3.Zero; // world frame object velocity
216
217 //Angular properties
218 private Vector3 m_angularMotorDirection = Vector3.Zero; // angular velocity requested by LSL motor
219
220 private float m_angularMotorTimescale = 0; // motor angular Attack rate set by LSL
221 private float m_angularMotorDecayTimescale = 0; // motor angular Decay rate set by LSL
222 private Vector3 m_angularFrictionTimescale = Vector3.Zero; // body angular Friction set by LSL
223
224 private Vector3 m_angularMotorDVel = Vector3.Zero; // decayed angular motor
225// private Vector3 m_angObjectVel = Vector3.Zero; // current body angular velocity
226 private Vector3 m_lastAngularVelocity = Vector3.Zero; // what was last applied to body
227
228 //Deflection properties
229 // private float m_angularDeflectionEfficiency = 0;
230 // private float m_angularDeflectionTimescale = 0;
231 // private float m_linearDeflectionEfficiency = 0;
232 // private float m_linearDeflectionTimescale = 0;
233
234 //Banking properties
235 // private float m_bankingEfficiency = 0;
236 // private float m_bankingMix = 0;
237 // private float m_bankingTimescale = 0;
238
239 //Hover and Buoyancy properties
240 private float m_VhoverHeight = 0f;
241// private float m_VhoverEfficiency = 0f;
242 private float m_VhoverTimescale = 0f;
243 private float m_VhoverTargetHeight = -1.0f; // if <0 then no hover, else its the current target height
244 private float m_VehicleBuoyancy = 0f; // Set by VEHICLE_BUOYANCY, for a vehicle.
245 // Modifies gravity. Slider between -1 (double-gravity) and 1 (full anti-gravity)
246 // KF: So far I have found no good method to combine a script-requested .Z velocity and gravity.
247 // Therefore only m_VehicleBuoyancy=1 (0g) will use the script-requested .Z velocity.
248
249 //Attractor properties
250 private float m_verticalAttractionEfficiency = 1.0f; // damped
251 private float m_verticalAttractionTimescale = 500f; // Timescale > 300 means no vert attractor.
252
253
254
255
256
257
258 public OdePrim(String primName, OdeScene parent_scene, Vector3 pos, Vector3 size,
259 Quaternion rotation, IMesh mesh, PrimitiveBaseShape pbs, bool pisPhysical, CollisionLocker dode)
260 {
261 ode = dode;
262 if (!pos.IsFinite())
263 {
264 pos = new Vector3(((float)Constants.RegionSize * 0.5f), ((float)Constants.RegionSize * 0.5f),
265 parent_scene.GetTerrainHeightAtXY(((float)Constants.RegionSize * 0.5f), ((float)Constants.RegionSize * 0.5f)) + 0.5f);
266 m_log.Warn("[PHYSICS]: Got nonFinite Object create Position");
267 }
268
269 _position = pos;
270 m_taintposition = pos;
271 PID_D = parent_scene.bodyPIDD;
272 PID_G = parent_scene.bodyPIDG;
273 m_density = parent_scene.geomDefaultDensity;
274 // m_tensor = parent_scene.bodyMotorJointMaxforceTensor;
275 body_autodisable_frames = parent_scene.bodyFramesAutoDisable;
276
277
278 prim_geom = IntPtr.Zero;
279// prev_geom = IntPtr.Zero;
280
281 if (!pos.IsFinite())
282 {
283 size = new Vector3(0.5f, 0.5f, 0.5f);
284 m_log.Warn("[PHYSICS]: Got nonFinite Object create Size");
285 }
286
287 if (size.X <= 0) size.X = 0.01f;
288 if (size.Y <= 0) size.Y = 0.01f;
289 if (size.Z <= 0) size.Z = 0.01f;
290
291 _size = size;
292 m_taintsize = _size;
293
294 if (!QuaternionIsFinite(rotation))
295 {
296 rotation = Quaternion.Identity;
297 m_log.Warn("[PHYSICS]: Got nonFinite Object create Rotation");
298 }
299
300 _orientation = rotation;
301 m_taintrot = _orientation;
302 _mesh = mesh;
303 _pbs = pbs;
304
305 _parent_scene = parent_scene;
306 m_targetSpace = (IntPtr)0;
307
308// if (pos.Z < 0)
309 if (pos.Z < parent_scene.GetTerrainHeightAtXY(pos.X, pos.Y))
310 m_isphysical = false;
311 else
312 {
313 m_isphysical = pisPhysical;
314 // If we're physical, we need to be in the master space for now.
315 // linksets *should* be in a space together.. but are not currently
316 if (m_isphysical)
317 m_targetSpace = _parent_scene.space;
318 }
319 m_primName = primName;
320 m_taintadd = true;
321 _parent_scene.AddPhysicsActorTaint(this);
322 // don't do .add() here; old geoms get recycled with the same hash
323 }
324
325 public override int PhysicsActorType
326 {
327 get { return (int) ActorTypes.Prim; }
328 set { return; }
329 }
330
331 public override bool SetAlwaysRun
332 {
333 get { return false; }
334 set { return; }
335 }
336
337 public override uint LocalID
338 {
339 set {
340 //m_log.Info("[PHYSICS]: Setting TrackerID: " + value);
341 m_localID = value; }
342 }
343
344 public override bool Grabbed
345 {
346 set { return; }
347 }
348
349 public override bool Selected
350 {
351 set {
352
353//Console.WriteLine("Sel {0} {1} {2}", m_primName, value, m_isphysical);
354 // This only makes the object not collidable if the object
355 // is physical or the object is modified somehow *IN THE FUTURE*
356 // without this, if an avatar selects prim, they can walk right
357 // through it while it's selected
358 m_collisionscore = 0;
359 if ((m_isphysical && !_zeroFlag) || !value)
360 {
361 m_taintselected = value;
362 _parent_scene.AddPhysicsActorTaint(this);
363 }
364 else
365 {
366 m_taintselected = value;
367 m_isSelected = value;
368 }
369 if(m_isSelected) disableBodySoft();
370 }
371 }
372
373 public override bool IsPhysical
374 {
375 get { return m_isphysical; }
376 set
377 {
378 m_isphysical = value;
379 if (!m_isphysical)
380 { // Zero the remembered last velocity
381 m_lastVelocity = Vector3.Zero;
382 if (m_type != Vehicle.TYPE_NONE) Halt();
383 }
384 }
385 }
386
387 public void setPrimForRemoval()
388 {
389 m_taintremove = true;
390 }
391
392 public override bool Flying
393 {
394 // no flying prims for you
395 get { return false; }
396 set { }
397 }
398
399 public override bool IsColliding
400 {
401 get { return iscolliding; }
402 set { iscolliding = value; }
403 }
404
405 public override bool CollidingGround
406 {
407 get { return false; }
408 set { return; }
409 }
410
411 public override bool CollidingObj
412 {
413 get { return false; }
414 set { return; }
415 }
416
417 public override bool ThrottleUpdates
418 {
419 get { return m_throttleUpdates; }
420 set { m_throttleUpdates = value; }
421 }
422
423 public override bool Stopped
424 {
425 get { return _zeroFlag; }
426 }
427
428 public override Vector3 Position
429 {
430 get { return _position; }
431
432 set { _position = value;
433 //m_log.Info("[PHYSICS]: " + _position.ToString());
434 }
435 }
436
437 public override Vector3 Size
438 {
439 get { return _size; }
440 set
441 {
442 if (value.IsFinite())
443 {
444 _size = value;
445 }
446 else
447 {
448 m_log.Warn("[PHYSICS]: Got NaN Size on object");
449 }
450 }
451 }
452
453 public override float Mass
454 {
455 get { return CalculateMass(); }
456 }
457
458 public override Vector3 Force
459 {
460 //get { return Vector3.Zero; }
461 get { return m_force; }
462 set
463 {
464 if (value.IsFinite())
465 {
466 m_force = value;
467 }
468 else
469 {
470 m_log.Warn("[PHYSICS]: NaN in Force Applied to an Object");
471 }
472 }
473 }
474
475 public override int VehicleType
476 {
477 get { return (int)m_type; }
478 set { ProcessTypeChange((Vehicle)value); }
479 }
480
481 public override void VehicleFloatParam(int param, float value)
482 {
483 ProcessFloatVehicleParam((Vehicle) param, value);
484 }
485
486 public override void VehicleVectorParam(int param, Vector3 value)
487 {
488 ProcessVectorVehicleParam((Vehicle) param, value);
489 }
490
491 public override void VehicleRotationParam(int param, Quaternion rotation)
492 {
493 ProcessRotationVehicleParam((Vehicle) param, rotation);
494 }
495
496 public override void VehicleFlags(int param, bool remove)
497 {
498 ProcessVehicleFlags(param, remove);
499 }
500
501 public override void SetVolumeDetect(int param)
502 {
503 lock (_parent_scene.OdeLock)
504 {
505 m_isVolumeDetect = (param!=0);
506 }
507 }
508
509 public override Vector3 CenterOfMass
510 {
511 get { return Vector3.Zero; }
512 }
513
514 public override Vector3 GeometricCenter
515 {
516 get { return Vector3.Zero; }
517 }
518
519 public override PrimitiveBaseShape Shape
520 {
521 set
522 {
523 _pbs = value;
524 m_taintshape = true;
525 }
526 }
527
528 public override Vector3 Velocity
529 {
530 get
531 {
532 // Averate previous velocity with the new one so
533 // client object interpolation works a 'little' better
534 if (_zeroFlag)
535 return Vector3.Zero;
536
537 Vector3 returnVelocity = Vector3.Zero;
538 returnVelocity.X = (m_lastVelocity.X + _velocity.X)/2;
539 returnVelocity.Y = (m_lastVelocity.Y + _velocity.Y)/2;
540 returnVelocity.Z = (m_lastVelocity.Z + _velocity.Z)/2;
541 return returnVelocity;
542 }
543 set
544 {
545 if (value.IsFinite())
546 {
547 _velocity = value;
548
549 m_taintVelocity = value;
550 _parent_scene.AddPhysicsActorTaint(this);
551 }
552 else
553 {
554 m_log.Warn("[PHYSICS]: Got NaN Velocity in Object");
555 }
556
557 }
558 }
559
560 public override Vector3 Torque
561 {
562 get
563 {
564 if (!m_isphysical || Body == IntPtr.Zero)
565 return Vector3.Zero;
566
567 return _torque;
568 }
569
570 set
571 {
572 if (value.IsFinite())
573 {
574 m_taintTorque = value;
575 _parent_scene.AddPhysicsActorTaint(this);
576 }
577 else
578 {
579 m_log.Warn("[PHYSICS]: Got NaN Torque in Object");
580 }
581 }
582 }
583
584 public override float CollisionScore
585 {
586 get { return m_collisionscore; }
587 set { m_collisionscore = value; }
588 }
589
590 public override bool Kinematic
591 {
592 get { return false; }
593 set { }
594 }
595
596 public override Quaternion Orientation
597 {
598 get { return _orientation; }
599 set
600 {
601 if (QuaternionIsFinite(value))
602 {
603 _orientation = value;
604 }
605 else
606 m_log.Warn("[PHYSICS]: Got NaN quaternion Orientation from Scene in Object");
607
608 }
609 }
610
611
612 public override bool FloatOnWater
613 {
614 set {
615 m_taintCollidesWater = value;
616 _parent_scene.AddPhysicsActorTaint(this);
617 }
618 }
619
620 public override void SetMomentum(Vector3 momentum)
621 {
622 }
623
624 public override Vector3 PIDTarget
625 {
626 set
627 {
628 if (value.IsFinite())
629 {
630 m_PIDTarget = value;
631 }
632 else
633 m_log.Warn("[PHYSICS]: Got NaN PIDTarget from Scene on Object");
634 }
635 }
636 public override bool PIDActive { set { m_usePID = value; } }
637 public override float PIDTau { set { m_PIDTau = value; } }
638
639 // For RotLookAt
640 public override Quaternion APIDTarget { set { m_APIDTarget = value; } }
641 public override bool APIDActive { set { m_useAPID = value; } }
642 public override float APIDStrength { set { m_APIDStrength = value; } }
643 public override float APIDDamping { set { m_APIDDamping = value; } }
644
645 public override float PIDHoverHeight { set { m_PIDHoverHeight = value; ; } }
646 public override bool PIDHoverActive { set { m_useHoverPID = value; } }
647 public override PIDHoverType PIDHoverType { set { m_PIDHoverType = value; } }
648 public override float PIDHoverTau { set { m_PIDHoverTau = value; } }
649
650 internal static bool QuaternionIsFinite(Quaternion q)
651 {
652 if (Single.IsNaN(q.X) || Single.IsInfinity(q.X))
653 return false;
654 if (Single.IsNaN(q.Y) || Single.IsInfinity(q.Y))
655 return false;
656 if (Single.IsNaN(q.Z) || Single.IsInfinity(q.Z))
657 return false;
658 if (Single.IsNaN(q.W) || Single.IsInfinity(q.W))
659 return false;
660 return true;
661 }
662
663 public override Vector3 Acceleration // client updates read data via here
664 {
665 get { return _acceleration; }
666 }
667
668
669 public void SetAcceleration(Vector3 accel) // No one calls this, and it would not do anything.
670 {
671 _acceleration = accel;
672 }
673
674 public override void AddForce(Vector3 force, bool pushforce)
675 {
676 if (force.IsFinite())
677 {
678 lock (m_forcelist)
679 m_forcelist.Add(force);
680
681 m_taintforce = true;
682 }
683 else
684 {
685 m_log.Warn("[PHYSICS]: Got Invalid linear force vector from Scene in Object");
686 }
687 //m_log.Info("[PHYSICS]: Added Force:" + force.ToString() + " to prim at " + Position.ToString());
688 }
689
690 public override void AddAngularForce(Vector3 force, bool pushforce)
691 {
692 if (force.IsFinite())
693 {
694 m_angularforcelist.Add(force);
695 m_taintaddangularforce = true;
696 }
697 else
698 {
699 m_log.Warn("[PHYSICS]: Got Invalid Angular force vector from Scene in Object");
700 }
701 }
702
703 public override Vector3 RotationalVelocity
704 {
705 get
706 {
707 return m_rotationalVelocity;
708 }
709 set
710 {
711 if (value.IsFinite())
712 {
713 m_rotationalVelocity = value;
714 }
715 else
716 {
717 m_log.Warn("[PHYSICS]: Got NaN RotationalVelocity in Object");
718 }
719 }
720 }
721
722 public override void CrossingFailure()
723 {
724 m_crossingfailures++;
725 if (m_crossingfailures > _parent_scene.geomCrossingFailuresBeforeOutofbounds)
726 {
727 base.RaiseOutOfBounds(_position);
728 return;
729 }
730 else if (m_crossingfailures == _parent_scene.geomCrossingFailuresBeforeOutofbounds)
731 {
732 m_log.Warn("[PHYSICS]: Too many crossing failures for: " + m_primName);
733 }
734 }
735
736 public override float Buoyancy
737 {
738 get { return m_buoyancy; }
739 set { m_buoyancy = value; }
740 }
741
742 public override void link(PhysicsActor obj)
743 {
744 m_taintparent = obj;
745 }
746
747 public override void delink()
748 {
749 m_taintparent = null;
750 }
751
752 public override void LockAngularMotion(Vector3 axis)
753 {
754 // This is actually ROTATION ENABLE, not a lock.
755 // default is <1,1,1> which is all enabled.
756 // The lock value is updated inside Move(), no point in using the taint system.
757 // OS 'm_taintAngularLock' etc change to m_rotateEnable.
758 if (axis.IsFinite())
759 {
760 axis.X = (axis.X > 0) ? 1f : 0f;
761 axis.Y = (axis.Y > 0) ? 1f : 0f;
762 axis.Z = (axis.Z > 0) ? 1f : 0f;
763 m_log.DebugFormat("[axislock]: <{0},{1},{2}>", axis.X, axis.Y, axis.Z);
764 m_rotateEnableRequest = axis;
765 m_rotateEnableUpdate = true;
766 }
767 else
768 {
769 m_log.Warn("[PHYSICS]: Got NaN locking axis from Scene on Object");
770 }
771 }
772
773
774 public void SetGeom(IntPtr geom)
775 {
776 if(prim_geom != IntPtr.Zero)
777 {
778 // Remove any old entries
779//string tPA;
780//_parent_scene.geom_name_map.TryGetValue(prim_geom, out tPA);
781//Console.WriteLine("**** Remove {0}", tPA);
782 if(_parent_scene.geom_name_map.ContainsKey(prim_geom)) _parent_scene.geom_name_map.Remove(prim_geom);
783 if(_parent_scene.actor_name_map.ContainsKey(prim_geom)) _parent_scene.actor_name_map.Remove(prim_geom);
784 d.GeomDestroy(prim_geom);
785 }
786
787 prim_geom = geom;
788//Console.WriteLine("SetGeom to " + prim_geom + " for " + m_primName);
789 if (prim_geom != IntPtr.Zero)
790 {
791 _parent_scene.geom_name_map[prim_geom] = this.m_primName;
792 _parent_scene.actor_name_map[prim_geom] = (PhysicsActor)this;
793 d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories);
794 d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
795//Console.WriteLine("**** Create {2} Dicts: actor={0} name={1}", _parent_scene.actor_name_map.Count, _parent_scene.geom_name_map.Count, this.m_primName);
796 }
797
798 if (childPrim)
799 {
800 if (_parent != null && _parent is OdePrim)
801 {
802 OdePrim parent = (OdePrim)_parent;
803//Console.WriteLine("SetGeom calls ChildSetGeom");
804 parent.ChildSetGeom(this);
805 }
806 }
807 //m_log.Warn("Setting Geom to: " + prim_geom);
808 }
809
810 public void enableBodySoft()
811 {
812 if (!childPrim)
813 {
814 if (m_isphysical && Body != IntPtr.Zero)
815 {
816 d.BodyEnable(Body);
817 if (m_type != Vehicle.TYPE_NONE)
818 Enable(Body, _parent_scene);
819 }
820
821 m_disabled = false;
822 }
823 }
824
825 public void disableBodySoft()
826 {
827 m_disabled = true;
828
829 if (m_isphysical && Body != IntPtr.Zero)
830 {
831 d.BodyDisable(Body);
832 Halt();
833 }
834 }
835
836 public void enableBody()
837 {
838 // Don't enable this body if we're a child prim
839 // this should be taken care of in the parent function not here
840 if (!childPrim)
841 {
842 // Sets the geom to a body
843 Body = d.BodyCreate(_parent_scene.world);
844
845 setMass();
846 d.BodySetPosition(Body, _position.X, _position.Y, _position.Z);
847 d.Quaternion myrot = new d.Quaternion();
848 myrot.X = _orientation.X;
849 myrot.Y = _orientation.Y;
850 myrot.Z = _orientation.Z;
851 myrot.W = _orientation.W;
852 d.BodySetQuaternion(Body, ref myrot);
853 d.GeomSetBody(prim_geom, Body);
854 m_collisionCategories |= CollisionCategories.Body;
855 m_collisionFlags |= (CollisionCategories.Land | CollisionCategories.Wind);
856
857 d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories);
858 d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
859
860 d.BodySetAutoDisableFlag(Body, true);
861 d.BodySetAutoDisableSteps(Body, body_autodisable_frames);
862
863 // disconnect from world gravity so we can apply buoyancy
864 d.BodySetGravityMode (Body, false);
865
866 m_interpenetrationcount = 0;
867 m_collisionscore = 0;
868 m_disabled = false;
869
870 if (m_type != Vehicle.TYPE_NONE)
871 {
872 Enable(Body, _parent_scene);
873 }
874
875 _parent_scene.addActivePrim(this);
876 }
877 }
878
879 #region Mass Calculation
880
881 private float CalculateMass()
882 {
883 float volume = _size.X * _size.Y * _size.Z; // default
884 float tmp;
885
886 float returnMass = 0;
887 float hollowAmount = (float)_pbs.ProfileHollow * 2.0e-5f;
888 float hollowVolume = hollowAmount * hollowAmount;
889
890 switch (_pbs.ProfileShape)
891 {
892 case ProfileShape.Square:
893 // default box
894
895 if (_pbs.PathCurve == (byte)Extrusion.Straight)
896 {
897 if (hollowAmount > 0.0)
898 {
899 switch (_pbs.HollowShape)
900 {
901 case HollowShape.Square:
902 case HollowShape.Same:
903 break;
904
905 case HollowShape.Circle:
906
907 hollowVolume *= 0.78539816339f;
908 break;
909
910 case HollowShape.Triangle:
911
912 hollowVolume *= (0.5f * .5f);
913 break;
914
915 default:
916 hollowVolume = 0;
917 break;
918 }
919 volume *= (1.0f - hollowVolume);
920 }
921 }
922
923 else if (_pbs.PathCurve == (byte)Extrusion.Curve1)
924 {
925 //a tube
926
927 volume *= 0.78539816339e-2f * (float)(200 - _pbs.PathScaleX);
928 tmp= 1.0f -2.0e-2f * (float)(200 - _pbs.PathScaleY);
929 volume -= volume*tmp*tmp;
930
931 if (hollowAmount > 0.0)
932 {
933 hollowVolume *= hollowAmount;
934
935 switch (_pbs.HollowShape)
936 {
937 case HollowShape.Square:
938 case HollowShape.Same:
939 break;
940
941 case HollowShape.Circle:
942 hollowVolume *= 0.78539816339f;;
943 break;
944
945 case HollowShape.Triangle:
946 hollowVolume *= 0.5f * 0.5f;
947 break;
948 default:
949 hollowVolume = 0;
950 break;
951 }
952 volume *= (1.0f - hollowVolume);
953 }
954 }
955
956 break;
957
958 case ProfileShape.Circle:
959
960 if (_pbs.PathCurve == (byte)Extrusion.Straight)
961 {
962 volume *= 0.78539816339f; // elipse base
963
964 if (hollowAmount > 0.0)
965 {
966 switch (_pbs.HollowShape)
967 {
968 case HollowShape.Same:
969 case HollowShape.Circle:
970 break;
971
972 case HollowShape.Square:
973 hollowVolume *= 0.5f * 2.5984480504799f;
974 break;
975
976 case HollowShape.Triangle:
977 hollowVolume *= .5f * 1.27323954473516f;
978 break;
979
980 default:
981 hollowVolume = 0;
982 break;
983 }
984 volume *= (1.0f - hollowVolume);
985 }
986 }
987
988 else if (_pbs.PathCurve == (byte)Extrusion.Curve1)
989 {
990 volume *= 0.61685027506808491367715568749226e-2f * (float)(200 - _pbs.PathScaleX);
991 tmp = 1.0f - .02f * (float)(200 - _pbs.PathScaleY);
992 volume *= (1.0f - tmp * tmp);
993
994 if (hollowAmount > 0.0)
995 {
996
997 // calculate the hollow volume by it's shape compared to the prim shape
998 hollowVolume *= hollowAmount;
999
1000 switch (_pbs.HollowShape)
1001 {
1002 case HollowShape.Same:
1003 case HollowShape.Circle:
1004 break;
1005
1006 case HollowShape.Square:
1007 hollowVolume *= 0.5f * 2.5984480504799f;
1008 break;
1009
1010 case HollowShape.Triangle:
1011 hollowVolume *= .5f * 1.27323954473516f;
1012 break;
1013
1014 default:
1015 hollowVolume = 0;
1016 break;
1017 }
1018 volume *= (1.0f - hollowVolume);
1019 }
1020 }
1021 break;
1022
1023 case ProfileShape.HalfCircle:
1024 if (_pbs.PathCurve == (byte)Extrusion.Curve1)
1025 {
1026 volume *= 0.52359877559829887307710723054658f;
1027 }
1028 break;
1029
1030 case ProfileShape.EquilateralTriangle:
1031
1032 if (_pbs.PathCurve == (byte)Extrusion.Straight)
1033 {
1034 volume *= 0.32475953f;
1035
1036 if (hollowAmount > 0.0)
1037 {
1038
1039 // calculate the hollow volume by it's shape compared to the prim shape
1040 switch (_pbs.HollowShape)
1041 {
1042 case HollowShape.Same:
1043 case HollowShape.Triangle:
1044 hollowVolume *= .25f;
1045 break;
1046
1047 case HollowShape.Square:
1048 hollowVolume *= 0.499849f * 3.07920140172638f;
1049 break;
1050
1051 case HollowShape.Circle:
1052 // Hollow shape is a perfect cyllinder in respect to the cube's scale
1053 // Cyllinder hollow volume calculation
1054
1055 hollowVolume *= 0.1963495f * 3.07920140172638f;
1056 break;
1057
1058 default:
1059 hollowVolume = 0;
1060 break;
1061 }
1062 volume *= (1.0f - hollowVolume);
1063 }
1064 }
1065 else if (_pbs.PathCurve == (byte)Extrusion.Curve1)
1066 {
1067 volume *= 0.32475953f;
1068 volume *= 0.01f * (float)(200 - _pbs.PathScaleX);
1069 tmp = 1.0f - .02f * (float)(200 - _pbs.PathScaleY);
1070 volume *= (1.0f - tmp * tmp);
1071
1072 if (hollowAmount > 0.0)
1073 {
1074
1075 hollowVolume *= hollowAmount;
1076
1077 switch (_pbs.HollowShape)
1078 {
1079 case HollowShape.Same:
1080 case HollowShape.Triangle:
1081 hollowVolume *= .25f;
1082 break;
1083
1084 case HollowShape.Square:
1085 hollowVolume *= 0.499849f * 3.07920140172638f;
1086 break;
1087
1088 case HollowShape.Circle:
1089
1090 hollowVolume *= 0.1963495f * 3.07920140172638f;
1091 break;
1092
1093 default:
1094 hollowVolume = 0;
1095 break;
1096 }
1097 volume *= (1.0f - hollowVolume);
1098 }
1099 }
1100 break;
1101
1102 default:
1103 break;
1104 }
1105
1106
1107
1108 float taperX1;
1109 float taperY1;
1110 float taperX;
1111 float taperY;
1112 float pathBegin;
1113 float pathEnd;
1114 float profileBegin;
1115 float profileEnd;
1116
1117 if (_pbs.PathCurve == (byte)Extrusion.Straight || _pbs.PathCurve == (byte)Extrusion.Flexible)
1118 {
1119 taperX1 = _pbs.PathScaleX * 0.01f;
1120 if (taperX1 > 1.0f)
1121 taperX1 = 2.0f - taperX1;
1122 taperX = 1.0f - taperX1;
1123
1124 taperY1 = _pbs.PathScaleY * 0.01f;
1125 if (taperY1 > 1.0f)
1126 taperY1 = 2.0f - taperY1;
1127 taperY = 1.0f - taperY1;
1128 }
1129 else
1130 {
1131 taperX = _pbs.PathTaperX * 0.01f;
1132 if (taperX < 0.0f)
1133 taperX = -taperX;
1134 taperX1 = 1.0f - taperX;
1135
1136 taperY = _pbs.PathTaperY * 0.01f;
1137 if (taperY < 0.0f)
1138 taperY = -taperY;
1139 taperY1 = 1.0f - taperY;
1140
1141 }
1142
1143
1144 volume *= (taperX1 * taperY1 + 0.5f * (taperX1 * taperY + taperX * taperY1) + 0.3333333333f * taperX * taperY);
1145
1146 pathBegin = (float)_pbs.PathBegin * 2.0e-5f;
1147 pathEnd = 1.0f - (float)_pbs.PathEnd * 2.0e-5f;
1148 volume *= (pathEnd - pathBegin);
1149
1150// this is crude aproximation
1151 profileBegin = (float)_pbs.ProfileBegin * 2.0e-5f;
1152 profileEnd = 1.0f - (float)_pbs.ProfileEnd * 2.0e-5f;
1153 volume *= (profileEnd - profileBegin);
1154
1155 returnMass = m_density * volume;
1156
1157 if (returnMass <= 0)
1158 returnMass = 0.0001f;//ckrinke: Mass must be greater then zero.
1159// else if (returnMass > _parent_scene.maximumMassObject)
1160// returnMass = _parent_scene.maximumMassObject;
1161
1162
1163
1164
1165 // Recursively calculate mass
1166 bool HasChildPrim = false;
1167 lock (childrenPrim)
1168 {
1169 if (childrenPrim.Count > 0)
1170 {
1171 HasChildPrim = true;
1172 }
1173
1174 }
1175 if (HasChildPrim)
1176 {
1177 OdePrim[] childPrimArr = new OdePrim[0];
1178
1179 lock (childrenPrim)
1180 childPrimArr = childrenPrim.ToArray();
1181
1182 for (int i = 0; i < childPrimArr.Length; i++)
1183 {
1184 if (childPrimArr[i] != null && !childPrimArr[i].m_taintremove)
1185 returnMass += childPrimArr[i].CalculateMass();
1186 // failsafe, this shouldn't happen but with OpenSim, you never know :)
1187 if (i > 256)
1188 break;
1189 }
1190 }
1191 if (returnMass > _parent_scene.maximumMassObject)
1192 returnMass = _parent_scene.maximumMassObject;
1193 return returnMass;
1194 }// end CalculateMass
1195
1196 #endregion
1197
1198 public void setMass()
1199 {
1200 if (Body != (IntPtr) 0)
1201 {
1202 float newmass = CalculateMass();
1203
1204 //m_log.Info("[PHYSICS]: New Mass: " + newmass.ToString());
1205
1206 d.MassSetBoxTotal(out pMass, newmass, _size.X, _size.Y, _size.Z);
1207 d.BodySetMass(Body, ref pMass);
1208 }
1209 }
1210
1211 public void disableBody()
1212 {
1213 //this kills the body so things like 'mesh' can re-create it.
1214 lock (this)
1215 {
1216 if (!childPrim)
1217 {
1218 if (Body != IntPtr.Zero)
1219 {
1220 _parent_scene.remActivePrim(this);
1221 m_collisionCategories &= ~CollisionCategories.Body;
1222 m_collisionFlags &= ~(CollisionCategories.Wind | CollisionCategories.Land);
1223
1224 if (prim_geom != IntPtr.Zero)
1225 {
1226 d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories);
1227 d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
1228 }
1229
1230
1231 d.BodyDestroy(Body);
1232 lock (childrenPrim)
1233 {
1234 if (childrenPrim.Count > 0)
1235 {
1236 foreach (OdePrim prm in childrenPrim)
1237 {
1238 _parent_scene.remActivePrim(prm);
1239 prm.Body = IntPtr.Zero;
1240 }
1241 }
1242 }
1243 Body = IntPtr.Zero;
1244 }
1245 }
1246 else
1247 {
1248 _parent_scene.remActivePrim(this);
1249
1250 m_collisionCategories &= ~CollisionCategories.Body;
1251 m_collisionFlags &= ~(CollisionCategories.Wind | CollisionCategories.Land);
1252
1253 if (prim_geom != IntPtr.Zero)
1254 {
1255 d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories);
1256 d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
1257 }
1258
1259
1260 Body = IntPtr.Zero;
1261 }
1262 }
1263 m_disabled = true;
1264 m_collisionscore = 0;
1265 }
1266
1267 private static Dictionary<IMesh, IntPtr> m_MeshToTriMeshMap = new Dictionary<IMesh, IntPtr>();
1268
1269 public void setMesh(OdeScene parent_scene, IMesh mesh)
1270 {
1271 // This sleeper is there to moderate how long it takes between
1272 // setting up the mesh and pre-processing it when we get rapid fire mesh requests on a single object
1273
1274 //Thread.Sleep(10);
1275
1276 //Kill Body so that mesh can re-make the geom
1277 if (IsPhysical && Body != IntPtr.Zero)
1278 {
1279 if (childPrim)
1280 {
1281 if (_parent != null)
1282 {
1283 OdePrim parent = (OdePrim)_parent;
1284 parent.ChildDelink(this);
1285 }
1286 }
1287 else
1288 {
1289 disableBody();
1290 }
1291 }
1292 IntPtr vertices, indices;
1293 int vertexCount, indexCount;
1294 int vertexStride, triStride;
1295 mesh.getVertexListAsPtrToFloatArray(out vertices, out vertexStride, out vertexCount); // Note, that vertices are fixed in unmanaged heap
1296 mesh.getIndexListAsPtrToIntArray(out indices, out triStride, out indexCount); // Also fixed, needs release after usage
1297
1298 mesh.releaseSourceMeshData(); // free up the original mesh data to save memory
1299 if (m_MeshToTriMeshMap.ContainsKey(mesh))
1300 {
1301 _triMeshData = m_MeshToTriMeshMap[mesh];
1302 }
1303 else
1304 {
1305 _triMeshData = d.GeomTriMeshDataCreate();
1306
1307 d.GeomTriMeshDataBuildSimple(_triMeshData, vertices, vertexStride, vertexCount, indices, indexCount, triStride);
1308 d.GeomTriMeshDataPreprocess(_triMeshData);
1309 m_MeshToTriMeshMap[mesh] = _triMeshData;
1310 }
1311
1312 _parent_scene.waitForSpaceUnlock(m_targetSpace);
1313 try
1314 {
1315 // if (prim_geom == IntPtr.Zero) // setGeom takes care of phys engine recreate and prim_geom pointer
1316 // {
1317 SetGeom(d.CreateTriMesh(m_targetSpace, _triMeshData, parent_scene.triCallback, null, null));
1318 // }
1319 }
1320 catch (AccessViolationException)
1321 {
1322 m_log.Error("[PHYSICS]: MESH LOCKED");
1323 return;
1324 }
1325
1326
1327 // if (IsPhysical && Body == (IntPtr) 0)
1328 // {
1329 // Recreate the body
1330 // m_interpenetrationcount = 0;
1331 // m_collisionscore = 0;
1332
1333 // enableBody();
1334 // }
1335 }
1336
1337 public void ProcessTaints(float timestep) //=============================================================================
1338 {
1339 if (m_taintadd)
1340 {
1341 changeadd(timestep);
1342 }
1343
1344 if (prim_geom != IntPtr.Zero)
1345 {
1346 if (!_position.ApproxEquals(m_taintposition, 0f))
1347 {
1348 changemove(timestep);
1349 }
1350 if (m_taintrot != _orientation)
1351 {
1352 if(childPrim && IsPhysical) // For physical child prim...
1353 {
1354 rotate(timestep);
1355 // KF: ODE will also rotate the parent prim!
1356 // so rotate the root back to where it was
1357 OdePrim parent = (OdePrim)_parent;
1358 parent.rotate(timestep);
1359 }
1360 else
1361 {
1362 //Just rotate the prim
1363 rotate(timestep);
1364 }
1365 }
1366 //
1367
1368 if (m_taintPhysics != m_isphysical && !(m_taintparent != _parent))
1369 {
1370 changePhysicsStatus(timestep);
1371 }//
1372
1373 if (!_size.ApproxEquals(m_taintsize,0f))
1374 changesize(timestep);
1375 //
1376
1377 if (m_taintshape)
1378 changeshape(timestep);
1379 //
1380
1381 if (m_taintforce)
1382 changeAddForce(timestep);
1383
1384 if (m_taintaddangularforce)
1385 changeAddAngularForce(timestep);
1386
1387 if (!m_taintTorque.ApproxEquals(Vector3.Zero, 0.001f))
1388 changeSetTorque(timestep);
1389
1390 if (m_taintdisable)
1391 changedisable(timestep);
1392
1393 if (m_taintselected != m_isSelected)
1394 changeSelectedStatus(timestep);
1395
1396 if (!m_taintVelocity.ApproxEquals(Vector3.Zero, 0.001f))
1397 changevelocity(timestep);
1398
1399 if (m_taintparent != _parent)
1400 changelink(timestep);
1401
1402 if (m_taintCollidesWater != m_collidesWater)
1403 changefloatonwater(timestep);
1404/* obsolete
1405 if (!m_angularLock.ApproxEquals(m_taintAngularLock,0f))
1406 changeAngularLock(timestep);
1407 */
1408 }
1409 else
1410 {
1411 m_log.Error("[PHYSICS]: The scene reused a disposed PhysActor! *waves finger*, Don't be evil. A couple of things can cause this. An improper prim breakdown(be sure to set prim_geom to zero after d.GeomDestroy! An improper buildup (creating the geom failed). Or, the Scene Reused a physics actor after disposing it.)");
1412 }
1413 }
1414
1415/* obsolete
1416 private void changeAngularLock(float timestep)
1417 {
1418 if (_parent == null)
1419 {
1420 m_angularLock = m_taintAngularLock;
1421 m_angularLockSet = true;
1422 }
1423 }
1424 */
1425 private void changelink(float timestep)
1426 {
1427 // If the newly set parent is not null
1428 // create link
1429 if (_parent == null && m_taintparent != null)
1430 {
1431 if (m_taintparent.PhysicsActorType == (int)ActorTypes.Prim)
1432 {
1433 OdePrim obj = (OdePrim)m_taintparent;
1434 //obj.disableBody();
1435 obj.ParentPrim(this);
1436
1437 /*
1438 if (obj.Body != (IntPtr)0 && Body != (IntPtr)0 && obj.Body != Body)
1439 {
1440 _linkJointGroup = d.JointGroupCreate(0);
1441 m_linkJoint = d.JointCreateFixed(_parent_scene.world, _linkJointGroup);
1442 d.JointAttach(m_linkJoint, obj.Body, Body);
1443 d.JointSetFixed(m_linkJoint);
1444 }
1445 */
1446 }
1447 }
1448 // If the newly set parent is null
1449 // destroy link
1450 else if (_parent != null && m_taintparent == null)
1451 {
1452 if (_parent is OdePrim)
1453 {
1454 OdePrim obj = (OdePrim)_parent;
1455 obj.ChildDelink(this);
1456 childPrim = false;
1457 //_parent = null;
1458 }
1459
1460 /*
1461 if (Body != (IntPtr)0 && _linkJointGroup != (IntPtr)0)
1462 d.JointGroupDestroy(_linkJointGroup);
1463
1464 _linkJointGroup = (IntPtr)0;
1465 m_linkJoint = (IntPtr)0;
1466 */
1467 }
1468
1469 _parent = m_taintparent;
1470 m_taintPhysics = m_isphysical;
1471 }
1472
1473 // I'm the parent
1474 // prim is the child
1475 public void ParentPrim(OdePrim prim)
1476 {
1477 if (this.m_localID != prim.m_localID)
1478 {
1479 if (Body == IntPtr.Zero)
1480 {
1481 Body = d.BodyCreate(_parent_scene.world);
1482 // disconnect from world gravity so we can apply buoyancy
1483 d.BodySetGravityMode (Body, false);
1484
1485 setMass();
1486 }
1487 if (Body != IntPtr.Zero)
1488 {
1489 lock (childrenPrim)
1490 {
1491 if (!childrenPrim.Contains(prim))
1492 {
1493 childrenPrim.Add(prim);
1494
1495 foreach (OdePrim prm in childrenPrim)
1496 {
1497 d.Mass m2;
1498 d.MassSetZero(out m2);
1499 d.MassSetBoxTotal(out m2, prim.CalculateMass(), prm._size.X, prm._size.Y, prm._size.Z);
1500
1501
1502 d.Quaternion quat = new d.Quaternion();
1503 quat.W = prm._orientation.W;
1504 quat.X = prm._orientation.X;
1505 quat.Y = prm._orientation.Y;
1506 quat.Z = prm._orientation.Z;
1507
1508 d.Matrix3 mat = new d.Matrix3();
1509 d.RfromQ(out mat, ref quat);
1510 d.MassRotate(ref m2, ref mat);
1511 d.MassTranslate(ref m2, Position.X - prm.Position.X, Position.Y - prm.Position.Y, Position.Z - prm.Position.Z);
1512 d.MassAdd(ref pMass, ref m2);
1513 }
1514 foreach (OdePrim prm in childrenPrim)
1515 {
1516
1517 prm.m_collisionCategories |= CollisionCategories.Body;
1518 prm.m_collisionFlags |= (CollisionCategories.Land | CollisionCategories.Wind);
1519
1520 if (prm.prim_geom == IntPtr.Zero)
1521 {
1522 m_log.Warn("[PHYSICS]: Unable to link one of the linkset elements. No geom yet");
1523 continue;
1524 }
1525 d.GeomSetCategoryBits(prm.prim_geom, (int)prm.m_collisionCategories);
1526 d.GeomSetCollideBits(prm.prim_geom, (int)prm.m_collisionFlags);
1527
1528
1529 d.Quaternion quat = new d.Quaternion();
1530 quat.W = prm._orientation.W;
1531 quat.X = prm._orientation.X;
1532 quat.Y = prm._orientation.Y;
1533 quat.Z = prm._orientation.Z;
1534
1535 d.Matrix3 mat = new d.Matrix3();
1536 d.RfromQ(out mat, ref quat);
1537 if (Body != IntPtr.Zero)
1538 {
1539 d.GeomSetBody(prm.prim_geom, Body);
1540 prm.childPrim = true;
1541 d.GeomSetOffsetWorldPosition(prm.prim_geom, prm.Position.X , prm.Position.Y, prm.Position.Z);
1542 //d.GeomSetOffsetPosition(prim.prim_geom,
1543 // (Position.X - prm.Position.X) - pMass.c.X,
1544 // (Position.Y - prm.Position.Y) - pMass.c.Y,
1545 // (Position.Z - prm.Position.Z) - pMass.c.Z);
1546 d.GeomSetOffsetWorldRotation(prm.prim_geom, ref mat);
1547 //d.GeomSetOffsetRotation(prm.prim_geom, ref mat);
1548 d.MassTranslate(ref pMass, -pMass.c.X, -pMass.c.Y, -pMass.c.Z);
1549 d.BodySetMass(Body, ref pMass);
1550 }
1551 else
1552 {
1553 m_log.Debug("[PHYSICS]:I ain't got no boooooooooddy, no body");
1554 }
1555
1556
1557 prm.m_interpenetrationcount = 0;
1558 prm.m_collisionscore = 0;
1559 prm.m_disabled = false;
1560
1561 prm.Body = Body;
1562 _parent_scene.addActivePrim(prm);
1563 }
1564 m_collisionCategories |= CollisionCategories.Body;
1565 m_collisionFlags |= (CollisionCategories.Land | CollisionCategories.Wind);
1566
1567 d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories);
1568 d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
1569
1570
1571 d.Quaternion quat2 = new d.Quaternion();
1572 quat2.W = _orientation.W;
1573 quat2.X = _orientation.X;
1574 quat2.Y = _orientation.Y;
1575 quat2.Z = _orientation.Z;
1576
1577 d.Matrix3 mat2 = new d.Matrix3();
1578 d.RfromQ(out mat2, ref quat2);
1579 d.GeomSetBody(prim_geom, Body);
1580 d.GeomSetOffsetWorldPosition(prim_geom, Position.X - pMass.c.X, Position.Y - pMass.c.Y, Position.Z - pMass.c.Z);
1581 //d.GeomSetOffsetPosition(prim.prim_geom,
1582 // (Position.X - prm.Position.X) - pMass.c.X,
1583 // (Position.Y - prm.Position.Y) - pMass.c.Y,
1584 // (Position.Z - prm.Position.Z) - pMass.c.Z);
1585 //d.GeomSetOffsetRotation(prim_geom, ref mat2);
1586 d.MassTranslate(ref pMass, -pMass.c.X, -pMass.c.Y, -pMass.c.Z);
1587 d.BodySetMass(Body, ref pMass);
1588
1589 d.BodySetAutoDisableFlag(Body, true);
1590 d.BodySetAutoDisableSteps(Body, body_autodisable_frames);
1591
1592
1593 m_interpenetrationcount = 0;
1594 m_collisionscore = 0;
1595 m_disabled = false;
1596
1597 d.BodySetPosition(Body, Position.X, Position.Y, Position.Z);
1598 if (m_type != Vehicle.TYPE_NONE) Enable(Body, _parent_scene);
1599 _parent_scene.addActivePrim(this);
1600 }
1601 }
1602 }
1603 }
1604
1605 }
1606
1607 private void ChildSetGeom(OdePrim odePrim)
1608 {
1609 //if (m_isphysical && Body != IntPtr.Zero)
1610 lock (childrenPrim)
1611 {
1612 foreach (OdePrim prm in childrenPrim)
1613 {
1614 //prm.childPrim = true;
1615 prm.disableBody();
1616 //prm.m_taintparent = null;
1617 //prm._parent = null;
1618 //prm.m_taintPhysics = false;
1619 //prm.m_disabled = true;
1620 //prm.childPrim = false;
1621 }
1622 }
1623 disableBody();
1624
1625
1626 if (Body != IntPtr.Zero)
1627 {
1628 _parent_scene.remActivePrim(this);
1629 }
1630
1631 lock (childrenPrim)
1632 {
1633 foreach (OdePrim prm in childrenPrim)
1634 {
1635 ParentPrim(prm);
1636 }
1637 }
1638
1639 }
1640
1641 private void ChildDelink(OdePrim odePrim)
1642 {
1643 // Okay, we have a delinked child.. need to rebuild the body.
1644 lock (childrenPrim)
1645 {
1646 foreach (OdePrim prm in childrenPrim)
1647 {
1648 prm.childPrim = true;
1649 prm.disableBody();
1650 //prm.m_taintparent = null;
1651 //prm._parent = null;
1652 //prm.m_taintPhysics = false;
1653 //prm.m_disabled = true;
1654 //prm.childPrim = false;
1655 }
1656 }
1657 disableBody();
1658
1659 lock (childrenPrim)
1660 {
1661 childrenPrim.Remove(odePrim);
1662 }
1663
1664 if (Body != IntPtr.Zero)
1665 {
1666 _parent_scene.remActivePrim(this);
1667 }
1668
1669 lock (childrenPrim)
1670 {
1671 foreach (OdePrim prm in childrenPrim)
1672 {
1673 ParentPrim(prm);
1674 }
1675 }
1676 }
1677
1678 private void changeSelectedStatus(float timestep)
1679 {
1680 if (m_taintselected)
1681 {
1682 m_collisionCategories = CollisionCategories.Selected;
1683 m_collisionFlags = (CollisionCategories.Sensor | CollisionCategories.Space);
1684
1685 // We do the body disable soft twice because 'in theory' a collision could have happened
1686 // in between the disabling and the collision properties setting
1687 // which would wake the physical body up from a soft disabling and potentially cause it to fall
1688 // through the ground.
1689
1690 // NOTE FOR JOINTS: this doesn't always work for jointed assemblies because if you select
1691 // just one part of the assembly, the rest of the assembly is non-selected and still simulating,
1692 // so that causes the selected part to wake up and continue moving.
1693
1694 // even if you select all parts of a jointed assembly, it is not guaranteed that the entire
1695 // assembly will stop simulating during the selection, because of the lack of atomicity
1696 // of select operations (their processing could be interrupted by a thread switch, causing
1697 // simulation to continue before all of the selected object notifications trickle down to
1698 // the physics engine).
1699
1700 // e.g. we select 100 prims that are connected by joints. non-atomically, the first 50 are
1701 // selected and disabled. then, due to a thread switch, the selection processing is
1702 // interrupted and the physics engine continues to simulate, so the last 50 items, whose
1703 // selection was not yet processed, continues to simulate. this wakes up ALL of the
1704 // first 50 again. then the last 50 are disabled. then the first 50, which were just woken
1705 // up, start simulating again, which in turn wakes up the last 50.
1706
1707 if (m_isphysical)
1708 {
1709 disableBodySoft();
1710 }
1711
1712 if (prim_geom != IntPtr.Zero)
1713 {
1714 d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories);
1715 d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
1716 }
1717
1718 if (m_isphysical)
1719 {
1720 disableBodySoft();
1721 }
1722 if (Body != IntPtr.Zero)
1723 {
1724 d.BodySetLinearVel(Body, 0f, 0f, 0f);
1725 d.BodySetForce(Body, 0f, 0f, 0f);
1726 d.BodySetAngularVel (Body, 0.0f, 0.0f, 0.0f);
1727 d.BodySetTorque (Body, 0.0f, 0.0f, 0.0f);
1728 }
1729
1730 }
1731 else
1732 {
1733 m_collisionCategories = CollisionCategories.Geom;
1734
1735 if (m_isphysical)
1736 m_collisionCategories |= CollisionCategories.Body;
1737
1738 m_collisionFlags = m_default_collisionFlags;
1739
1740 if (m_collidesLand)
1741 m_collisionFlags |= CollisionCategories.Land;
1742 if (m_collidesWater)
1743 m_collisionFlags |= CollisionCategories.Water;
1744
1745 if (prim_geom != IntPtr.Zero)
1746 {
1747 d.GeomSetCategoryBits(prim_geom, (int)m_collisionCategories);
1748 d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
1749 }
1750 if (Body != IntPtr.Zero)
1751 {
1752 d.BodySetLinearVel(Body, 0f, 0f, 0f);
1753 d.BodySetForce(Body, 0f, 0f, 0f);
1754 d.BodySetAngularVel (Body, 0.0f, 0.0f, 0.0f);
1755 d.BodySetTorque (Body, 0.0f, 0.0f, 0.0f);
1756 }
1757
1758 if (m_isphysical)
1759 {
1760 if (Body != IntPtr.Zero)
1761 {
1762 enableBodySoft();
1763 }
1764 }
1765 }
1766
1767 resetCollisionAccounting();
1768 m_isSelected = m_taintselected;
1769 }//end changeSelectedStatus
1770
1771 public void ResetTaints()
1772 {
1773 m_taintposition = _position;
1774 m_taintrot = _orientation;
1775 m_taintPhysics = m_isphysical;
1776 m_taintselected = m_isSelected;
1777 m_taintsize = _size;
1778 m_taintshape = false;
1779 m_taintforce = false;
1780 m_taintdisable = false;
1781 m_taintVelocity = Vector3.Zero;
1782 }
1783
1784 public void CreateGeom(IntPtr m_targetSpace, IMesh _mesh)
1785 {
1786 if (_mesh != null) // Special - make mesh
1787 {
1788 setMesh(_parent_scene, _mesh);
1789 }
1790 else // not a mesh
1791 {
1792 if (_pbs.ProfileShape == ProfileShape.HalfCircle && _pbs.PathCurve == (byte)Extrusion.Curve1) // special profile??
1793 {
1794 if (_size.X == _size.Y && _size.Y == _size.Z && _size.X == _size.Z) // Equi-size
1795 {
1796 if (((_size.X / 2f) > 0f)) // Has size
1797 {
1798 _parent_scene.waitForSpaceUnlock(m_targetSpace);
1799 try
1800 {
1801 SetGeom(d.CreateSphere(m_targetSpace, _size.X / 2));
1802 }
1803 catch (AccessViolationException)
1804 {
1805 m_log.Warn("[PHYSICS]: Unable to create physics proxy for object");
1806 ode.dunlock(_parent_scene.world);
1807 return;
1808 }
1809 }
1810 else
1811 {
1812 _parent_scene.waitForSpaceUnlock(m_targetSpace);
1813 try
1814 {
1815 SetGeom(d.CreateBox(m_targetSpace, _size.X, _size.Y, _size.Z));
1816 }
1817 catch (AccessViolationException)
1818 {
1819 m_log.Warn("[PHYSICS]: Unable to create physics proxy for object");
1820 ode.dunlock(_parent_scene.world);
1821 return;
1822 }
1823 }
1824 }
1825 else // not equi-size
1826 {
1827 _parent_scene.waitForSpaceUnlock(m_targetSpace);
1828 try
1829 {
1830 SetGeom(d.CreateBox(m_targetSpace, _size.X, _size.Y, _size.Z));
1831 }
1832 catch (AccessViolationException)
1833 {
1834 m_log.Warn("[PHYSICS]: Unable to create physics proxy for object");
1835 ode.dunlock(_parent_scene.world);
1836 return;
1837 }
1838 }
1839 }
1840
1841 else // not special profile
1842 {
1843 _parent_scene.waitForSpaceUnlock(m_targetSpace);
1844 try
1845 {
1846 SetGeom(d.CreateBox(m_targetSpace, _size.X, _size.Y, _size.Z));
1847 }
1848 catch (AccessViolationException)
1849 {
1850 m_log.Warn("[PHYSICS]: Unable to create physics proxy for object");
1851 ode.dunlock(_parent_scene.world);
1852 return;
1853 }
1854 }
1855 }
1856 }
1857
1858 public void changeadd(float timestep)
1859 {
1860 int[] iprimspaceArrItem = _parent_scene.calculateSpaceArrayItemFromPos(_position);
1861 IntPtr targetspace = _parent_scene.calculateSpaceForGeom(_position);
1862
1863 if (targetspace == IntPtr.Zero)
1864 targetspace = _parent_scene.createprimspace(iprimspaceArrItem[0], iprimspaceArrItem[1]);
1865
1866 m_targetSpace = targetspace;
1867
1868 if (_mesh == null && m_meshfailed == false)
1869 {
1870 if (_parent_scene.needsMeshing(_pbs))
1871 {
1872 // Don't need to re-enable body.. it's done in SetMesh
1873 try
1874 {
1875 _mesh = _parent_scene.mesher.CreateMesh(m_primName, _pbs, _size, _parent_scene.meshSculptLOD, IsPhysical);
1876 }
1877 catch
1878 {
1879 //Don't continuously try to mesh prims when meshing has failed
1880 m_meshfailed = true;
1881 }
1882 // createmesh returns null when it's a shape that isn't a cube.
1883 // m_log.Debug(m_localID);
1884 }
1885 }
1886
1887
1888 lock (_parent_scene.OdeLock)
1889 {
1890 CreateGeom(m_targetSpace, _mesh);
1891
1892 if (prim_geom != IntPtr.Zero)
1893 {
1894 d.GeomSetPosition(prim_geom, _position.X, _position.Y, _position.Z);
1895 d.Quaternion myrot = new d.Quaternion();
1896 myrot.X = _orientation.X;
1897 myrot.Y = _orientation.Y;
1898 myrot.Z = _orientation.Z;
1899 myrot.W = _orientation.W;
1900 d.GeomSetQuaternion(prim_geom, ref myrot);
1901 }
1902
1903 if (m_isphysical && Body == IntPtr.Zero)
1904 {
1905 enableBody();
1906 }
1907 }
1908
1909 changeSelectedStatus(timestep);
1910
1911 m_taintadd = false;
1912 }
1913
1914 public void changemove(float timestep)
1915 {
1916 if (m_isphysical)
1917 {
1918// if (!m_disabled && !m_taintremove && !childPrim) After one edit m_disabled is sometimes set, disabling further edits!
1919 if (!m_taintremove && !childPrim)
1920 {
1921 if (Body == IntPtr.Zero)
1922 enableBody();
1923 //Prim auto disable after 20 frames,
1924 //if you move it, re-enable the prim manually.
1925 if (_parent != null)
1926 {
1927 if (m_linkJoint != IntPtr.Zero)
1928 {
1929 d.JointDestroy(m_linkJoint);
1930 m_linkJoint = IntPtr.Zero;
1931 }
1932 }
1933 if (Body != IntPtr.Zero)
1934 {
1935 d.BodySetPosition(Body, _position.X, _position.Y, _position.Z);
1936
1937 if (_parent != null)
1938 {
1939 OdePrim odParent = (OdePrim)_parent;
1940 if (Body != (IntPtr)0 && odParent.Body != (IntPtr)0 && Body != odParent.Body)
1941 {
1942// KF: Fixed Joints were removed? Anyway - this Console.WriteLine does not show up, so routine is not used??
1943Console.WriteLine("ODEPrim JointCreateFixed !!!");
1944 m_linkJoint = d.JointCreateFixed(_parent_scene.world, _linkJointGroup);
1945 d.JointAttach(m_linkJoint, Body, odParent.Body);
1946 d.JointSetFixed(m_linkJoint);
1947 }
1948 }
1949 d.BodyEnable(Body);
1950 if (m_type != Vehicle.TYPE_NONE)
1951 {
1952 Enable(Body, _parent_scene);
1953 }
1954 }
1955 else
1956 {
1957 m_log.Warn("[PHYSICS]: Body Still null after enableBody(). This is a crash scenario.");
1958 }
1959 }
1960 //else
1961 // {
1962 //m_log.Debug("[BUG]: race!");
1963 //}
1964 }
1965 else
1966 {
1967 // string primScenAvatarIn = _parent_scene.whichspaceamIin(_position);
1968 // int[] arrayitem = _parent_scene.calculateSpaceArrayItemFromPos(_position);
1969 _parent_scene.waitForSpaceUnlock(m_targetSpace);
1970
1971 IntPtr tempspace = _parent_scene.recalculateSpaceForGeom(prim_geom, _position, m_targetSpace);
1972 m_targetSpace = tempspace;
1973
1974 _parent_scene.waitForSpaceUnlock(m_targetSpace);
1975 if (prim_geom != IntPtr.Zero)
1976 {
1977 d.GeomSetPosition(prim_geom, _position.X, _position.Y, _position.Z);
1978
1979 _parent_scene.waitForSpaceUnlock(m_targetSpace);
1980 d.SpaceAdd(m_targetSpace, prim_geom);
1981 }
1982 }
1983
1984 changeSelectedStatus(timestep);
1985
1986 resetCollisionAccounting();
1987 m_taintposition = _position;
1988 }
1989
1990
1991
1992 public void rotate(float timestep)
1993 {
1994 d.Quaternion myrot = new d.Quaternion();
1995 myrot.X = _orientation.X;
1996 myrot.Y = _orientation.Y;
1997 myrot.Z = _orientation.Z;
1998 myrot.W = _orientation.W;
1999 if (Body != IntPtr.Zero)
2000 {
2001 // KF: If this is a root prim do BodySet
2002 d.BodySetQuaternion(Body, ref myrot);
2003 }
2004 else
2005 {
2006 // daughter prim, do Geom set
2007 d.GeomSetQuaternion(prim_geom, ref myrot);
2008 }
2009
2010 resetCollisionAccounting();
2011 m_taintrot = _orientation;
2012 }
2013
2014 private void resetCollisionAccounting()
2015 {
2016 m_collisionscore = 0;
2017 m_interpenetrationcount = 0;
2018 m_disabled = false;
2019 }
2020
2021 public void changedisable(float timestep)
2022 {
2023 m_disabled = true;
2024 if (Body != IntPtr.Zero)
2025 {
2026 d.BodyDisable(Body);
2027 Body = IntPtr.Zero;
2028 }
2029
2030 m_taintdisable = false;
2031 }
2032
2033 public void changePhysicsStatus(float timestep)
2034 {
2035 if (m_isphysical == true)
2036 {
2037 if (Body == IntPtr.Zero)
2038 {
2039 if (_pbs.SculptEntry && _parent_scene.meshSculptedPrim)
2040 {
2041 changeshape(2f);
2042 }
2043 else
2044 {
2045 enableBody();
2046 }
2047 }
2048 }
2049 else
2050 {
2051 if (Body != IntPtr.Zero)
2052 {
2053 if (_pbs.SculptEntry && _parent_scene.meshSculptedPrim)
2054 {
2055 _mesh = null;
2056 changeadd(2f);
2057 }
2058 if (childPrim)
2059 {
2060 if (_parent != null)
2061 {
2062 OdePrim parent = (OdePrim)_parent;
2063 parent.ChildDelink(this);
2064 }
2065 }
2066 else
2067 {
2068 disableBody();
2069 }
2070 }
2071 }
2072
2073 changeSelectedStatus(timestep);
2074
2075 resetCollisionAccounting();
2076 m_taintPhysics = m_isphysical;
2077 }
2078
2079 public void changesize(float timestamp)
2080 {
2081
2082 string oldname = _parent_scene.geom_name_map[prim_geom];
2083
2084 if (_size.X <= 0) _size.X = 0.01f;
2085 if (_size.Y <= 0) _size.Y = 0.01f;
2086 if (_size.Z <= 0) _size.Z = 0.01f;
2087
2088 // Cleanup of old prim geometry
2089 if (_mesh != null)
2090 {
2091 // Cleanup meshing here
2092 }
2093 //kill body to rebuild
2094 if (IsPhysical && Body != IntPtr.Zero)
2095 {
2096 if (childPrim)
2097 {
2098 if (_parent != null)
2099 {
2100 OdePrim parent = (OdePrim)_parent;
2101 parent.ChildDelink(this);
2102 }
2103 }
2104 else
2105 {
2106 disableBody();
2107 }
2108 }
2109 if (d.SpaceQuery(m_targetSpace, prim_geom))
2110 {
2111 _parent_scene.waitForSpaceUnlock(m_targetSpace);
2112 d.SpaceRemove(m_targetSpace, prim_geom);
2113 }
2114 // we don't need to do space calculation because the client sends a position update also.
2115
2116 // Construction of new prim
2117 if (_parent_scene.needsMeshing(_pbs) && m_meshfailed == false)
2118 {
2119 float meshlod = _parent_scene.meshSculptLOD;
2120
2121 if (IsPhysical)
2122 meshlod = _parent_scene.MeshSculptphysicalLOD;
2123 // Don't need to re-enable body.. it's done in SetMesh
2124
2125 IMesh mesh = null;
2126
2127 try
2128 {
2129 if (_parent_scene.needsMeshing(_pbs))
2130 mesh = _parent_scene.mesher.CreateMesh(oldname, _pbs, _size, meshlod, IsPhysical);
2131 }
2132 catch
2133 {
2134 m_meshfailed = true;
2135 }
2136
2137 //IMesh mesh = _parent_scene.mesher.CreateMesh(oldname, _pbs, _size, meshlod, IsPhysical);
2138 CreateGeom(m_targetSpace, mesh);
2139
2140
2141 }
2142 else
2143 {
2144 _mesh = null;
2145 CreateGeom(m_targetSpace, _mesh);
2146 }
2147
2148 d.GeomSetPosition(prim_geom, _position.X, _position.Y, _position.Z);
2149 d.Quaternion myrot = new d.Quaternion();
2150 myrot.X = _orientation.X;
2151 myrot.Y = _orientation.Y;
2152 myrot.Z = _orientation.Z;
2153 myrot.W = _orientation.W;
2154 d.GeomSetQuaternion(prim_geom, ref myrot);
2155
2156 //d.GeomBoxSetLengths(prim_geom, _size.X, _size.Y, _size.Z);
2157 if (IsPhysical && Body == IntPtr.Zero && !childPrim)
2158 {
2159 // Re creates body on size.
2160 // EnableBody also does setMass()
2161 enableBody();
2162 d.BodyEnable(Body);
2163 }
2164
2165 _parent_scene.geom_name_map[prim_geom] = oldname;
2166
2167 changeSelectedStatus(timestamp);
2168 if (childPrim)
2169 {
2170 if (_parent is OdePrim)
2171 {
2172 OdePrim parent = (OdePrim)_parent;
2173 parent.ChildSetGeom(this);
2174 }
2175 }
2176 resetCollisionAccounting();
2177 m_taintsize = _size;
2178 }
2179
2180
2181
2182 public void changefloatonwater(float timestep)
2183 {
2184 m_collidesWater = m_taintCollidesWater;
2185
2186 if (prim_geom != IntPtr.Zero)
2187 {
2188 if (m_collidesWater)
2189 {
2190 m_collisionFlags |= CollisionCategories.Water;
2191 }
2192 else
2193 {
2194 m_collisionFlags &= ~CollisionCategories.Water;
2195 }
2196 d.GeomSetCollideBits(prim_geom, (int)m_collisionFlags);
2197 }
2198 }
2199
2200 public void changeshape(float timestamp)
2201 {
2202 string oldname = _parent_scene.geom_name_map[prim_geom];
2203
2204 // Cleanup of old prim geometry and Bodies
2205 if (IsPhysical && Body != IntPtr.Zero)
2206 {
2207 if (childPrim)
2208 {
2209 if (_parent != null)
2210 {
2211 OdePrim parent = (OdePrim)_parent;
2212 parent.ChildDelink(this);
2213 }
2214 }
2215 else
2216 {
2217 disableBody();
2218 }
2219 }
2220
2221
2222 // we don't need to do space calculation because the client sends a position update also.
2223 if (_size.X <= 0) _size.X = 0.01f;
2224 if (_size.Y <= 0) _size.Y = 0.01f;
2225 if (_size.Z <= 0) _size.Z = 0.01f;
2226 // Construction of new prim
2227
2228 if (_parent_scene.needsMeshing(_pbs) && m_meshfailed == false)
2229 {
2230 // Don't need to re-enable body.. it's done in SetMesh
2231 float meshlod = _parent_scene.meshSculptLOD;
2232
2233 if (IsPhysical)
2234 meshlod = _parent_scene.MeshSculptphysicalLOD;
2235 try
2236 {
2237 IMesh mesh = _parent_scene.mesher.CreateMesh(oldname, _pbs, _size, meshlod, IsPhysical);
2238 CreateGeom(m_targetSpace, mesh);
2239 }
2240 catch
2241 {
2242 m_meshfailed = true;
2243 }
2244 // createmesh returns null when it doesn't mesh.
2245 }
2246 else
2247 {
2248 _mesh = null;
2249 CreateGeom(m_targetSpace, null);
2250 }
2251
2252 d.GeomSetPosition(prim_geom, _position.X, _position.Y, _position.Z);
2253 d.Quaternion myrot = new d.Quaternion();
2254 //myrot.W = _orientation.w;
2255 myrot.W = _orientation.W;
2256 myrot.X = _orientation.X;
2257 myrot.Y = _orientation.Y;
2258 myrot.Z = _orientation.Z;
2259 d.GeomSetQuaternion(prim_geom, ref myrot);
2260
2261 //d.GeomBoxSetLengths(prim_geom, _size.X, _size.Y, _size.Z);
2262 if (IsPhysical && Body == IntPtr.Zero)
2263 {
2264 // Re creates body on size.
2265 // EnableBody also does setMass()
2266 enableBody();
2267 if (Body != IntPtr.Zero)
2268 {
2269 d.BodyEnable(Body);
2270 }
2271 }
2272 _parent_scene.geom_name_map[prim_geom] = oldname;
2273
2274 changeSelectedStatus(timestamp);
2275 if (childPrim)
2276 {
2277 if (_parent is OdePrim)
2278 {
2279 OdePrim parent = (OdePrim)_parent;
2280 parent.ChildSetGeom(this);
2281 }
2282 }
2283 resetCollisionAccounting();
2284 m_taintshape = false;
2285 }
2286
2287 public void changeAddForce(float timestamp)
2288 {
2289 if (!m_isSelected)
2290 {
2291 lock (m_forcelist)
2292 {
2293 //m_log.Info("[PHYSICS]: dequeing forcelist");
2294 if (IsPhysical)
2295 {
2296 Vector3 iforce = Vector3.Zero;
2297 int i = 0;
2298 try
2299 {
2300 for (i = 0; i < m_forcelist.Count; i++)
2301 {
2302
2303 iforce = iforce + (m_forcelist[i] * 100);
2304 }
2305 }
2306 catch (IndexOutOfRangeException)
2307 {
2308 m_forcelist = new List<Vector3>();
2309 m_collisionscore = 0;
2310 m_interpenetrationcount = 0;
2311 m_taintforce = false;
2312 return;
2313 }
2314 catch (ArgumentOutOfRangeException)
2315 {
2316 m_forcelist = new List<Vector3>();
2317 m_collisionscore = 0;
2318 m_interpenetrationcount = 0;
2319 m_taintforce = false;
2320 return;
2321 }
2322 d.BodyEnable(Body);
2323
2324 d.BodyAddForce(Body, iforce.X, iforce.Y, iforce.Z);
2325 }
2326 m_forcelist.Clear();
2327 }
2328
2329 m_collisionscore = 0;
2330 m_interpenetrationcount = 0;
2331 }
2332
2333 m_taintforce = false;
2334
2335 }
2336
2337
2338
2339 public void changeSetTorque(float timestamp)
2340 {
2341 if (!m_isSelected)
2342 {
2343 if (IsPhysical && Body != IntPtr.Zero)
2344 {
2345 d.BodySetTorque(Body, m_taintTorque.X, m_taintTorque.Y, m_taintTorque.Z);
2346 }
2347 }
2348
2349 m_taintTorque = Vector3.Zero;
2350 }
2351
2352 public void changeAddAngularForce(float timestamp)
2353 {
2354 if (!m_isSelected)
2355 {
2356 lock (m_angularforcelist)
2357 {
2358 //m_log.Info("[PHYSICS]: dequeing forcelist");
2359 if (IsPhysical)
2360 {
2361 Vector3 iforce = Vector3.Zero;
2362 for (int i = 0; i < m_angularforcelist.Count; i++)
2363 {
2364 iforce = iforce + (m_angularforcelist[i] * 100);
2365 }
2366 d.BodyEnable(Body);
2367 d.BodyAddTorque(Body, iforce.X, iforce.Y, iforce.Z);
2368
2369 }
2370 m_angularforcelist.Clear();
2371 }
2372
2373 m_collisionscore = 0;
2374 m_interpenetrationcount = 0;
2375 }
2376
2377 m_taintaddangularforce = false;
2378 }
2379
2380 private void changevelocity(float timestep)
2381 {
2382 if (!m_isSelected)
2383 {
2384 Thread.Sleep(20);
2385 if (IsPhysical)
2386 {
2387 if (Body != IntPtr.Zero)
2388 d.BodySetLinearVel(Body, m_taintVelocity.X, m_taintVelocity.Y, m_taintVelocity.Z);
2389 }
2390
2391 //resetCollisionAccounting();
2392 }
2393 m_taintVelocity = Vector3.Zero;
2394 }
2395
2396 public void UpdatePositionAndVelocity()
2397 {
2398 return; // moved to the Move () method
2399 }
2400
2401 public d.Mass FromMatrix4(Matrix4 pMat, ref d.Mass obj)
2402 {
2403 obj.I.M00 = pMat[0, 0];
2404 obj.I.M01 = pMat[0, 1];
2405 obj.I.M02 = pMat[0, 2];
2406 obj.I.M10 = pMat[1, 0];
2407 obj.I.M11 = pMat[1, 1];
2408 obj.I.M12 = pMat[1, 2];
2409 obj.I.M20 = pMat[2, 0];
2410 obj.I.M21 = pMat[2, 1];
2411 obj.I.M22 = pMat[2, 2];
2412 return obj;
2413 }
2414
2415 public override void SubscribeEvents(int ms)
2416 {
2417 m_eventsubscription = ms;
2418 _parent_scene.addCollisionEventReporting(this);
2419 }
2420
2421 public override void UnSubscribeEvents()
2422 {
2423 _parent_scene.remCollisionEventReporting(this);
2424 m_eventsubscription = 0;
2425 }
2426
2427 public void AddCollisionEvent(uint CollidedWith, ContactPoint contact)
2428 {
2429 if (CollisionEventsThisFrame == null)
2430 CollisionEventsThisFrame = new CollisionEventUpdate();
2431 CollisionEventsThisFrame.addCollider(CollidedWith, contact);
2432 }
2433
2434 public void SendCollisions()
2435 {
2436 if (CollisionEventsThisFrame == null)
2437 return;
2438
2439 base.SendCollisionUpdate(CollisionEventsThisFrame);
2440
2441 if (CollisionEventsThisFrame.m_objCollisionList.Count == 0)
2442 CollisionEventsThisFrame = null;
2443 else
2444 CollisionEventsThisFrame = new CollisionEventUpdate();
2445 }
2446
2447 public override bool SubscribedEvents()
2448 {
2449 if (m_eventsubscription > 0)
2450 return true;
2451 return false;
2452 }
2453
2454 public static Matrix4 Inverse(Matrix4 pMat)
2455 {
2456 if (determinant3x3(pMat) == 0)
2457 {
2458 return Matrix4.Identity; // should probably throw an error. singluar matrix inverse not possible
2459 }
2460
2461
2462
2463 return (Adjoint(pMat) / determinant3x3(pMat));
2464 }
2465
2466 public static Matrix4 Adjoint(Matrix4 pMat)
2467 {
2468 Matrix4 adjointMatrix = new Matrix4();
2469 for (int i=0; i<4; i++)
2470 {
2471 for (int j=0; j<4; j++)
2472 {
2473 Matrix4SetValue(ref adjointMatrix, i, j, (float)(Math.Pow(-1, i + j) * (determinant3x3(Minor(pMat, i, j)))));
2474 }
2475 }
2476
2477 adjointMatrix = Transpose(adjointMatrix);
2478 return adjointMatrix;
2479 }
2480
2481 public static Matrix4 Minor(Matrix4 matrix, int iRow, int iCol)
2482 {
2483 Matrix4 minor = new Matrix4();
2484 int m = 0, n = 0;
2485 for (int i = 0; i < 4; i++)
2486 {
2487 if (i == iRow)
2488 continue;
2489 n = 0;
2490 for (int j = 0; j < 4; j++)
2491 {
2492 if (j == iCol)
2493 continue;
2494 Matrix4SetValue(ref minor, m,n, matrix[i, j]);
2495 n++;
2496 }
2497 m++;
2498 }
2499 return minor;
2500 }
2501
2502 public static Matrix4 Transpose(Matrix4 pMat)
2503 {
2504 Matrix4 transposeMatrix = new Matrix4();
2505 for (int i = 0; i < 4; i++)
2506 for (int j = 0; j < 4; j++)
2507 Matrix4SetValue(ref transposeMatrix, i, j, pMat[j, i]);
2508 return transposeMatrix;
2509 }
2510
2511 public static void Matrix4SetValue(ref Matrix4 pMat, int r, int c, float val)
2512 {
2513 switch (r)
2514 {
2515 case 0:
2516 switch (c)
2517 {
2518 case 0:
2519 pMat.M11 = val;
2520 break;
2521 case 1:
2522 pMat.M12 = val;
2523 break;
2524 case 2:
2525 pMat.M13 = val;
2526 break;
2527 case 3:
2528 pMat.M14 = val;
2529 break;
2530 }
2531
2532 break;
2533 case 1:
2534 switch (c)
2535 {
2536 case 0:
2537 pMat.M21 = val;
2538 break;
2539 case 1:
2540 pMat.M22 = val;
2541 break;
2542 case 2:
2543 pMat.M23 = val;
2544 break;
2545 case 3:
2546 pMat.M24 = val;
2547 break;
2548 }
2549
2550 break;
2551 case 2:
2552 switch (c)
2553 {
2554 case 0:
2555 pMat.M31 = val;
2556 break;
2557 case 1:
2558 pMat.M32 = val;
2559 break;
2560 case 2:
2561 pMat.M33 = val;
2562 break;
2563 case 3:
2564 pMat.M34 = val;
2565 break;
2566 }
2567
2568 break;
2569 case 3:
2570 switch (c)
2571 {
2572 case 0:
2573 pMat.M41 = val;
2574 break;
2575 case 1:
2576 pMat.M42 = val;
2577 break;
2578 case 2:
2579 pMat.M43 = val;
2580 break;
2581 case 3:
2582 pMat.M44 = val;
2583 break;
2584 }
2585
2586 break;
2587 }
2588 }
2589 private static float determinant3x3(Matrix4 pMat)
2590 {
2591 float det = 0;
2592 float diag1 = pMat[0, 0]*pMat[1, 1]*pMat[2, 2];
2593 float diag2 = pMat[0, 1]*pMat[2, 1]*pMat[2, 0];
2594 float diag3 = pMat[0, 2]*pMat[1, 0]*pMat[2, 1];
2595 float diag4 = pMat[2, 0]*pMat[1, 1]*pMat[0, 2];
2596 float diag5 = pMat[2, 1]*pMat[1, 2]*pMat[0, 0];
2597 float diag6 = pMat[2, 2]*pMat[1, 0]*pMat[0, 1];
2598
2599 det = diag1 + diag2 + diag3 - (diag4 + diag5 + diag6);
2600 return det;
2601
2602 }
2603
2604 private static void DMassCopy(ref d.Mass src, ref d.Mass dst)
2605 {
2606 dst.c.W = src.c.W;
2607 dst.c.X = src.c.X;
2608 dst.c.Y = src.c.Y;
2609 dst.c.Z = src.c.Z;
2610 dst.mass = src.mass;
2611 dst.I.M00 = src.I.M00;
2612 dst.I.M01 = src.I.M01;
2613 dst.I.M02 = src.I.M02;
2614 dst.I.M10 = src.I.M10;
2615 dst.I.M11 = src.I.M11;
2616 dst.I.M12 = src.I.M12;
2617 dst.I.M20 = src.I.M20;
2618 dst.I.M21 = src.I.M21;
2619 dst.I.M22 = src.I.M22;
2620 }
2621
2622 public override void SetMaterial(int pMaterial)
2623 {
2624 m_material = pMaterial;
2625 }
2626
2627 internal void ProcessFloatVehicleParam(Vehicle pParam, float pValue)
2628 {
2629 switch (pParam)
2630 {
2631 case Vehicle.ANGULAR_DEFLECTION_EFFICIENCY:
2632 if (pValue < 0.01f) pValue = 0.01f;
2633 // m_angularDeflectionEfficiency = pValue;
2634 break;
2635 case Vehicle.ANGULAR_DEFLECTION_TIMESCALE:
2636 if (pValue < 0.1f) pValue = 0.1f;
2637 // m_angularDeflectionTimescale = pValue;
2638 break;
2639 case Vehicle.ANGULAR_MOTOR_DECAY_TIMESCALE:
2640 if (pValue < 0.3f) pValue = 0.3f;
2641 m_angularMotorDecayTimescale = pValue;
2642 break;
2643 case Vehicle.ANGULAR_MOTOR_TIMESCALE:
2644 if (pValue < 0.3f) pValue = 0.3f;
2645 m_angularMotorTimescale = pValue;
2646 break;
2647 case Vehicle.BANKING_EFFICIENCY:
2648 if (pValue < 0.01f) pValue = 0.01f;
2649 // m_bankingEfficiency = pValue;
2650 break;
2651 case Vehicle.BANKING_MIX:
2652 if (pValue < 0.01f) pValue = 0.01f;
2653 // m_bankingMix = pValue;
2654 break;
2655 case Vehicle.BANKING_TIMESCALE:
2656 if (pValue < 0.01f) pValue = 0.01f;
2657 // m_bankingTimescale = pValue;
2658 break;
2659 case Vehicle.BUOYANCY:
2660 if (pValue < -1f) pValue = -1f;
2661 if (pValue > 1f) pValue = 1f;
2662 m_VehicleBuoyancy = pValue;
2663 break;
2664// case Vehicle.HOVER_EFFICIENCY:
2665// if (pValue < 0f) pValue = 0f;
2666// if (pValue > 1f) pValue = 1f;
2667// m_VhoverEfficiency = pValue;
2668// break;
2669 case Vehicle.HOVER_HEIGHT:
2670 m_VhoverHeight = pValue;
2671 break;
2672 case Vehicle.HOVER_TIMESCALE:
2673 if (pValue < 0.1f) pValue = 0.1f;
2674 m_VhoverTimescale = pValue;
2675 break;
2676 case Vehicle.LINEAR_DEFLECTION_EFFICIENCY:
2677 if (pValue < 0.01f) pValue = 0.01f;
2678 // m_linearDeflectionEfficiency = pValue;
2679 break;
2680 case Vehicle.LINEAR_DEFLECTION_TIMESCALE:
2681 if (pValue < 0.01f) pValue = 0.01f;
2682 // m_linearDeflectionTimescale = pValue;
2683 break;
2684 case Vehicle.LINEAR_MOTOR_DECAY_TIMESCALE:
2685 if (pValue < 0.3f) pValue = 0.3f;
2686 m_linearMotorDecayTimescale = pValue;
2687 break;
2688 case Vehicle.LINEAR_MOTOR_TIMESCALE:
2689 if (pValue < 0.1f) pValue = 0.1f;
2690 m_linearMotorTimescale = pValue;
2691 break;
2692 case Vehicle.VERTICAL_ATTRACTION_EFFICIENCY:
2693 if (pValue < 0.1f) pValue = 0.1f; // Less goes unstable
2694 if (pValue > 1.0f) pValue = 1.0f;
2695 m_verticalAttractionEfficiency = pValue;
2696 break;
2697 case Vehicle.VERTICAL_ATTRACTION_TIMESCALE:
2698 if (pValue < 0.1f) pValue = 0.1f;
2699 m_verticalAttractionTimescale = pValue;
2700 break;
2701
2702 // These are vector properties but the engine lets you use a single float value to
2703 // set all of the components to the same value
2704 case Vehicle.ANGULAR_FRICTION_TIMESCALE:
2705 if (pValue > 30f) pValue = 30f;
2706 if (pValue < 0.1f) pValue = 0.1f;
2707 m_angularFrictionTimescale = new Vector3(pValue, pValue, pValue);
2708 break;
2709 case Vehicle.ANGULAR_MOTOR_DIRECTION:
2710 m_angularMotorDirection = new Vector3(pValue, pValue, pValue);
2711 UpdateAngDecay();
2712 break;
2713 case Vehicle.LINEAR_FRICTION_TIMESCALE:
2714 if (pValue < 0.1f) pValue = 0.1f;
2715 m_linearFrictionTimescale = new Vector3(pValue, pValue, pValue);
2716 break;
2717 case Vehicle.LINEAR_MOTOR_DIRECTION:
2718 m_linearMotorDirection = new Vector3(pValue, pValue, pValue);
2719 UpdateLinDecay();
2720 break;
2721 case Vehicle.LINEAR_MOTOR_OFFSET:
2722 // m_linearMotorOffset = new Vector3(pValue, pValue, pValue);
2723 break;
2724
2725 }
2726
2727 }//end ProcessFloatVehicleParam
2728
2729 internal void ProcessVectorVehicleParam(Vehicle pParam, Vector3 pValue)
2730 {
2731 switch (pParam)
2732 {
2733 case Vehicle.ANGULAR_FRICTION_TIMESCALE:
2734 if (pValue.X > 30f) pValue.X = 30f;
2735 if (pValue.X < 0.1f) pValue.X = 0.1f;
2736 if (pValue.Y > 30f) pValue.Y = 30f;
2737 if (pValue.Y < 0.1f) pValue.Y = 0.1f;
2738 if (pValue.Z > 30f) pValue.Z = 30f;
2739 if (pValue.Z < 0.1f) pValue.Z = 0.1f;
2740 m_angularFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z);
2741 break;
2742 case Vehicle.ANGULAR_MOTOR_DIRECTION:
2743 m_angularMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z);
2744 // Limit requested angular speed to 2 rps= 4 pi rads/sec
2745 if(m_angularMotorDirection.X > 12.56f) m_angularMotorDirection.X = 12.56f;
2746 if(m_angularMotorDirection.X < - 12.56f) m_angularMotorDirection.X = - 12.56f;
2747 if(m_angularMotorDirection.Y > 12.56f) m_angularMotorDirection.Y = 12.56f;
2748 if(m_angularMotorDirection.Y < - 12.56f) m_angularMotorDirection.Y = - 12.56f;
2749 if(m_angularMotorDirection.Z > 12.56f) m_angularMotorDirection.Z = 12.56f;
2750 if(m_angularMotorDirection.Z < - 12.56f) m_angularMotorDirection.Z = - 12.56f;
2751 UpdateAngDecay();
2752 break;
2753 case Vehicle.LINEAR_FRICTION_TIMESCALE:
2754 if (pValue.X < 0.1f) pValue.X = 0.1f;
2755 if (pValue.Y < 0.1f) pValue.Y = 0.1f;
2756 if (pValue.Z < 0.1f) pValue.Z = 0.1f;
2757 m_linearFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z);
2758 break;
2759 case Vehicle.LINEAR_MOTOR_DIRECTION:
2760 m_linearMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z); // velocity requested by LSL, for max limiting
2761 UpdateLinDecay();
2762 break;
2763 case Vehicle.LINEAR_MOTOR_OFFSET:
2764 // m_linearMotorOffset = new Vector3(pValue.X, pValue.Y, pValue.Z);
2765 break;
2766 }
2767
2768 }//end ProcessVectorVehicleParam
2769
2770 internal void ProcessRotationVehicleParam(Vehicle pParam, Quaternion pValue)
2771 {
2772 switch (pParam)
2773 {
2774 case Vehicle.REFERENCE_FRAME:
2775 // m_referenceFrame = pValue;
2776 break;
2777 }
2778
2779 }//end ProcessRotationVehicleParam
2780
2781 internal void ProcessVehicleFlags(int pParam, bool remove)
2782 {
2783 if (remove)
2784 {
2785 m_flags &= ~((VehicleFlag)pParam);
2786 }
2787 else
2788 {
2789 m_flags |= (VehicleFlag)pParam;
2790 }
2791 }
2792
2793 internal void ProcessTypeChange(Vehicle pType)
2794 {
2795 // Set Defaults For Type
2796 m_type = pType;
2797 switch (pType)
2798 {
2799 case Vehicle.TYPE_SLED:
2800 m_linearFrictionTimescale = new Vector3(30, 1, 1000);
2801 m_angularFrictionTimescale = new Vector3(30, 30, 30);
2802// m_lLinMotorVel = Vector3.Zero;
2803 m_linearMotorTimescale = 1000;
2804 m_linearMotorDecayTimescale = 120;
2805 m_angularMotorDirection = Vector3.Zero;
2806 m_angularMotorDVel = Vector3.Zero;
2807 m_angularMotorTimescale = 1000;
2808 m_angularMotorDecayTimescale = 120;
2809 m_VhoverHeight = 0;
2810// m_VhoverEfficiency = 1;
2811 m_VhoverTimescale = 10;
2812 m_VehicleBuoyancy = 0;
2813 // m_linearDeflectionEfficiency = 1;
2814 // m_linearDeflectionTimescale = 1;
2815 // m_angularDeflectionEfficiency = 1;
2816 // m_angularDeflectionTimescale = 1000;
2817 // m_bankingEfficiency = 0;
2818 // m_bankingMix = 1;
2819 // m_bankingTimescale = 10;
2820 // m_referenceFrame = Quaternion.Identity;
2821 m_flags &=
2822 ~(VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY |
2823 VehicleFlag.HOVER_GLOBAL_HEIGHT | VehicleFlag.HOVER_UP_ONLY);
2824 m_flags |= (VehicleFlag.NO_DEFLECTION_UP | VehicleFlag.LIMIT_ROLL_ONLY | VehicleFlag.LIMIT_MOTOR_UP);
2825 break;
2826 case Vehicle.TYPE_CAR:
2827 m_linearFrictionTimescale = new Vector3(100, 2, 1000);
2828 m_angularFrictionTimescale = new Vector3(30, 30, 30); // was 1000, but sl max frict time is 30.
2829// m_lLinMotorVel = Vector3.Zero;
2830 m_linearMotorTimescale = 1;
2831 m_linearMotorDecayTimescale = 60;
2832 m_angularMotorDirection = Vector3.Zero;
2833 m_angularMotorDVel = Vector3.Zero;
2834 m_angularMotorTimescale = 1;
2835 m_angularMotorDecayTimescale = 0.8f;
2836 m_VhoverHeight = 0;
2837// m_VhoverEfficiency = 0;
2838 m_VhoverTimescale = 1000;
2839 m_VehicleBuoyancy = 0;
2840 // // m_linearDeflectionEfficiency = 1;
2841 // // m_linearDeflectionTimescale = 2;
2842 // // m_angularDeflectionEfficiency = 0;
2843 // m_angularDeflectionTimescale = 10;
2844 m_verticalAttractionEfficiency = 1f;
2845 m_verticalAttractionTimescale = 10f;
2846 // m_bankingEfficiency = -0.2f;
2847 // m_bankingMix = 1;
2848 // m_bankingTimescale = 1;
2849 // m_referenceFrame = Quaternion.Identity;
2850 m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.HOVER_GLOBAL_HEIGHT);
2851 m_flags |= (VehicleFlag.NO_DEFLECTION_UP | VehicleFlag.LIMIT_ROLL_ONLY | VehicleFlag.HOVER_UP_ONLY |
2852 VehicleFlag.LIMIT_MOTOR_UP);
2853 break;
2854 case Vehicle.TYPE_BOAT:
2855 m_linearFrictionTimescale = new Vector3(10, 3, 2);
2856 m_angularFrictionTimescale = new Vector3(10,10,10);
2857// m_lLinMotorVel = Vector3.Zero;
2858 m_linearMotorTimescale = 5;
2859 m_linearMotorDecayTimescale = 60;
2860 m_angularMotorDirection = Vector3.Zero;
2861 m_angularMotorDVel = Vector3.Zero;
2862 m_angularMotorTimescale = 4;
2863 m_angularMotorDecayTimescale = 4;
2864 m_VhoverHeight = 0;
2865// m_VhoverEfficiency = 0.5f;
2866 m_VhoverTimescale = 2;
2867 m_VehicleBuoyancy = 1;
2868 // m_linearDeflectionEfficiency = 0.5f;
2869 // m_linearDeflectionTimescale = 3;
2870 // m_angularDeflectionEfficiency = 0.5f;
2871 // m_angularDeflectionTimescale = 5;
2872 m_verticalAttractionEfficiency = 0.5f;
2873 m_verticalAttractionTimescale = 5f;
2874 // m_bankingEfficiency = -0.3f;
2875 // m_bankingMix = 0.8f;
2876 // m_bankingTimescale = 1;
2877 // m_referenceFrame = Quaternion.Identity;
2878 m_flags &= ~(VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.LIMIT_ROLL_ONLY |
2879 VehicleFlag.HOVER_GLOBAL_HEIGHT | VehicleFlag.HOVER_UP_ONLY);
2880 m_flags |= (VehicleFlag.NO_DEFLECTION_UP | VehicleFlag.HOVER_WATER_ONLY |
2881 VehicleFlag.LIMIT_MOTOR_UP);
2882 break;
2883 case Vehicle.TYPE_AIRPLANE:
2884 m_linearFrictionTimescale = new Vector3(200, 10, 5);
2885 m_angularFrictionTimescale = new Vector3(20, 20, 20);
2886// m_lLinMotorVel = Vector3.Zero;
2887 m_linearMotorTimescale = 2;
2888 m_linearMotorDecayTimescale = 60;
2889 m_angularMotorDirection = Vector3.Zero;
2890 m_angularMotorDVel = Vector3.Zero;
2891 m_angularMotorTimescale = 4;
2892 m_angularMotorDecayTimescale = 4;
2893 m_VhoverHeight = 0;
2894// m_VhoverEfficiency = 0.5f;
2895 m_VhoverTimescale = 1000;
2896 m_VehicleBuoyancy = 0;
2897 // m_linearDeflectionEfficiency = 0.5f;
2898 // m_linearDeflectionTimescale = 3;
2899 // m_angularDeflectionEfficiency = 1;
2900 // m_angularDeflectionTimescale = 2;
2901 m_verticalAttractionEfficiency = 0.9f;
2902 m_verticalAttractionTimescale = 2f;
2903 // m_bankingEfficiency = 1;
2904 // m_bankingMix = 0.7f;
2905 // m_bankingTimescale = 2;
2906 // m_referenceFrame = Quaternion.Identity;
2907 m_flags &= ~(VehicleFlag.NO_DEFLECTION_UP | VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY |
2908 VehicleFlag.HOVER_GLOBAL_HEIGHT | VehicleFlag.HOVER_UP_ONLY | VehicleFlag.LIMIT_MOTOR_UP);
2909 m_flags |= (VehicleFlag.LIMIT_ROLL_ONLY);
2910 break;
2911 case Vehicle.TYPE_BALLOON:
2912 m_linearFrictionTimescale = new Vector3(5, 5, 5);
2913 m_angularFrictionTimescale = new Vector3(10, 10, 10);
2914 m_linearMotorTimescale = 5;
2915 m_linearMotorDecayTimescale = 60;
2916 m_angularMotorDirection = Vector3.Zero;
2917 m_angularMotorDVel = Vector3.Zero;
2918 m_angularMotorTimescale = 6;
2919 m_angularMotorDecayTimescale = 10;
2920 m_VhoverHeight = 5;
2921// m_VhoverEfficiency = 0.8f;
2922 m_VhoverTimescale = 10;
2923 m_VehicleBuoyancy = 1;
2924 // m_linearDeflectionEfficiency = 0;
2925 // m_linearDeflectionTimescale = 5;
2926 // m_angularDeflectionEfficiency = 0;
2927 // m_angularDeflectionTimescale = 5;
2928 m_verticalAttractionEfficiency = 1f;
2929 m_verticalAttractionTimescale = 100f;
2930 // m_bankingEfficiency = 0;
2931 // m_bankingMix = 0.7f;
2932 // m_bankingTimescale = 5;
2933 // m_referenceFrame = Quaternion.Identity;
2934 m_flags &= ~(VehicleFlag.NO_DEFLECTION_UP | VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY |
2935 VehicleFlag.HOVER_UP_ONLY | VehicleFlag.LIMIT_MOTOR_UP);
2936 m_flags |= (VehicleFlag.LIMIT_ROLL_ONLY | VehicleFlag.HOVER_GLOBAL_HEIGHT);
2937 break;
2938
2939 }
2940 }//end SetDefaultsForType
2941
2942 internal void Enable(IntPtr pBody, OdeScene pParentScene)
2943 {
2944 if (m_type == Vehicle.TYPE_NONE)
2945 return;
2946
2947 m_body = pBody;
2948 }
2949
2950
2951 internal void Halt()
2952 { // Kill all motions, when non-physical
2953 // m_linearMotorDirection = Vector3.Zero;
2954 m_lLinMotorDVel = Vector3.Zero;
2955 m_lLinObjectVel = Vector3.Zero;
2956 m_wLinObjectVel = Vector3.Zero;
2957 m_angularMotorDirection = Vector3.Zero;
2958 m_lastAngularVelocity = Vector3.Zero;
2959 m_angularMotorDVel = Vector3.Zero;
2960 _acceleration = Vector3.Zero;
2961 }
2962
2963 private void UpdateLinDecay()
2964 {
2965// if (Math.Abs(m_linearMotorDirection.X) > Math.Abs(m_lLinMotorDVel.X)) m_lLinMotorDVel.X = m_linearMotorDirection.X;
2966// if (Math.Abs(m_linearMotorDirection.Y) > Math.Abs(m_lLinMotorDVel.Y)) m_lLinMotorDVel.Y = m_linearMotorDirection.Y;
2967// if (Math.Abs(m_linearMotorDirection.Z) > Math.Abs(m_lLinMotorDVel.Z)) m_lLinMotorDVel.Z = m_linearMotorDirection.Z;
2968 m_lLinMotorDVel.X = m_linearMotorDirection.X;
2969 m_lLinMotorDVel.Y = m_linearMotorDirection.Y;
2970 m_lLinMotorDVel.Z = m_linearMotorDirection.Z;
2971 } // else let the motor decay on its own
2972
2973 private void UpdateAngDecay()
2974 {
2975// if (Math.Abs(m_angularMotorDirection.X) > Math.Abs(m_angularMotorDVel.X)) m_angularMotorDVel.X = m_angularMotorDirection.X;
2976// if (Math.Abs(m_angularMotorDirection.Y) > Math.Abs(m_angularMotorDVel.Y)) m_angularMotorDVel.Y = m_angularMotorDirection.Y;
2977// if (Math.Abs(m_angularMotorDirection.Z) > Math.Abs(m_angularMotorDVel.Z)) m_angularMotorDVel.Z = m_angularMotorDirection.Z;
2978 m_angularMotorDVel.X = m_angularMotorDirection.X;
2979 m_angularMotorDVel.Y = m_angularMotorDirection.Y;
2980 m_angularMotorDVel.Z = m_angularMotorDirection.Z;
2981 } // else let the motor decay on its own
2982
2983 public void Move(float timestep)
2984 {
2985 float fx = 0;
2986 float fy = 0;
2987 float fz = 0;
2988 Vector3 linvel; // velocity applied, including any reversal
2989 int outside = 0;
2990
2991 // If geomCrossingFailuresBeforeOutofbounds is set to 0 in OpenSim.ini then phys objects bounce off region borders.
2992 // This is a temp patch until proper region crossing is developed.
2993
2994 int failureLimit = _parent_scene.geomCrossingFailuresBeforeOutofbounds;
2995 float fence = _parent_scene.geomRegionFence;
2996
2997 frcount++; // used to limit debug comment output
2998 if (frcount > 50)
2999 frcount = 0;
3000
3001 if(revcount > 0) revcount--;
3002
3003 if (IsPhysical && (Body != IntPtr.Zero) && !m_isSelected && !childPrim) // Only move root prims.
3004 {
3005 // Old public void UpdatePositionAndVelocity(), more accuratley calculated here
3006 bool lastZeroFlag = _zeroFlag; // was it stopped
3007
3008 d.Vector3 vec = d.BodyGetPosition(Body);
3009 Vector3 l_position = Vector3.Zero;
3010 l_position.X = vec.X;
3011 l_position.Y = vec.Y;
3012 l_position.Z = vec.Z;
3013 m_lastposition = _position;
3014 _position = l_position;
3015
3016 d.Quaternion ori = d.BodyGetQuaternion(Body);
3017 // Quaternion l_orientation = Quaternion.Identity;
3018 _orientation.X = ori.X;
3019 _orientation.Y = ori.Y;
3020 _orientation.Z = ori.Z;
3021 _orientation.W = ori.W;
3022 m_lastorientation = _orientation;
3023
3024 d.Vector3 vel = d.BodyGetLinearVel(Body);
3025 m_lastVelocity = _velocity;
3026 _velocity.X = vel.X;
3027 _velocity.Y = vel.Y;
3028 _velocity.Z = vel.Z;
3029 _acceleration = ((_velocity - m_lastVelocity) / timestep);
3030
3031 d.Vector3 torque = d.BodyGetTorque(Body);
3032 _torque = new Vector3(torque.X, torque.Y, torque.Z);
3033
3034 base.RequestPhysicsterseUpdate();
3035
3036//Console.WriteLine("Move {0} at {1}", m_primName, l_position);
3037
3038 // Check if outside region
3039 // In Scene.cs/CrossPrimGroupIntoNewRegion the object is checked for 0.1M from border!
3040 if (l_position.X > ((float)_parent_scene.WorldExtents.X - fence))
3041 {
3042 l_position.X = ((float)_parent_scene.WorldExtents.X - fence);
3043 outside = 1;
3044 }
3045
3046 if (l_position.X < fence)
3047 {
3048 l_position.X = fence;
3049 outside = 2;
3050 }
3051 if (l_position.Y > ((float)_parent_scene.WorldExtents.Y - fence))
3052 {
3053 l_position.Y = ((float)_parent_scene.WorldExtents.Y - fence);
3054 outside = 3;
3055 }
3056
3057 if (l_position.Y < fence)
3058 {
3059 l_position.Y = fence;
3060 outside = 4;
3061 }
3062
3063 if (outside > 0)
3064 {
3065
3066//Console.WriteLine("Border {0} fence={1}", l_position, fence);
3067 if (fence > 0.0f) // bounce object off boundary
3068 {
3069 if (revcount == 0)
3070 {
3071 if (outside < 3)
3072 {
3073 _velocity.X = -_velocity.X;
3074 }
3075 else
3076 {
3077 _velocity.Y = -_velocity.Y;
3078 }
3079 if (m_type != Vehicle.TYPE_NONE) Halt();
3080 _position = l_position;
3081 m_taintposition = _position;
3082 m_lastVelocity = _velocity;
3083 _acceleration = Vector3.Zero;
3084 d.BodySetPosition(Body, _position.X, _position.Y, _position.Z);
3085 d.BodySetLinearVel(Body, _velocity.X, _velocity.Y, _velocity.Z);
3086 base.RequestPhysicsterseUpdate();
3087
3088 revcount = 25; // wait for object to move away from border
3089 }
3090 } // else old crossing mode
3091 else if (m_crossingfailures < failureLimit)
3092 { // keep trying to cross?
3093 _position = l_position;
3094 //_parent_scene.remActivePrim(this);
3095 if (_parent == null) base.RequestPhysicsterseUpdate();
3096 return; // Dont process any other motion?
3097 }
3098 else
3099 { // Too many tries
3100 if (_parent == null) base.RaiseOutOfBounds(l_position);
3101 return; // Dont process any other motion?
3102 } // end various methods
3103 } // end outside region horizontally
3104
3105
3106 if (l_position.Z < 0)
3107 {
3108 // This is so prim that get lost underground don't fall forever and suck up
3109 //
3110 // Sim resources and memory.
3111 // Disables the prim's movement physics....
3112 // It's a hack and will generate a console message if it fails.
3113
3114 //IsPhysical = false;
3115 if (_parent == null) base.RaiseOutOfBounds(_position);
3116
3117
3118 _acceleration.X = 0; // This stuff may stop client display but it has no
3119 _acceleration.Y = 0; // effect on the object in phys engine!
3120 _acceleration.Z = 0;
3121
3122 _velocity.X = 0;
3123 _velocity.Y = 0;
3124 _velocity.Z = 0;
3125 m_rotationalVelocity.X = 0;
3126 m_rotationalVelocity.Y = 0;
3127 m_rotationalVelocity.Z = 0;
3128
3129 if (_parent == null) base.RequestPhysicsterseUpdate();
3130
3131 m_throttleUpdates = false;
3132 throttleCounter = 0;
3133 _zeroFlag = true;
3134 //outofBounds = true;
3135 } // end neg Z check
3136
3137 // Is it moving?
3138 /* if ((Math.Abs(m_lastposition.X - l_position.X) < 0.02)
3139 && (Math.Abs(m_lastposition.Y - l_position.Y) < 0.02)
3140 && (Math.Abs(m_lastposition.Z - l_position.Z) < 0.02) */
3141 if ( (Vector3.Mag(_velocity) < 0.01) && // moving very slowly
3142 (Vector3.Mag(_velocity) < Vector3.Mag(m_lastVelocity)) && // decelerating
3143 (1.0 - Math.Abs(Quaternion.Dot(m_lastorientation, _orientation)) < 0.0001) ) // spinning very slowly
3144 {
3145 _zeroFlag = true;
3146 m_throttleUpdates = false;
3147 }
3148 else
3149 {
3150 //m_log.Debug(Math.Abs(m_lastposition.X - l_position.X).ToString());
3151 _zeroFlag = false;
3152 m_lastUpdateSent = false;
3153 //m_throttleUpdates = false;
3154 }
3155
3156 if (_zeroFlag)
3157 { // Its stopped
3158 _velocity.X = 0.0f;
3159 _velocity.Y = 0.0f;
3160 // _velocity.Z = 0.0f;
3161
3162 _acceleration.X = 0;
3163 _acceleration.Y = 0;
3164 // _acceleration.Z = 0;
3165
3166 m_rotationalVelocity.X = 0;
3167 m_rotationalVelocity.Y = 0;
3168 m_rotationalVelocity.Z = 0;
3169 // Stop it in the phys engine
3170 d.BodySetLinearVel(Body, 0.0f, 0.0f, _velocity.Z);
3171 d.BodySetAngularVel (Body, 0.0f, 0.0f, 0.0f);
3172
3173 if (!m_lastUpdateSent)
3174 {
3175 m_throttleUpdates = false;
3176 throttleCounter = 0;
3177 if (_parent == null)
3178 {
3179 base.RequestPhysicsterseUpdate();
3180 }
3181
3182 m_lastUpdateSent = true;
3183 }
3184 }
3185 else
3186 { // Its moving
3187 if (lastZeroFlag != _zeroFlag)
3188 {
3189 if (_parent == null)
3190 {
3191 base.RequestPhysicsterseUpdate();
3192 }
3193 }
3194 m_lastUpdateSent = false;
3195 if (!m_throttleUpdates || throttleCounter > _parent_scene.geomUpdatesPerThrottledUpdate)
3196 {
3197 if (_parent == null)
3198 {
3199 base.RequestPhysicsterseUpdate();
3200 }
3201 }
3202 else
3203 {
3204 throttleCounter++;
3205 }
3206 }
3207 m_lastposition = l_position;
3208
3209 /// End UpdatePositionAndVelocity insert
3210
3211
3212 // Rotation lock =====================================
3213 if(m_rotateEnableUpdate)
3214 {
3215 // Snapshot current angles, set up Amotor(s)
3216 m_rotateEnableUpdate = false;
3217 m_rotateEnable = m_rotateEnableRequest;
3218//Console.WriteLine("RotEnable {0} = {1}",m_primName, m_rotateEnable);
3219
3220 if (Amotor != IntPtr.Zero)
3221 {
3222 d.JointDestroy(Amotor);
3223 Amotor = IntPtr.Zero;
3224//Console.WriteLine("Old Amotor Destroyed");
3225 }
3226
3227 if (!m_rotateEnable.ApproxEquals(Vector3.One, 0.003f))
3228 { // not all are enabled
3229 d.Quaternion r = d.BodyGetQuaternion(Body);
3230 Quaternion locrot = new Quaternion(r.X, r.Y, r.Z, r.W);
3231 // extract the axes vectors
3232 Vector3 vX = new Vector3(1f,0f,0f);
3233 Vector3 vY = new Vector3(0f,1f,0f);
3234 Vector3 vZ = new Vector3(0f,0f,1f);
3235 vX = vX * locrot;
3236 vY = vY * locrot;
3237 vZ = vZ * locrot;
3238 // snapshot the current angle vectors
3239 m_lockX = vX;
3240 m_lockY = vY;
3241 m_lockZ = vZ;
3242 // m_lockRot = locrot;
3243 Amotor = d.JointCreateAMotor(_parent_scene.world, IntPtr.Zero);
3244 d.JointAttach(Amotor, Body, IntPtr.Zero);
3245 d.JointSetAMotorMode(Amotor, 0); // User mode??
3246//Console.WriteLine("New Amotor Created for {0}", m_primName);
3247
3248 float axisnum = 3; // how many to lock
3249 axisnum = (axisnum - (m_rotateEnable.X + m_rotateEnable.Y + m_rotateEnable.Z));
3250 d.JointSetAMotorNumAxes(Amotor,(int)axisnum);
3251//Console.WriteLine("AxisNum={0}",(int)axisnum);
3252
3253 int i = 0;
3254
3255 if (m_rotateEnable.X == 0)
3256 {
3257 d.JointSetAMotorAxis(Amotor, i, 0, m_lockX.X, m_lockX.Y, m_lockX.Z);
3258//Console.WriteLine("AxisX {0} set to {1}", i, m_lockX);
3259 i++;
3260 }
3261
3262 if (m_rotateEnable.Y == 0)
3263 {
3264 d.JointSetAMotorAxis(Amotor, i, 0, m_lockY.X, m_lockY.Y, m_lockY.Z);
3265//Console.WriteLine("AxisY {0} set to {1}", i, m_lockY);
3266 i++;
3267 }
3268
3269 if (m_rotateEnable.Z == 0)
3270 {
3271 d.JointSetAMotorAxis(Amotor, i, 0, m_lockZ.X, m_lockZ.Y, m_lockZ.Z);
3272//Console.WriteLine("AxisZ {0} set to {1}", i, m_lockZ);
3273 i++;
3274 }
3275
3276 // These lowstops and high stops are effectively (no wiggle room)
3277 d.JointSetAMotorParam(Amotor, (int)dParam.LowStop, 0f);
3278 d.JointSetAMotorParam(Amotor, (int)dParam.LoStop3, 0f);
3279 d.JointSetAMotorParam(Amotor, (int)dParam.LoStop2, 0f);
3280 d.JointSetAMotorParam(Amotor, (int)dParam.HiStop, 0f);
3281 d.JointSetAMotorParam(Amotor, (int)dParam.HiStop3, 0f);
3282 d.JointSetAMotorParam(Amotor, (int)dParam.HiStop2, 0f);
3283 d.JointSetAMotorParam(Amotor, (int) dParam.Vel, 0f);
3284 d.JointSetAMotorParam(Amotor, (int) dParam.Vel3, 0f);
3285 d.JointSetAMotorParam(Amotor, (int) dParam.Vel2, 0f);
3286 d.JointSetAMotorParam(Amotor, (int)dParam.StopCFM, 0f);
3287 d.JointSetAMotorParam(Amotor, (int)dParam.StopCFM3, 0f);
3288 d.JointSetAMotorParam(Amotor, (int)dParam.StopCFM2, 0f);
3289 } // else none are locked
3290 } // end Rotation Update
3291
3292
3293 // VEHICLE processing ==========================================
3294 if (m_type != Vehicle.TYPE_NONE)
3295 {
3296 // get body attitude
3297 d.Quaternion rot = d.BodyGetQuaternion(Body);
3298 Quaternion rotq = new Quaternion(rot.X, rot.Y, rot.Z, rot.W); // rotq = rotation of object
3299 Quaternion irotq = Quaternion.Inverse(rotq);
3300
3301 // VEHICLE Linear Motion
3302 d.Vector3 velnow = d.BodyGetLinearVel(Body); // this is in world frame
3303 Vector3 vel_now = new Vector3(velnow.X, velnow.Y, velnow.Z);
3304 m_lLinObjectVel = vel_now * irotq;
3305 if (m_linearMotorDecayTimescale < 300.0f) //setting of 300 or more disables decay rate
3306 {
3307 if ( Vector3.Mag(m_lLinMotorDVel) < 1.0f)
3308 {
3309 float decayfactor = m_linearMotorDecayTimescale/timestep;
3310 Vector3 decayAmount = (m_lLinMotorDVel/decayfactor);
3311 m_lLinMotorDVel -= decayAmount;
3312 }
3313 else
3314 {
3315 float decayfactor = 3.0f - (0.57f * (float)Math.Log((double)(m_linearMotorDecayTimescale)));
3316 Vector3 decel = Vector3.Normalize(m_lLinMotorDVel) * decayfactor * timestep;
3317 m_lLinMotorDVel -= decel;
3318 }
3319 if (m_lLinMotorDVel.ApproxEquals(Vector3.Zero, 0.01f))
3320 {
3321 m_lLinMotorDVel = Vector3.Zero;
3322 }
3323
3324 /* else
3325 {
3326 if (Math.Abs(m_lLinMotorDVel.X) < Math.Abs(m_lLinObjectVel.X)) m_lLinObjectVel.X = m_lLinMotorDVel.X;
3327 if (Math.Abs(m_lLinMotorDVel.Y) < Math.Abs(m_lLinObjectVel.Y)) m_lLinObjectVel.Y = m_lLinMotorDVel.Y;
3328 if (Math.Abs(m_lLinMotorDVel.Z) < Math.Abs(m_lLinObjectVel.Z)) m_lLinObjectVel.Z = m_lLinMotorDVel.Z;
3329 } */
3330 } // end linear motor decay
3331
3332 if ( (! m_lLinMotorDVel.ApproxEquals(Vector3.Zero, 0.01f)) || (! m_lLinObjectVel.ApproxEquals(Vector3.Zero, 0.01f)) )
3333 {
3334 if(!d.BodyIsEnabled (Body)) d.BodyEnable (Body);
3335 if (m_linearMotorTimescale < 300.0f)
3336 {
3337 Vector3 attack_error = m_lLinMotorDVel - m_lLinObjectVel;
3338 float linfactor = m_linearMotorTimescale/timestep;
3339 Vector3 attackAmount = (attack_error/linfactor) * 1.3f;
3340 m_lLinObjectVel += attackAmount;
3341 }
3342 if (m_linearFrictionTimescale.X < 300.0f)
3343 {
3344 float fricfactor = m_linearFrictionTimescale.X / timestep;
3345 float fricX = m_lLinObjectVel.X / fricfactor;
3346 m_lLinObjectVel.X -= fricX;
3347 }
3348 if (m_linearFrictionTimescale.Y < 300.0f)
3349 {
3350 float fricfactor = m_linearFrictionTimescale.Y / timestep;
3351 float fricY = m_lLinObjectVel.Y / fricfactor;
3352 m_lLinObjectVel.Y -= fricY;
3353 }
3354 if (m_linearFrictionTimescale.Z < 300.0f)
3355 {
3356 float fricfactor = m_linearFrictionTimescale.Z / timestep;
3357 float fricZ = m_lLinObjectVel.Z / fricfactor;
3358 m_lLinObjectVel.Z -= fricZ;
3359 }
3360 }
3361 m_wLinObjectVel = m_lLinObjectVel * rotq;
3362
3363 // Gravity and Buoyancy
3364 Vector3 grav = Vector3.Zero;
3365 if(m_VehicleBuoyancy < 1.0f)
3366 {
3367 // There is some gravity, make a gravity force vector
3368 // that is applied after object velocity.
3369 d.Mass objMass;
3370 d.BodyGetMass(Body, out objMass);
3371 // m_VehicleBuoyancy: -1=2g; 0=1g; 1=0g;
3372 grav.Z = _parent_scene.gravityz * objMass.mass * (1f - m_VehicleBuoyancy); // Applied later as a force
3373 } // else its 1.0, no gravity.
3374
3375 // Hovering
3376 if( (m_flags & (VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | VehicleFlag.HOVER_GLOBAL_HEIGHT)) != 0)
3377 {
3378 // We should hover, get the target height
3379 d.Vector3 pos = d.BodyGetPosition(Body);
3380 if((m_flags & VehicleFlag.HOVER_WATER_ONLY) == VehicleFlag.HOVER_WATER_ONLY)
3381 {
3382 m_VhoverTargetHeight = _parent_scene.GetWaterLevel() + m_VhoverHeight;
3383 }
3384 else if((m_flags & VehicleFlag.HOVER_TERRAIN_ONLY) == VehicleFlag.HOVER_TERRAIN_ONLY)
3385 {
3386 m_VhoverTargetHeight = _parent_scene.GetTerrainHeightAtXY(pos.X, pos.Y) + m_VhoverHeight;
3387 }
3388 else if((m_flags & VehicleFlag.HOVER_GLOBAL_HEIGHT) == VehicleFlag.HOVER_GLOBAL_HEIGHT)
3389 {
3390 m_VhoverTargetHeight = m_VhoverHeight;
3391 }
3392
3393 if((m_flags & VehicleFlag.HOVER_UP_ONLY) == VehicleFlag.HOVER_UP_ONLY)
3394 {
3395 // If body is aready heigher, use its height as target height
3396 if(pos.Z > m_VhoverTargetHeight) m_VhoverTargetHeight = pos.Z;
3397 }
3398
3399// m_VhoverEfficiency = 0f; // 0=boucy, 1=Crit.damped
3400// m_VhoverTimescale = 0f; // time to acheive height
3401// timestep is time since last frame,in secs
3402 float herr0 = pos.Z - m_VhoverTargetHeight;
3403 // Replace Vertical speed with correction figure if significant
3404 if(Math.Abs(herr0) > 0.01f )
3405 {
3406 //? d.Mass objMass;
3407 //? d.BodyGetMass(Body, out objMass);
3408 m_wLinObjectVel.Z = - ( (herr0 * timestep * 50.0f) / m_VhoverTimescale);
3409 //KF: m_VhoverEfficiency is not yet implemented
3410 }
3411 else
3412 {
3413 m_wLinObjectVel.Z = 0f;
3414 }
3415 }
3416 else
3417 { // not hovering
3418 if (m_wLinObjectVel.Z == 0f)
3419 { // Gravity rules
3420 m_wLinObjectVel.Z = vel_now.Z;
3421 } // else the motor has it
3422 }
3423 linvel = m_wLinObjectVel;
3424
3425 // Vehicle Linear Motion done =======================================
3426 // Apply velocity
3427 d.BodySetLinearVel(Body, linvel.X, linvel.Y, linvel.Z);
3428 // apply gravity force
3429 d.BodyAddForce(Body, grav.X, grav.Y, grav.Z);
3430//if(frcount == 0) Console.WriteLine("Vel={0} Force={1}",linvel , grav);
3431 // end MoveLinear()
3432
3433
3434 // MoveAngular
3435 /*
3436 private Vector3 m_angularMotorDirection = Vector3.Zero; // angular velocity requested by LSL motor
3437
3438 private float m_angularMotorTimescale = 0; // motor angular Attack rate set by LSL
3439 private float m_angularMotorDecayTimescale = 0; // motor angular Decay rate set by LSL
3440 private Vector3 m_angularFrictionTimescale = Vector3.Zero; // body angular Friction set by LSL
3441
3442 private Vector3 m_angularMotorDVel = Vector3.Zero; // decayed angular motor
3443 private Vector3 m_angObjectVel = Vector3.Zero; // what was last applied to body
3444 */
3445//if(frcount == 0) Console.WriteLine("MoveAngular ");
3446
3447 d.Vector3 angularObjectVel = d.BodyGetAngularVel(Body);
3448 Vector3 angObjectVel = new Vector3(angularObjectVel.X, angularObjectVel.Y, angularObjectVel.Z);
3449 angObjectVel = angObjectVel * irotq; // ============ Converts to LOCAL rotation
3450
3451//if(frcount == 0) Console.WriteLine("V0 = {0}", angObjectVel);
3452
3453 // Decay Angular Motor 1. In SL this also depends on attack rate! decay ~= 23/Attack.
3454 float atk_decayfactor = 23.0f / (m_angularMotorTimescale * timestep);
3455 m_angularMotorDVel -= m_angularMotorDVel / atk_decayfactor;
3456 // Decay Angular Motor 2.
3457 if (m_angularMotorDecayTimescale < 300.0f)
3458 {
3459 if ( Vector3.Mag(m_angularMotorDVel) < 1.0f)
3460 {
3461 float decayfactor = (m_angularMotorDecayTimescale)/timestep;
3462 Vector3 decayAmount = (m_angularMotorDVel/decayfactor);
3463 m_angularMotorDVel -= decayAmount;
3464 }
3465 else
3466 {
3467 Vector3 decel = Vector3.Normalize(m_angularMotorDVel) * timestep / m_angularMotorDecayTimescale;
3468 m_angularMotorDVel -= decel;
3469 }
3470
3471 if (m_angularMotorDVel.ApproxEquals(Vector3.Zero, 0.01f))
3472 {
3473 m_angularMotorDVel = Vector3.Zero;
3474 }
3475 else
3476 {
3477 if (Math.Abs(m_angularMotorDVel.X) < Math.Abs(angObjectVel.X)) angObjectVel.X = m_angularMotorDVel.X;
3478 if (Math.Abs(m_angularMotorDVel.Y) < Math.Abs(angObjectVel.Y)) angObjectVel.Y = m_angularMotorDVel.Y;
3479 if (Math.Abs(m_angularMotorDVel.Z) < Math.Abs(angObjectVel.Z)) angObjectVel.Z = m_angularMotorDVel.Z;
3480 }
3481 } // end decay angular motor
3482//if(frcount == 0) Console.WriteLine("MotorDvel {0} Obj {1}", m_angularMotorDVel, angObjectVel);
3483
3484//if(frcount == 0) Console.WriteLine("VA = {0}", angObjectVel);
3485
3486 if ( (! m_angularMotorDVel.ApproxEquals(Vector3.Zero, 0.01f)) || (! angObjectVel.ApproxEquals(Vector3.Zero, 0.01f)) )
3487 { // if motor or object have motion
3488 if(!d.BodyIsEnabled (Body)) d.BodyEnable (Body);
3489
3490 if (m_angularMotorTimescale < 300.0f)
3491 {
3492 Vector3 attack_error = m_angularMotorDVel - angObjectVel;
3493 float angfactor = m_angularMotorTimescale/timestep;
3494 Vector3 attackAmount = (attack_error/angfactor);
3495 angObjectVel += attackAmount;
3496//if(frcount == 0) Console.WriteLine("Accel {0} Attk {1}",FrAaccel, attackAmount);
3497//if(frcount == 0) Console.WriteLine("V2+= {0}", angObjectVel);
3498 }
3499
3500 angObjectVel.X -= angObjectVel.X / (m_angularFrictionTimescale.X * 0.7f / timestep);
3501 angObjectVel.Y -= angObjectVel.Y / (m_angularFrictionTimescale.Y * 0.7f / timestep);
3502 angObjectVel.Z -= angObjectVel.Z / (m_angularFrictionTimescale.Z * 0.7f / timestep);
3503 } // else no signif. motion
3504
3505//if(frcount == 0) Console.WriteLine("Dmotor {0} Obj {1}", m_angularMotorDVel, angObjectVel);
3506 // Bank section tba
3507 // Deflection section tba
3508//if(frcount == 0) Console.WriteLine("V3 = {0}", angObjectVel);
3509
3510
3511 /* // Rotation Axis Disables:
3512 if (!m_angularEnable.ApproxEquals(Vector3.One, 0.003f))
3513 {
3514 if (m_angularEnable.X == 0)
3515 angObjectVel.X = 0f;
3516 if (m_angularEnable.Y == 0)
3517 angObjectVel.Y = 0f;
3518 if (m_angularEnable.Z == 0)
3519 angObjectVel.Z = 0f;
3520 }
3521 */
3522 angObjectVel = angObjectVel * rotq; // ================ Converts to WORLD rotation
3523
3524 // Vertical attractor section
3525 Vector3 vertattr = Vector3.Zero;
3526
3527 if(m_verticalAttractionTimescale < 300)
3528 {
3529 float VAservo = 1.0f / (m_verticalAttractionTimescale * timestep);
3530 // make a vector pointing up
3531 Vector3 verterr = Vector3.Zero;
3532 verterr.Z = 1.0f;
3533 // rotate it to Body Angle
3534 verterr = verterr * rotq;
3535 // verterr.X and .Y are the World error ammounts. They are 0 when there is no error (Vehicle Body is 'vertical'), and .Z will be 1.
3536 // As the body leans to its side |.X| will increase to 1 and .Z fall to 0. As body inverts |.X| will fall and .Z will go
3537 // negative. Similar for tilt and |.Y|. .X and .Y must be modulated to prevent a stable inverted body.
3538
3539 if (verterr.Z < 0.0f)
3540 { // Deflection from vertical exceeds 90-degrees. This method will ensure stable return to
3541 // vertical, BUT for some reason a z-rotation is imparted to the object. TBI.
3542//Console.WriteLine("InvertFlip");
3543 verterr.X = 2.0f - verterr.X;
3544 verterr.Y = 2.0f - verterr.Y;
3545 }
3546 verterr *= 0.5f;
3547 // verterror is 0 (no error) to +/- 1 (max error at 180-deg tilt)
3548 Vector3 xyav = angObjectVel;
3549 xyav.Z = 0.0f;
3550 if ((!xyav.ApproxEquals(Vector3.Zero, 0.001f)) || (verterr.Z < 0.49f))
3551 {
3552 // As the body rotates around the X axis, then verterr.Y increases; Rotated around Y then .X increases, so
3553 // Change Body angular velocity X based on Y, and Y based on X. Z is not changed.
3554 vertattr.X = verterr.Y;
3555 vertattr.Y = - verterr.X;
3556 vertattr.Z = 0f;
3557//if(frcount == 0) Console.WriteLine("VAerr=" + verterr);
3558
3559 // scaling appears better usingsquare-law
3560 float damped = m_verticalAttractionEfficiency * m_verticalAttractionEfficiency;
3561 float bounce = 1.0f - damped;
3562 // 0 = crit damp, 1 = bouncy
3563 float oavz = angObjectVel.Z; // retain z velocity
3564 // time-scaled correction, which sums, therefore is bouncy:
3565 angObjectVel = (angObjectVel + (vertattr * VAservo * 0.0333f)) * bounce;
3566 // damped, good @ < 90:
3567 angObjectVel = angObjectVel + (vertattr * VAservo * 0.0667f * damped);
3568 angObjectVel.Z = oavz;
3569//if(frcount == 0) Console.WriteLine("VA+");
3570//Console.WriteLine("VAttr {0} OAvel {1}", vertattr, angObjectVel);
3571 }
3572 else
3573 {
3574 // else error is very small
3575 angObjectVel.X = 0f;
3576 angObjectVel.Y = 0f;
3577//if(frcount == 0) Console.WriteLine("VA0");
3578 }
3579 } // else vertical attractor is off
3580//if(frcount == 0) Console.WriteLine("V1 = {0}", angObjectVel);
3581
3582
3583 m_lastAngularVelocity = angObjectVel;
3584 // apply Angular Velocity to body
3585 d.BodySetAngularVel (Body, m_lastAngularVelocity.X, m_lastAngularVelocity.Y, m_lastAngularVelocity.Z);
3586//if(frcount == 0) Console.WriteLine("V4 = {0}", m_lastAngularVelocity);
3587
3588 } // end VEHICLES
3589 else
3590 {
3591 // Dyamics (NON-'VEHICLES') are dealt with here ================================================================
3592
3593 if(!d.BodyIsEnabled (Body)) d.BodyEnable (Body); // KF add 161009
3594
3595 /// Dynamics Buoyancy
3596 //KF: m_buoyancy is set by llSetBuoyancy() and is for non-vehicle.
3597 // m_buoyancy: (unlimited value) <0=Falls fast; 0=1g; 1=0g; >1 = floats up
3598 // NB Prims in ODE are no subject to global gravity
3599 // This should only affect gravity operations
3600
3601 float m_mass = CalculateMass();
3602 // calculate z-force due togravity on object.
3603 fz = _parent_scene.gravityz * (1.0f - m_buoyancy) * m_mass; // force = acceleration * mass
3604 if ((m_usePID) && (m_PIDTau > 0.0f)) // Dynamics llMoveToTarget.
3605 {
3606 fz = 0; // llMoveToTarget ignores gravity.
3607 // it also ignores mass of object, and any physical resting on it.
3608 // Vector3 m_PIDTarget is where we are going
3609 // float m_PIDTau is time to get there
3610 fx = 0;
3611 fy = 0;
3612 d.Vector3 pos = d.BodyGetPosition(Body);
3613 Vector3 error = new Vector3(
3614 (m_PIDTarget.X - pos.X),
3615 (m_PIDTarget.Y - pos.Y),
3616 (m_PIDTarget.Z - pos.Z));
3617 if (error.ApproxEquals(Vector3.Zero,0.01f))
3618 { // Very close, Jump there and quit move
3619
3620 d.BodySetPosition(Body, m_PIDTarget.X, m_PIDTarget.Y, m_PIDTarget.Z);
3621 _target_velocity = Vector3.Zero;
3622 d.BodySetLinearVel(Body, _target_velocity.X, _target_velocity.Y, _target_velocity.Z);
3623 }
3624 else
3625 {
3626 float scale = 50.0f * timestep / m_PIDTau;
3627 if ((error.ApproxEquals(Vector3.Zero,0.5f)) && (_target_velocity != Vector3.Zero))
3628 {
3629 // Nearby, quit update of velocity
3630 }
3631 else
3632 { // Far, calc damped velocity
3633 _target_velocity = error * scale;
3634 }
3635 d.BodySetLinearVel(Body, _target_velocity.X, _target_velocity.Y, _target_velocity.Z);
3636 }
3637 } // end PID MoveToTarget
3638
3639
3640 /// Dynamics Hover ===================================================================================
3641 // Hover PID Controller can only run if the PIDcontroller is not in use.
3642 if (m_useHoverPID && !m_usePID)
3643 {
3644//Console.WriteLine("Hover " + m_primName);
3645
3646 // If we're using the PID controller, then we have no gravity
3647 fz = (-1 * _parent_scene.gravityz) * m_mass;
3648
3649 // no lock; for now it's only called from within Simulate()
3650
3651 // If the PID Controller isn't active then we set our force
3652 // calculating base velocity to the current position
3653
3654 if ((m_PIDTau < 1))
3655 {
3656 PID_G = PID_G / m_PIDTau;
3657 }
3658
3659 if ((PID_G - m_PIDTau) <= 0)
3660 {
3661 PID_G = m_PIDTau + 1;
3662 }
3663
3664
3665 // Where are we, and where are we headed?
3666 d.Vector3 pos = d.BodyGetPosition(Body);
3667// d.Vector3 vel = d.BodyGetLinearVel(Body);
3668
3669
3670 // Non-Vehicles have a limited set of Hover options.
3671 // determine what our target height really is based on HoverType
3672 switch (m_PIDHoverType)
3673 {
3674 case PIDHoverType.Ground:
3675 m_groundHeight = _parent_scene.GetTerrainHeightAtXY(pos.X, pos.Y);
3676 m_targetHoverHeight = m_groundHeight + m_PIDHoverHeight;
3677 break;
3678 case PIDHoverType.GroundAndWater:
3679 m_groundHeight = _parent_scene.GetTerrainHeightAtXY(pos.X, pos.Y);
3680 m_waterHeight = _parent_scene.GetWaterLevel();
3681 if (m_groundHeight > m_waterHeight)
3682 {
3683 m_targetHoverHeight = m_groundHeight + m_PIDHoverHeight;
3684 }
3685 else
3686 {
3687 m_targetHoverHeight = m_waterHeight + m_PIDHoverHeight;
3688 }
3689 break;
3690
3691 } // end switch (m_PIDHoverType)
3692
3693
3694 _target_velocity =
3695 new Vector3(0.0f, 0.0f,
3696 (m_targetHoverHeight - pos.Z) * ((PID_G - m_PIDHoverTau) * timestep)
3697 );
3698
3699 // if velocity is zero, use position control; otherwise, velocity control
3700
3701 if (_target_velocity.ApproxEquals(Vector3.Zero, 0.1f))
3702 {
3703 // keep track of where we stopped. No more slippin' & slidin'
3704
3705 // We only want to deactivate the PID Controller if we think we want to have our surrogate
3706 // react to the physics scene by moving it's position.
3707 // Avatar to Avatar collisions
3708 // Prim to avatar collisions
3709 d.Vector3 dlinvel = vel;
3710 d.BodySetPosition(Body, pos.X, pos.Y, m_targetHoverHeight);
3711 d.BodySetLinearVel(Body, dlinvel.X, dlinvel.Y, dlinvel.Z);
3712 d.BodyAddForce(Body, 0, 0, fz);
3713 //KF this prevents furthur motions return;
3714 }
3715 else
3716 {
3717 _zeroFlag = false;
3718
3719 // We're flying and colliding with something
3720 fz = fz + ((_target_velocity.Z - vel.Z) * (PID_D) * m_mass);
3721 }
3722 } // end m_useHoverPID && !m_usePID
3723
3724
3725 /// Dynamics Apply Forces ===================================================================================
3726 fx *= m_mass;
3727 fy *= m_mass;
3728 //fz *= m_mass;
3729 fx += m_force.X;
3730 fy += m_force.Y;
3731 fz += m_force.Z;
3732
3733 //m_log.Info("[OBJPID]: X:" + fx.ToString() + " Y:" + fy.ToString() + " Z:" + fz.ToString());
3734 if (fx != 0 || fy != 0 || fz != 0)
3735 {
3736 //m_taintdisable = true;
3737 //base.RaiseOutOfBounds(Position);
3738 //d.BodySetLinearVel(Body, fx, fy, 0f);
3739 if (!d.BodyIsEnabled(Body))
3740 {
3741 // A physical body at rest on a surface will auto-disable after a while,
3742 // this appears to re-enable it incase the surface it is upon vanishes,
3743 // and the body should fall again.
3744 d.BodySetLinearVel(Body, 0f, 0f, 0f);
3745 d.BodySetForce(Body, 0f, 0f, 0f);
3746 enableBodySoft();
3747 }
3748
3749 // 35x10 = 350n times the mass per second applied maximum.
3750 float nmax = 35f * m_mass;
3751 float nmin = -35f * m_mass;
3752
3753
3754 if (fx > nmax)
3755 fx = nmax;
3756 if (fx < nmin)
3757 fx = nmin;
3758 if (fy > nmax)
3759 fy = nmax;
3760 if (fy < nmin)
3761 fy = nmin;
3762 d.BodyAddForce(Body, fx, fy, fz);
3763 } // end apply forces
3764 } // end Vehicle/Dynamics
3765
3766 /// RotLookAt / LookAt =================================================================================
3767 if (m_useAPID)
3768 {
3769 // RotLookAt, apparently overrides all other rotation sources. Inputs:
3770 // Quaternion m_APIDTarget
3771 // float m_APIDStrength // From SL experiments, this is the time to get there
3772 // float m_APIDDamping // From SL experiments, this is damping, 1.0 = damped, 0.1 = wobbly
3773 // Also in SL the mass of the object has no effect on time to get there.
3774 // Factors:
3775 // get present body rotation
3776 float limit = 1.0f;
3777 float rscaler = 50f; // adjusts rotation damping time
3778 float lscaler = 10f; // adjusts linear damping time in llLookAt
3779 float RLAservo = 0f;
3780 Vector3 diff_axis;
3781 float diff_angle;
3782 d.Quaternion rot = d.BodyGetQuaternion(Body); // prim present rotation
3783 Quaternion rotq = new Quaternion(rot.X, rot.Y, rot.Z, rot.W);
3784 Quaternion rtarget = new Quaternion();
3785
3786 if(m_APIDTarget.W == -99.9f)
3787 {
3788 // this is really a llLookAt(), x,y,z is the target vector
3789 Vector3 target = new Vector3(m_APIDTarget.X, m_APIDTarget.Y, m_APIDTarget.Z);
3790 Vector3 ospin = new Vector3(1.0f, 0.0f, 0.0f) * rotq;
3791 Vector3 error = new Vector3(0.0f, 0.0f, 0.0f);
3792 float twopi = 2.0f * (float)Math.PI;
3793 Vector3 dir = target - _position;
3794 dir.Normalize();
3795 float tzrot = (float)Math.Atan2(dir.Y, dir.X);
3796 float txy = (float)Math.Sqrt((dir.X * dir.X) + (dir.Y * dir.Y));
3797 float terot = (float)Math.Atan2(dir.Z, txy);
3798 float ozrot = (float)Math.Atan2(ospin.Y, ospin.X);
3799 float oxy = (float)Math.Sqrt((ospin.X * ospin.X) + (ospin.Y * ospin.Y));
3800 float oerot = (float)Math.Atan2(ospin.Z, oxy);
3801 float ra = 2.0f * ((rotq.W * rotq.X) + (rotq.Y * rotq.Z));
3802 float rb = 1.0f - 2.0f * ((rotq.Y * rotq.Y)+(rotq.X * rotq.X));
3803 float roll = (float)Math.Atan2(ra, rb);
3804 float errorz = tzrot - ozrot;
3805 if(errorz > (float)Math.PI) errorz -= twopi;
3806 else if(errorz < -(float)Math.PI) errorz += twopi;
3807 float errory = oerot - terot;
3808 if(errory > (float)Math.PI) errory -= twopi;
3809 else if(errory < -(float)Math.PI) errory += twopi;
3810 diff_angle = Math.Abs(errorz) + Math.Abs(errory) + Math.Abs(roll);
3811 if(diff_angle > 0.01f * m_APIDdamper)
3812 {
3813 m_APIDdamper = 1.0f;
3814 RLAservo = timestep / m_APIDStrength * rscaler;
3815 errorz *= RLAservo;
3816 errory *= RLAservo;
3817 error.X = -roll * 8.0f;
3818 error.Y = errory;
3819 error.Z = errorz;
3820 error *= rotq;
3821 d.BodySetAngularVel (Body, error.X, error.Y, error.Z);
3822 }
3823 else
3824 {
3825 d.BodySetAngularVel (Body, 0.0f, 0.0f, 0.0f);
3826 m_APIDdamper = 2.0f;
3827 }
3828 }
3829 else
3830 {
3831 // this is a llRotLookAt()
3832 rtarget = m_APIDTarget;
3833
3834 Quaternion rot_diff = Quaternion.Inverse(rotq) * rtarget; // difference to desired rot
3835 rot_diff.GetAxisAngle(out diff_axis, out diff_angle); // convert to axis to point at & error angle
3836//if(frcount == 0) Console.WriteLine("axis {0} angle {1}",diff_axis * 57.3f, diff_angle);
3837
3838 // diff_axis.Normalize(); it already is!
3839 if(diff_angle > 0.01f * m_APIDdamper) // diff_angle is always +ve // if there is enough error
3840 {
3841 m_APIDdamper = 1.0f;
3842 Vector3 rotforce = new Vector3(diff_axis.X, diff_axis.Y, diff_axis.Z);
3843 rotforce = rotforce * rotq;
3844 if(diff_angle > limit) diff_angle = limit; // cap the rotate rate
3845 RLAservo = timestep / m_APIDStrength * lscaler;
3846 rotforce = rotforce * RLAservo * diff_angle ;
3847 d.BodySetAngularVel (Body, rotforce.X, rotforce.Y, rotforce.Z);
3848//Console.WriteLine("axis= " + diff_axis + " angle= " + diff_angle + "servo= " + RLAservo);
3849 }
3850 else
3851 { // close enough
3852 d.BodySetAngularVel (Body, 0.0f, 0.0f, 0.0f);
3853 m_APIDdamper = 2.0f;
3854 }
3855 } // end llLookAt/llRotLookAt
3856//if(frcount == 0) Console.WriteLine("mass= " + m_mass + " servo= " + RLAservo + " angle= " + diff_angle);
3857 } // end m_useAPID
3858 } // end root prims
3859 } // end Move()
3860 } // end class
3861}