1 #include <Main/fg_props.hxx>
2 #include "RigidBody.hpp"
3 
4 namespace yasim {
5 
RigidBody()6 RigidBody::RigidBody()
7 {
8     // Allocate space for 16 masses initially.  More space will be
9     // allocated dynamically.
10     _nMasses = 0;
11     _massesAlloced = 16;
12     _masses = new Mass[_massesAlloced];
13     _gyro[0] = _gyro[1] = _gyro[2] = 0;
14     _spin[0] = _spin[1] = _spin[2] = 0;
15     _bodyN = fgGetNode("/fdm/yasim/model/masses", true);
16 }
17 
~RigidBody()18 RigidBody::~RigidBody()
19 {
20     delete[] _masses;
21 }
22 
23 /// add new point mass to body
24 /// isStatic: set to true for masses that do not change per iteration (everything but fuel?)
addMass(float mass,const float * pos,bool isStatic)25 int RigidBody::addMass(float mass, const float* pos, bool isStatic)
26 {
27     // If out of space, reallocate twice as much
28     if(_nMasses == _massesAlloced) {
29         _massesAlloced *= 2;
30         Mass *m2 = new Mass[_massesAlloced];
31         int i;
32         for(i=0; i<_nMasses; i++)
33             m2[i] = _masses[i];
34         delete[] _masses;
35         _masses = m2;
36     }
37     setMass(_nMasses, mass, pos, isStatic);
38     return _nMasses++;
39 }
40 
41 /// change mass
42 /// handle: returned by addMass
setMass(int handle,float mass)43 void RigidBody::setMass(int handle, float mass)
44 {
45     if (_masses[handle].m  == mass)
46       return;
47     _masses[handle].m = mass;
48     // if static mass is changed, reset pre-calculated mass
49     // may apply to weights like cargo, pax, that usually do not change with FDM rate
50     if (_masses[handle].isStatic)
51       _staticMass.m = 0;
52     if (_bodyN != 0)
53       _bodyN->getChild("mass", handle, true)->getNode("mass", true)->setFloatValue(mass);
54 }
55 
setMass(int handle,float mass,const float * pos,bool isStatic)56 void RigidBody::setMass(int handle, float mass, const float* pos, bool isStatic)
57 {
58     _masses[handle].isStatic = isStatic;
59     Math::set3(pos, _masses[handle].p);
60     setMass(handle, mass);
61     if (_bodyN != 0) {
62       SGPropertyNode_ptr n = _bodyN->getChild("mass", handle, true);
63       n->getNode("isStatic", true)->setValue(isStatic);
64       n->getNode("pos-x", true)->setFloatValue(pos[0]);
65       n->getNode("pos-y", true)->setFloatValue(pos[1]);
66       n->getNode("pos-z", true)->setFloatValue(pos[2]);
67     }
68 }
69 
getMassPosition(int handle,float * out) const70 void RigidBody::getMassPosition(int handle, float* out) const
71 {
72     Math::set3(_masses[handle].p, out);
73 }
74 
75 // Calcualtes the rotational velocity of a particular point.  All
76 // coordinates are local!
pointVelocity(const float * pos,const float * rot,float * out)77 void RigidBody::pointVelocity(const float* pos, const float* rot, float* out)
78 {
79     Math::sub3(pos, _cg, out);   //  out = pos-cg
80     Math::cross3(rot, out, out); //      = rot cross (pos-cg)
81 }
82 
_recalcStatic()83 void RigidBody::_recalcStatic()
84 {
85     // aggregate all masses that do not change (e.g. fuselage, wings) into one point mass
86     _staticMass.m = 0;
87     Math::zero3(_staticMass.p);
88     int i;
89     int s = 0;
90     for(i=0; i<_nMasses; i++) {
91         if (_masses[i].isStatic) {
92             s++;
93             float mass = _masses[i].m;
94             _staticMass.m += mass;
95             float momentum[3];
96             Math::mul3(mass, _masses[i].p, momentum);
97             Math::add3(momentum, _staticMass.p, _staticMass.p);
98         }
99     }
100     Math::mul3(1/_staticMass.m, _staticMass.p, _staticMass.p);
101     if (_bodyN != 0) {
102         _bodyN->getNode("aggregated-mass", true)->setFloatValue(_staticMass.m);
103         _bodyN->getNode("aggregated-count", true)->setIntValue(s);
104         _bodyN->getNode("aggregated-pos-x", true)->setFloatValue(_staticMass.p[0]);
105         _bodyN->getNode("aggregated-pos-y", true)->setFloatValue(_staticMass.p[1]);
106         _bodyN->getNode("aggregated-pos-z", true)->setFloatValue(_staticMass.p[2]);
107     }
108     // Now the inertia tensor:
109     for(i=0; i<9; i++)
110         _tI_static[i] = 0;
111 
112     for(i=0; i<_nMasses; i++) {
113         if (_masses[i].isStatic) {
114             float m = _masses[i].m;
115 
116             float x = _masses[i].p[0] - _staticMass.p[0];
117             float y = _masses[i].p[1] - _staticMass.p[1];
118             float z = _masses[i].p[2] - _staticMass.p[2];
119 
120             float xy = m*x*y; float yz = m*y*z; float zx = m*z*x;
121             float x2 = m*x*x; float y2 = m*y*y; float z2 = m*z*z;
122 
123             // tensor is symmetric, so we can save some calculations in the loop
124             _tI_static[0] += y2+z2;  _tI_static[1] -=    xy;  _tI_static[2] -=    zx;
125                                      _tI_static[4] += x2+z2;  _tI_static[5] -=    yz;
126                                                               _tI_static[8] += x2+y2;
127         }
128     }
129     // copy symmetric elements
130     _tI_static[3] = _tI_static[1];
131     _tI_static[6] = _tI_static[2];
132     _tI_static[7] = _tI_static[5];
133 }
134 
135 /// calculate the total mass, centre of gravity and inertia tensor
136 /**
137   recalc is used when compiling the model but more important it is called in
138   Model::iterate() e.g. at FDM rate (120 Hz)
139   We can save some CPU due to the symmetry of the tensor and by aggregating
140   masses that do not change during flight.
141  */
recalc()142 void RigidBody::recalc()
143 {
144     //aggregate static masses into one mass
145     if (_staticMass.m == 0) _recalcStatic();
146 
147     // Calculate the c.g and total mass
148     // init with pre-calculated static mass
149     _totalMass = _staticMass.m;
150     Math::mul3(_staticMass.m, _staticMass.p, _cg);
151     int i;
152     for(i=0; i<_nMasses; i++) {
153         // only masses we did not aggregate
154         if (!_masses[i].isStatic) {
155             float mass = _masses[i].m;
156             _totalMass += mass;
157             float momentum[3];
158             Math::mul3(mass, _masses[i].p, momentum);
159             Math::add3(momentum, _cg, _cg);
160         }
161     }
162     Math::mul3(1/_totalMass, _cg, _cg);
163 
164     // Now the inertia tensor:
165     for(i=0; i<9; i++)
166         _tI[i] = _tI_static[i];
167 
168     for(i=0; i<_nMasses; i++) {
169         if (!_masses[i].isStatic) {
170             float m = _masses[i].m;
171 
172             float x = _masses[i].p[0] - _cg[0];
173             float y = _masses[i].p[1] - _cg[1];
174             float z = _masses[i].p[2] - _cg[2];
175             float mx = m*x;
176             float my = m*y;
177             float mz = m*z;
178 
179             float xy = mx*y; float yz = my*z; float zx = mz*x;
180             float x2 = mx*x; float y2 = my*y; float z2 = mz*z;
181 
182             _tI[0] += y2+z2;  _tI[1] -=    xy;  _tI[2] -=    zx;
183                               _tI[4] += x2+z2;  _tI[5] -=    yz;
184                                                 _tI[8] += x2+y2;
185         }
186     }
187     // copy symmetric elements
188     _tI[3] = _tI[1];
189     _tI[6] = _tI[2];
190     _tI[7] = _tI[5];
191 
192     //calculate inverse
193     Math::invert33_sym(_tI, _invI);
194 }
195 
reset()196 void RigidBody::reset()
197 {
198     Math::zero3(_torque);
199     Math::zero3(_force);
200 }
201 
addForce(const float * pos,const float * force)202 void RigidBody::addForce(const float* pos, const float* force)
203 {
204     addForce(force);
205 
206     // For a force F at position X, the torque about the c.g C is:
207     // torque = F cross (C - X)
208     float v[3], t[3];
209     Math::sub3(_cg, pos, v);
210     Math::cross3(force, v, t);
211     addTorque(t);
212 }
213 
getAccel(const float * pos,float * accelOut) const214 void RigidBody::getAccel(const float* pos, float* accelOut) const
215 {
216     getAccel(accelOut);
217 
218     // Turn the "spin" vector into a normalized spin axis "a" and a
219     // radians/sec scalar "rate".
220     float a[3];
221     float rate = Math::mag3(_spin);
222     Math::set3(_spin, a);
223     if (rate !=0 )
224         Math::mul3(1/rate, a, a);
225     //an else branch is not neccesary. a, which is a=(0,0,0) in the else case, is only used in a dot product
226     float v[3];
227     Math::sub3(_cg, pos, v);             // v = cg - pos
228     Math::mul3(Math::dot3(v, a), a, a);  // a = a * (v dot a)
229     Math::add3(v, a, v);                 // v = v + a
230 
231     // Now v contains the vector from pos to the rotation axis.
232     // Multiply by the square of the rotation rate to get the linear
233     // acceleration.
234     Math::mul3(rate*rate, v, v);
235     Math::add3(v, accelOut, accelOut);
236 }
237 
getAngularAccel(float * accelOut) const238 void RigidBody::getAngularAccel(float* accelOut) const
239 {
240     // Compute "tau" as the externally applied torque, plus the
241     // counter-torque due to the internal gyro.
242     float tau[3]; // torque
243     Math::cross3(_gyro, _spin, tau);
244     Math::add3(_torque, tau, tau);
245 
246     // Now work the equation of motion.  Use "v" as a notational
247     // shorthand, as the value isn't an acceleration until the end.
248     float *v = accelOut;
249     Math::vmul33(_tI, _spin, v);  // v = I*omega
250     Math::cross3(_spin, v, v);   // v = omega X I*omega
251     Math::add3(tau, v, v);       // v = tau + (omega X I*omega)
252     Math::vmul33(_invI, v, v);   // v = invI*(tau + (omega X I*omega))
253 }
254 
getInertiaMatrix(float * inertiaOut) const255 void RigidBody::getInertiaMatrix(float* inertiaOut) const
256 {
257     // valid only after a call to RigidBody::recalc()
258     // See comment at top of RigidBody.hpp on units.
259     for(int i=0;i<9;i++)
260     {
261        inertiaOut[i] = _tI[i];
262     }
263 }
264 
265 }; // namespace yasim
266