aboutsummaryrefslogtreecommitdiffstatshomepage
path: root/OpenSim/Region/Physics
diff options
context:
space:
mode:
authorRobert Adams2012-11-25 20:03:36 -0800
committerRobert Adams2012-11-25 20:04:33 -0800
commit084e3926ca4c344279935f1bce3173c8f6e8258a (patch)
tree2a6d442f2264061be5e86ed0bd27cec4737e0846 /OpenSim/Region/Physics
parentBulletSim: up the vehicle angular damping to 0.95. Still trying to overcome t... (diff)
downloadopensim-SC_OLD-084e3926ca4c344279935f1bce3173c8f6e8258a.zip
opensim-SC_OLD-084e3926ca4c344279935f1bce3173c8f6e8258a.tar.gz
opensim-SC_OLD-084e3926ca4c344279935f1bce3173c8f6e8258a.tar.bz2
opensim-SC_OLD-084e3926ca4c344279935f1bce3173c8f6e8258a.tar.xz
BulletSim: use m_angularMotor to do the basic movement. Add the setting of same. Rename the angular forces and add comments to match MoveAngular to the form of MoveLinear.
Diffstat (limited to 'OpenSim/Region/Physics')
-rw-r--r--OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs124
1 files changed, 64 insertions, 60 deletions
diff --git a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs
index 7757584..95a4134 100644
--- a/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs
+++ b/OpenSim/Region/Physics/BulletSPlugin/BSDynamics.cs
@@ -93,6 +93,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
93 // private Vector3 m_linearMotorOffset = Vector3.Zero; 93 // private Vector3 m_linearMotorOffset = Vector3.Zero;
94 94
95 //Angular properties 95 //Angular properties
96 private BSVMotor m_angularMotor = new BSVMotor("AngularMotor");
96 private Vector3 m_angularMotorDirection = Vector3.Zero; // angular velocity requested by LSL motor 97 private Vector3 m_angularMotorDirection = Vector3.Zero; // angular velocity requested by LSL motor
97 // private int m_angularMotorApply = 0; // application frame counter 98 // private int m_angularMotorApply = 0; // application frame counter
98 private Vector3 m_angularMotorVelocity = Vector3.Zero; // current angular motor velocity 99 private Vector3 m_angularMotorVelocity = Vector3.Zero; // current angular motor velocity
@@ -153,9 +154,11 @@ namespace OpenSim.Region.Physics.BulletSPlugin
153 break; 154 break;
154 case Vehicle.ANGULAR_MOTOR_DECAY_TIMESCALE: 155 case Vehicle.ANGULAR_MOTOR_DECAY_TIMESCALE:
155 m_angularMotorDecayTimescale = Math.Max(0.01f, Math.Min(pValue,120)); 156 m_angularMotorDecayTimescale = Math.Max(0.01f, Math.Min(pValue,120));
157 m_angularMotor.TargetValueDecayTimeScale = m_angularMotorDecayTimescale;
156 break; 158 break;
157 case Vehicle.ANGULAR_MOTOR_TIMESCALE: 159 case Vehicle.ANGULAR_MOTOR_TIMESCALE:
158 m_angularMotorTimescale = Math.Max(pValue, 0.01f); 160 m_angularMotorTimescale = Math.Max(pValue, 0.01f);
161 m_angularMotor.TimeScale = m_angularMotorTimescale;
159 break; 162 break;
160 case Vehicle.BANKING_EFFICIENCY: 163 case Vehicle.BANKING_EFFICIENCY:
161 m_bankingEfficiency = Math.Max(-1f, Math.Min(pValue, 1f)); 164 m_bankingEfficiency = Math.Max(-1f, Math.Min(pValue, 1f));
@@ -203,10 +206,11 @@ namespace OpenSim.Region.Physics.BulletSPlugin
203 // set all of the components to the same value 206 // set all of the components to the same value
204 case Vehicle.ANGULAR_FRICTION_TIMESCALE: 207 case Vehicle.ANGULAR_FRICTION_TIMESCALE:
205 m_angularFrictionTimescale = new Vector3(pValue, pValue, pValue); 208 m_angularFrictionTimescale = new Vector3(pValue, pValue, pValue);
209 m_angularMotor.FrictionTimescale = m_angularFrictionTimescale;
206 break; 210 break;
207 case Vehicle.ANGULAR_MOTOR_DIRECTION: 211 case Vehicle.ANGULAR_MOTOR_DIRECTION:
208 m_angularMotorDirection = new Vector3(pValue, pValue, pValue); 212 m_angularMotorDirection = new Vector3(pValue, pValue, pValue);
209 // m_angularMotorApply = 100; 213 m_angularMotor.SetTarget(m_angularMotorDirection);
210 break; 214 break;
211 case Vehicle.LINEAR_FRICTION_TIMESCALE: 215 case Vehicle.LINEAR_FRICTION_TIMESCALE:
212 m_linearFrictionTimescale = new Vector3(pValue, pValue, pValue); 216 m_linearFrictionTimescale = new Vector3(pValue, pValue, pValue);
@@ -231,6 +235,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
231 { 235 {
232 case Vehicle.ANGULAR_FRICTION_TIMESCALE: 236 case Vehicle.ANGULAR_FRICTION_TIMESCALE:
233 m_angularFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z); 237 m_angularFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z);
238 m_angularMotor.FrictionTimescale = m_angularFrictionTimescale;
234 break; 239 break;
235 case Vehicle.ANGULAR_MOTOR_DIRECTION: 240 case Vehicle.ANGULAR_MOTOR_DIRECTION:
236 // Limit requested angular speed to 2 rps= 4 pi rads/sec 241 // Limit requested angular speed to 2 rps= 4 pi rads/sec
@@ -238,7 +243,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
238 pValue.Y = Math.Max(-12.56f, Math.Min(pValue.Y, 12.56f)); 243 pValue.Y = Math.Max(-12.56f, Math.Min(pValue.Y, 12.56f));
239 pValue.Z = Math.Max(-12.56f, Math.Min(pValue.Z, 12.56f)); 244 pValue.Z = Math.Max(-12.56f, Math.Min(pValue.Z, 12.56f));
240 m_angularMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z); 245 m_angularMotorDirection = new Vector3(pValue.X, pValue.Y, pValue.Z);
241 // m_angularMotorApply = 100; 246 m_angularMotor.SetTarget(m_angularMotorDirection);
242 break; 247 break;
243 case Vehicle.LINEAR_FRICTION_TIMESCALE: 248 case Vehicle.LINEAR_FRICTION_TIMESCALE:
244 m_linearFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z); 249 m_linearFrictionTimescale = new Vector3(pValue.X, pValue.Y, pValue.Z);
@@ -358,10 +363,13 @@ namespace OpenSim.Region.Physics.BulletSPlugin
358 m_bankingMix = 1; 363 m_bankingMix = 1;
359 364
360 m_referenceFrame = Quaternion.Identity; 365 m_referenceFrame = Quaternion.Identity;
361 m_flags |= (VehicleFlag.NO_DEFLECTION_UP | VehicleFlag.LIMIT_ROLL_ONLY | VehicleFlag.LIMIT_MOTOR_UP); 366 m_flags &= ~(VehicleFlag.HOVER_WATER_ONLY
362 m_flags &= 367 | VehicleFlag.HOVER_TERRAIN_ONLY
363 ~(VehicleFlag.HOVER_WATER_ONLY | VehicleFlag.HOVER_TERRAIN_ONLY | 368 | VehicleFlag.HOVER_GLOBAL_HEIGHT
364 VehicleFlag.HOVER_GLOBAL_HEIGHT | VehicleFlag.HOVER_UP_ONLY); 369 | VehicleFlag.HOVER_UP_ONLY);
370 m_flags |= (VehicleFlag.NO_DEFLECTION_UP
371 | VehicleFlag.LIMIT_ROLL_ONLY
372 | VehicleFlag.LIMIT_MOTOR_UP);
365 break; 373 break;
366 case Vehicle.TYPE_CAR: 374 case Vehicle.TYPE_CAR:
367 m_linearMotorDirection = Vector3.Zero; 375 m_linearMotorDirection = Vector3.Zero;
@@ -521,8 +529,14 @@ namespace OpenSim.Region.Physics.BulletSPlugin
521 // Update any physical parameters based on this type. 529 // Update any physical parameters based on this type.
522 Refresh(); 530 Refresh();
523 531
524 m_linearMotor = new BSVMotor("LinearMotor", m_linearMotorTimescale, m_linearMotorDecayTimescale, m_linearFrictionTimescale, 1f); 532 m_linearMotor = new BSVMotor("LinearMotor", m_linearMotorTimescale,
533 m_linearMotorDecayTimescale, m_linearFrictionTimescale, 1f);
525 m_linearMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG DEBUG (enables detail logging) 534 m_linearMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG DEBUG (enables detail logging)
535 m_angularMotor = new BSVMotor("AngularMotor", m_angularMotorTimescale,
536 m_angularMotorDecayTimescale, m_angularFrictionTimescale, 1f);
537 m_angularMotor.PhysicsScene = PhysicsScene; // DEBUG DEBUG DEBUG (enables detail logging)
538
539 // m_bankingMotor = new BSVMotor("BankingMotor", ...);
526 } 540 }
527 541
528 // Some of the properties of this prim may have changed. 542 // Some of the properties of this prim may have changed.
@@ -577,26 +591,16 @@ namespace OpenSim.Region.Physics.BulletSPlugin
577 if (!IsActive) return; 591 if (!IsActive) return;
578 592
579 MoveLinear(pTimestep); 593 MoveLinear(pTimestep);
580 // Commented out for debug
581 MoveAngular(pTimestep); 594 MoveAngular(pTimestep);
582 // Prim.ApplyTorqueImpulse(-Prim.RotationalVelocity * m_vehicleMass, false); // DEBUG DEBUG
583 // Prim.ForceRotationalVelocity = -Prim.RotationalVelocity; // DEBUG DEBUG
584 595
585 LimitRotation(pTimestep); 596 LimitRotation(pTimestep);
586 597
587 // remember the position so next step we can limit absolute movement effects 598 // remember the position so next step we can limit absolute movement effects
588 m_lastPositionVector = Prim.ForcePosition; 599 m_lastPositionVector = Prim.ForcePosition;
589 600
590 VDetailLog("{0},BSDynamics.Step,frict={1},grav={2},inertia={3},mass={4}", // DEBUG DEBUG
591 Prim.LocalID,
592 BulletSimAPI.GetFriction2(Prim.PhysBody.ptr),
593 BulletSimAPI.GetGravity2(Prim.PhysBody.ptr),
594 Prim.Inertia,
595 m_vehicleMass
596 );
597 VDetailLog("{0},BSDynamics.Step,done,pos={1},force={2},velocity={3},angvel={4}", 601 VDetailLog("{0},BSDynamics.Step,done,pos={1},force={2},velocity={3},angvel={4}",
598 Prim.LocalID, Prim.ForcePosition, Prim.Force, Prim.ForceVelocity, Prim.RotationalVelocity); 602 Prim.LocalID, Prim.ForcePosition, Prim.Force, Prim.ForceVelocity, Prim.RotationalVelocity);
599 }// end Step 603 }
600 604
601 // Apply the effect of the linear motor. 605 // Apply the effect of the linear motor.
602 // Also does hover and float. 606 // Also does hover and float.
@@ -790,6 +794,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
790 } // end MoveLinear() 794 } // end MoveLinear()
791 795
792 // ======================================================================= 796 // =======================================================================
797 // =======================================================================
793 // Apply the effect of the angular motor. 798 // Apply the effect of the angular motor.
794 private void MoveAngular(float pTimestep) 799 private void MoveAngular(float pTimestep)
795 { 800 {
@@ -819,12 +824,10 @@ namespace OpenSim.Region.Physics.BulletSPlugin
819 m_angularMotorVelocity = Vector3.Zero; 824 m_angularMotorVelocity = Vector3.Zero;
820 } 825 }
821 826
822 #region Vertical attactor 827 Vector3 angularMotorContribution = m_angularMotor.Step(pTimestep);
823
824 Vector3 vertattr = Vector3.Zero;
825 Vector3 deflection = Vector3.Zero;
826 Vector3 banking = Vector3.Zero;
827 828
829 // ==================================================================
830 Vector3 verticalAttractionContribution = Vector3.Zero;
828 // If vertical attaction timescale is reasonable and we applied an angular force last time... 831 // If vertical attaction timescale is reasonable and we applied an angular force last time...
829 if (m_verticalAttractionTimescale < 300 && m_lastAngularVelocity != Vector3.Zero) 832 if (m_verticalAttractionTimescale < 300 && m_lastAngularVelocity != Vector3.Zero)
830 { 833 {
@@ -854,24 +857,23 @@ namespace OpenSim.Region.Physics.BulletSPlugin
854 // As the body rotates around the X axis, then verticalError.Y increases; Rotated around Y 857 // As the body rotates around the X axis, then verticalError.Y increases; Rotated around Y
855 // then .X increases, so change Body angular velocity X based on Y, and Y based on X. 858 // then .X increases, so change Body angular velocity X based on Y, and Y based on X.
856 // Z is not changed. 859 // Z is not changed.
857 vertattr.X = verticalError.Y; 860 verticalAttractionContribution.X = verticalError.Y;
858 vertattr.Y = - verticalError.X; 861 verticalAttractionContribution.Y = - verticalError.X;
859 vertattr.Z = 0f; 862 verticalAttractionContribution.Z = 0f;
860 863
861 // scaling appears better usingsquare-law 864 // scaling appears better usingsquare-law
862 Vector3 angularVelocity = Prim.ForceRotationalVelocity; 865 Vector3 angularVelocity = Prim.ForceRotationalVelocity;
863 float bounce = 1.0f - (m_verticalAttractionEfficiency * m_verticalAttractionEfficiency); 866 float bounce = 1.0f - (m_verticalAttractionEfficiency * m_verticalAttractionEfficiency);
864 vertattr.X += bounce * angularVelocity.X; 867 verticalAttractionContribution.X += bounce * angularVelocity.X;
865 vertattr.Y += bounce * angularVelocity.Y; 868 verticalAttractionContribution.Y += bounce * angularVelocity.Y;
866 869
867 VDetailLog("{0},MoveAngular,verticalAttraction,VAservo={1},effic={2},verticalError={3},bounce={4},vertattr={5}", 870 VDetailLog("{0},MoveAngular,verticalAttraction,VAservo={1},effic={2},verticalError={3},bounce={4},vertattr={5}",
868 Prim.LocalID, VAservo, m_verticalAttractionEfficiency, verticalError, bounce, vertattr); 871 Prim.LocalID, VAservo, m_verticalAttractionEfficiency, verticalError, bounce, verticalAttractionContribution);
869 872
870 } 873 }
871 #endregion // Vertical attactor
872
873 #region Deflection
874 874
875 // ==================================================================
876 Vector3 deflectionContribution = Vector3.Zero;
875 if (m_angularDeflectionEfficiency != 0) 877 if (m_angularDeflectionEfficiency != 0)
876 { 878 {
877 // Compute a scaled vector that points in the preferred axis (X direction) 879 // Compute a scaled vector that points in the preferred axis (X direction)
@@ -882,18 +884,16 @@ namespace OpenSim.Region.Physics.BulletSPlugin
882 Vector3 preferredAxisOfMotion = scaledDefaultDirection * Quaternion.Add(Prim.ForceOrientation, m_referenceFrame); 884 Vector3 preferredAxisOfMotion = scaledDefaultDirection * Quaternion.Add(Prim.ForceOrientation, m_referenceFrame);
883 885
884 // Scale by efficiency and timescale 886 // Scale by efficiency and timescale
885 deflection = (preferredAxisOfMotion * (m_angularDeflectionEfficiency) / m_angularDeflectionTimescale) * pTimestep; 887 deflectionContribution = (preferredAxisOfMotion * (m_angularDeflectionEfficiency) / m_angularDeflectionTimescale) * pTimestep;
886 888
887 VDetailLog("{0},MoveAngular,Deflection,perfAxis={1},deflection={2}", 889 VDetailLog("{0},MoveAngular,Deflection,perfAxis={1},deflection={2}",
888 Prim.LocalID, preferredAxisOfMotion, deflection); 890 Prim.LocalID, preferredAxisOfMotion, deflectionContribution);
889 // This deflection computation is not correct. 891 // This deflection computation is not correct.
890 deflection = Vector3.Zero; 892 deflectionContribution = Vector3.Zero;
891 } 893 }
892 894
893 #endregion 895 // ==================================================================
894 896 Vector3 bankingContribution = Vector3.Zero;
895 #region Banking
896
897 if (m_bankingEfficiency != 0) 897 if (m_bankingEfficiency != 0)
898 { 898 {
899 Vector3 dir = Vector3.One * Prim.ForceOrientation; 899 Vector3 dir = Vector3.One * Prim.ForceOrientation;
@@ -923,7 +923,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
923 } 923 }
924 else 924 else
925 { 925 {
926 banking.Z += (effSquared * (mult * mix)) * (m_angularMotorVelocity.X) * 4; 926 bankingContribution.Z += (effSquared * (mult * mix)) * (m_angularMotorVelocity.X) * 4;
927 } 927 }
928 928
929 //If they are colliding, we probably shouldn't shove the prim around... probably 929 //If they are colliding, we probably shouldn't shove the prim around... probably
@@ -941,22 +941,23 @@ namespace OpenSim.Region.Physics.BulletSPlugin
941 else if (bankingRot.X < -3) 941 else if (bankingRot.X < -3)
942 bankingRot.X = -3; 942 bankingRot.X = -3;
943 bankingRot *= Prim.ForceOrientation; 943 bankingRot *= Prim.ForceOrientation;
944 banking += bankingRot; 944 bankingContribution += bankingRot;
945 } 945 }
946 m_angularMotorVelocity.X *= m_bankingEfficiency == 1 ? 0.0f : 1 - m_bankingEfficiency; 946 m_angularMotorVelocity.X *= m_bankingEfficiency == 1 ? 0.0f : 1 - m_bankingEfficiency;
947 VDetailLog("{0},MoveAngular,Banking,bEff={1},angMotVel={2},effSq={3},mult={4},mix={5},banking={6}", 947 VDetailLog("{0},MoveAngular,Banking,bEff={1},angMotVel={2},effSq={3},mult={4},mix={5},banking={6}",
948 Prim.LocalID, m_bankingEfficiency, m_angularMotorVelocity, effSquared, mult, mix, banking); 948 Prim.LocalID, m_bankingEfficiency, m_angularMotorVelocity, effSquared, mult, mix, bankingContribution);
949 } 949 }
950 950
951 #endregion 951 // ==================================================================
952 952 m_lastVertAttractor = verticalAttractionContribution;
953 m_lastVertAttractor = vertattr;
954 953
955 // Sum velocities 954 // Sum velocities
956 m_lastAngularVelocity = m_angularMotorVelocity + vertattr + banking + deflection; 955 m_lastAngularVelocity = angularMotorContribution
957 956 + verticalAttractionContribution
958 #region Linear Motor Offset 957 + bankingContribution
958 + deflectionContribution;
959 959
960 // ==================================================================
960 //Offset section 961 //Offset section
961 if (m_linearMotorOffset != Vector3.Zero) 962 if (m_linearMotorOffset != Vector3.Zero)
962 { 963 {
@@ -972,8 +973,8 @@ namespace OpenSim.Region.Physics.BulletSPlugin
972 // 973 //
973 // The torque created is the linear velocity crossed with the offset 974 // The torque created is the linear velocity crossed with the offset
974 975
975 // NOTE: this computation does should be in the linear section 976 // TODO: this computation should be in the linear section
976 // because there we know the impulse being applied. 977 // because that is where we know the impulse being applied.
977 Vector3 torqueFromOffset = Vector3.Zero; 978 Vector3 torqueFromOffset = Vector3.Zero;
978 // torqueFromOffset = Vector3.Cross(m_linearMotorOffset, appliedImpulse); 979 // torqueFromOffset = Vector3.Cross(m_linearMotorOffset, appliedImpulse);
979 if (float.IsNaN(torqueFromOffset.X)) 980 if (float.IsNaN(torqueFromOffset.X))
@@ -987,8 +988,8 @@ namespace OpenSim.Region.Physics.BulletSPlugin
987 VDetailLog("{0},BSDynamic.MoveAngular,motorOffset,applyTorqueImpulse={1}", Prim.LocalID, torqueFromOffset); 988 VDetailLog("{0},BSDynamic.MoveAngular,motorOffset,applyTorqueImpulse={1}", Prim.LocalID, torqueFromOffset);
988 } 989 }
989 990
990 #endregion 991 // ==================================================================
991 992 // NO_DEFLECTION_UP says angular motion should not add any pitch or roll movement
992 if ((m_flags & (VehicleFlag.NO_DEFLECTION_UP)) != 0) 993 if ((m_flags & (VehicleFlag.NO_DEFLECTION_UP)) != 0)
993 { 994 {
994 m_lastAngularVelocity.X = 0; 995 m_lastAngularVelocity.X = 0;
@@ -996,6 +997,7 @@ namespace OpenSim.Region.Physics.BulletSPlugin
996 VDetailLog("{0},MoveAngular,noDeflectionUp,lastAngular={1}", Prim.LocalID, m_lastAngularVelocity); 997 VDetailLog("{0},MoveAngular,noDeflectionUp,lastAngular={1}", Prim.LocalID, m_lastAngularVelocity);
997 } 998 }
998 999
1000 // ==================================================================
999 if (m_lastAngularVelocity.ApproxEquals(Vector3.Zero, 0.01f)) 1001 if (m_lastAngularVelocity.ApproxEquals(Vector3.Zero, 0.01f))
1000 { 1002 {
1001 m_lastAngularVelocity = Vector3.Zero; // Reduce small value to zero. 1003 m_lastAngularVelocity = Vector3.Zero; // Reduce small value to zero.
@@ -1008,18 +1010,20 @@ namespace OpenSim.Region.Physics.BulletSPlugin
1008 // The above calculates the absolute angular velocity needed. Angular velocity is massless. 1010 // The above calculates the absolute angular velocity needed. Angular velocity is massless.
1009 // Since we are stuffing the angular velocity directly into the object, the computed 1011 // Since we are stuffing the angular velocity directly into the object, the computed
1010 // velocity needs to be scaled by the timestep. 1012 // velocity needs to be scaled by the timestep.
1011 Vector3 applyAngularForce = ((m_lastAngularVelocity * pTimestep) - Prim.ForceRotationalVelocity); 1013 // Also remove any motion that is on the object so added motion is only from vehicle.
1014 Vector3 applyAngularForce = ((m_lastAngularVelocity * pTimestep)
1015 - Prim.ForceRotationalVelocity);
1012 Prim.ForceRotationalVelocity = applyAngularForce; 1016 Prim.ForceRotationalVelocity = applyAngularForce;
1013 1017
1014 // Decay the angular movement for next time 1018 VDetailLog("{0},MoveAngular,done,newRotVel={1},lastAngular={2}",
1015 Vector3 decayamount = (Vector3.One / m_angularFrictionTimescale) * pTimestep; 1019 Prim.LocalID, applyAngularForce, m_lastAngularVelocity);
1016 m_lastAngularVelocity *= Vector3.One - decayamount;
1017
1018 VDetailLog("{0},MoveAngular,done,newRotVel={1},decay={2},lastAngular={3}",
1019 Prim.LocalID, applyAngularForce, decayamount, m_lastAngularVelocity);
1020 } 1020 }
1021 } //end MoveAngular 1021 }
1022 1022
1023 // This is from previous instantiations of XXXDynamics.cs.
1024 // Applies roll reference frame.
1025 // TODO: is this the right way to separate the code to do this operation?
1026 // Should this be in MoveAngular()?
1023 internal void LimitRotation(float timestep) 1027 internal void LimitRotation(float timestep)
1024 { 1028 {
1025 Quaternion rotq = Prim.ForceOrientation; 1029 Quaternion rotq = Prim.ForceOrientation;