1 // FGAICarrier - FGAIShip-derived class creates an AI aircraft carrier
3 // Written by David Culp, started October 2004.
4 // - davidculp2@comcast.net
6 // This program is free software; you can redistribute it and/or
7 // modify it under the terms of the GNU General Public License as
8 // published by the Free Software Foundation; either version 2 of the
9 // License, or (at your option) any later version.
11 // This program is distributed in the hope that it will be useful, but
12 // WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 // General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27 #include <simgear/math/point3d.hxx>
28 #include <simgear/math/sg_geodesy.hxx>
30 #include <Main/util.hxx>
31 #include <Main/viewer.hxx>
33 #include "AICarrier.hxx"
36 #include "AIScenario.hxx"
39 FGAICarrier::FGAICarrier(FGAIManager* mgr) : FGAIShip(mgr) {
40 _type_str = "carrier";
44 FGAICarrier::~FGAICarrier() {
47 void FGAICarrier::setSolidObjects(const list<string>& so) {
51 void FGAICarrier::setWireObjects(const list<string>& wo) {
55 void FGAICarrier::setCatapultObjects(const list<string>& co) {
56 catapult_objects = co;
59 void FGAICarrier::setParkingPositions(const list<ParkPosition>& p) {
63 void FGAICarrier::setSign(const string& s) {
67 void FGAICarrier::setFlolsOffset(const Point3D& off) {
71 void FGAICarrier::getVelocityWrtEarth(sgVec3 v) {
72 sgCopyVec3(v, vel_wrt_earth );
75 void FGAICarrier::update(double dt) {
79 // Update the velocity information stored in those nodes.
80 double v_north = 0.51444444*speed*cos(hdg * SGD_DEGREES_TO_RADIANS);
81 double v_east = 0.51444444*speed*sin(hdg * SGD_DEGREES_TO_RADIANS);
83 double sin_lat = sin(pos.lat() * SGD_DEGREES_TO_RADIANS);
84 double cos_lat = cos(pos.lat() * SGD_DEGREES_TO_RADIANS);
85 double sin_lon = sin(pos.lon() * SGD_DEGREES_TO_RADIANS);
86 double cos_lon = cos(pos.lon() * SGD_DEGREES_TO_RADIANS);
87 sgSetVec3( vel_wrt_earth,
88 - cos_lon*sin_lat*v_north - sin_lon*v_east,
89 - sin_lon*sin_lat*v_north + cos_lon*v_east,
94 bool FGAICarrier::init() {
95 if (!FGAIShip::init())
98 // process the 3d model here
99 // mark some objects solid, mark the wires ...
101 // The model should be used for altitude computations.
102 // To avoid that every detail in a carrier 3D model will end into
103 // the aircraft local cache, only set the HOT traversal bit on
105 ssgEntity *sel = aip.getSceneGraph();
106 // Clear the HOT traversal flag
108 // Selectively set that flag again for wires/cats/solid objects.
109 // Attach a pointer to this carrier class to those objects.
110 mark_wires(sel, wire_objects);
111 mark_cat(sel, catapult_objects);
112 mark_solid(sel, solid_objects);
117 void FGAICarrier::bind() {
120 props->tie("controls/flols/source-lights",
121 SGRawValuePointer<int>(&source));
122 props->tie("controls/flols/distance-m",
123 SGRawValuePointer<double>(&dist));
124 props->tie("controls/flols/angle-degs",
125 SGRawValuePointer<double>(&angle));
126 props->setBoolValue("controls/flols/cut-lights", false);
127 props->setBoolValue("controls/flols/wave-off-lights", false);
128 props->setBoolValue("controls/flols/cond-datum-lights", true);
129 props->setBoolValue("controls/crew", false);
131 props->setStringValue("sign", sign.c_str());
134 void FGAICarrier::unbind() {
136 props->untie("controls/flols/source-lights");
137 props->untie("controls/flols/distance-m");
138 props->untie("controls/flols/angle-degs");
141 bool FGAICarrier::getParkPosition(const string& id, Point3D& geodPos,
142 double& hdng, sgdVec3 uvw)
144 list<ParkPosition>::iterator it = ppositions.begin();
145 while (it != ppositions.end()) {
146 // Take either the specified one or the first one ...
147 if ((*it).name == id || id.empty()) {
148 ParkPosition ppos = *it;
149 geodPos = getGeocPosAt(ppos.offset);
150 hdng = hdg + ppos.heading_deg;
151 double shdng = sin(ppos.heading_deg * SGD_DEGREES_TO_RADIANS);
152 double chdng = cos(ppos.heading_deg * SGD_DEGREES_TO_RADIANS);
153 double speed_fps = speed*1.6878099;
154 sgdSetVec3(uvw, chdng*speed_fps, shdng*speed_fps, 0);
163 void FGAICarrier::mark_nohot(ssgEntity* e) {
164 if (e->isAKindOf(ssgTypeBranch())) {
165 ssgBranch* br = (ssgBranch*)e;
167 for ( kid = br->getKid(0); kid != NULL ; kid = br->getNextKid() )
170 br->clrTraversalMaskBits(SSGTRAV_HOT);
172 } else if (e->isAKindOf(ssgTypeLeaf())) {
174 e->clrTraversalMaskBits(SSGTRAV_HOT);
179 bool FGAICarrier::mark_wires(ssgEntity* e, const list<string>& wire_objects, bool mark) {
181 if (e->isAKindOf(ssgTypeBranch())) {
182 ssgBranch* br = (ssgBranch*)e;
185 list<string>::const_iterator it;
186 for (it = wire_objects.begin(); it != wire_objects.end(); ++it)
187 mark = mark || (e->getName() && (*it) == e->getName());
189 for ( kid = br->getKid(0); kid != NULL ; kid = br->getNextKid() )
190 found = mark_wires(kid, wire_objects, mark) || found;
193 br->setTraversalMaskBits(SSGTRAV_HOT);
195 } else if (e->isAKindOf(ssgTypeLeaf())) {
196 list<string>::const_iterator it;
197 for (it = wire_objects.begin(); it != wire_objects.end(); ++it) {
198 if (mark || (e->getName() && (*it) == e->getName())) {
199 e->setTraversalMaskBits(SSGTRAV_HOT);
200 ssgBase* ud = e->getUserData();
202 FGAICarrierHardware* ch = dynamic_cast<FGAICarrierHardware*>(ud);
204 SG_LOG(SG_GENERAL, SG_WARN,
205 "AICarrier: Carrier hardware gets marked twice!\n"
206 " You have propably a whole branch marked as"
207 " a wire which also includes other carrier hardware."
210 SG_LOG(SG_GENERAL, SG_ALERT,
211 "AICarrier: Found user data attached to a leaf node which "
212 "should be marked as a wire!\n ****Skipping!****");
215 e->setUserData( FGAICarrierHardware::newWire( this ) );
216 ssgLeaf *l = (ssgLeaf*)e;
217 if ( l->getNumLines() != 1 ) {
218 SG_LOG(SG_GENERAL, SG_ALERT,
219 "AICarrier: Found wires not modelled with exactly one line!");
229 bool FGAICarrier::mark_solid(ssgEntity* e, const list<string>& solid_objects, bool mark) {
231 if (e->isAKindOf(ssgTypeBranch())) {
232 ssgBranch* br = (ssgBranch*)e;
235 list<string>::const_iterator it;
236 for (it = solid_objects.begin(); it != solid_objects.end(); ++it)
237 mark = mark || (e->getName() && (*it) == e->getName());
239 for ( kid = br->getKid(0); kid != NULL ; kid = br->getNextKid() )
240 found = mark_solid(kid, solid_objects, mark) || found;
243 br->setTraversalMaskBits(SSGTRAV_HOT);
245 } else if (e->isAKindOf(ssgTypeLeaf())) {
246 list<string>::const_iterator it;
247 for (it = solid_objects.begin(); it != solid_objects.end(); ++it) {
248 if (mark || (e->getName() && (*it) == e->getName())) {
249 e->setTraversalMaskBits(SSGTRAV_HOT);
250 ssgBase* ud = e->getUserData();
252 FGAICarrierHardware* ch = dynamic_cast<FGAICarrierHardware*>(ud);
254 SG_LOG(SG_GENERAL, SG_WARN,
255 "AICarrier: Carrier hardware gets marked twice!\n"
256 " You have propably a whole branch marked solid"
257 " which also includes other carrier hardware."
260 SG_LOG(SG_GENERAL, SG_ALERT,
261 "AICarrier: Found user data attached to a leaf node which "
262 "should be marked solid!\n ****Skipping!****");
265 e->setUserData( FGAICarrierHardware::newSolid( this ) );
274 bool FGAICarrier::mark_cat(ssgEntity* e, const list<string>& cat_objects, bool mark) {
276 if (e->isAKindOf(ssgTypeBranch())) {
277 ssgBranch* br = (ssgBranch*)e;
280 list<string>::const_iterator it;
281 for (it = cat_objects.begin(); it != cat_objects.end(); ++it)
282 mark = mark || (e->getName() && (*it) == e->getName());
284 for ( kid = br->getKid(0); kid != NULL ; kid = br->getNextKid() )
285 found = mark_cat(kid, cat_objects, mark) || found;
288 br->setTraversalMaskBits(SSGTRAV_HOT);
290 } else if (e->isAKindOf(ssgTypeLeaf())) {
291 list<string>::const_iterator it;
292 for (it = cat_objects.begin(); it != cat_objects.end(); ++it) {
293 if (mark || (e->getName() && (*it) == e->getName())) {
294 e->setTraversalMaskBits(SSGTRAV_HOT);
295 ssgBase* ud = e->getUserData();
297 FGAICarrierHardware* ch = dynamic_cast<FGAICarrierHardware*>(ud);
299 SG_LOG(SG_GENERAL, SG_WARN,
300 "AICarrier: Carrier hardware gets marked twice!\n"
301 "You have probably a whole branch marked as"
302 "a catapult which also includes other carrier hardware."
305 SG_LOG(SG_GENERAL, SG_ALERT,
306 "AICarrier: Found user data attached to a leaf node which "
307 "should be marked as a catapult!\n ****Skipping!****");
310 e->setUserData( FGAICarrierHardware::newCatapult( this ) );
311 ssgLeaf *l = (ssgLeaf*)e;
312 if ( l->getNumLines() != 1 ) {
313 SG_LOG(SG_GENERAL, SG_ALERT,
314 "AICarrier: Found a cat not modelled with exactly "
317 // Now some special code to make sure the cat points in the right
318 // direction. The 0 index must be the backward end, the 1 index
320 // Forward is positive x-direction in our 3D model, also the model
321 // as such is flattened when it is loaded, so we do not need to
322 // care for transforms ...
324 l->getLine(0, v, v+1 );
326 for (int k=0; k<2; ++k)
327 sgCopyVec3( ends[k], l->getVertex( v[k] ) );
329 // When the 1 end is behind the 0 end, swap the coordinates.
330 if (ends[0][0] < ends[1][0]) {
331 sgCopyVec3( l->getVertex( v[0] ), ends[1] );
332 sgCopyVec3( l->getVertex( v[1] ), ends[0] );
344 void FGAICarrier::UpdateFlols( double dt) {
354 double flolsXYZ[3], eyeXYZ[3];
355 double lat, lon, alt;
359 /* cout << "x_offset " << flols_x_offset
360 << " y_offset " << flols_y_offset
361 << " z_offset " << flols_z_offset << endl;
363 cout << "roll " << roll
364 << " heading " << hdg
365 << " pitch " << pitch << endl;
367 cout << "carrier lon " << pos[0]
369 << " alt " << pos[2] << endl;*/
371 // set the Flols intitial position to the carrier position
375 /* cout << "flols lon " << flolspos[0]
376 << " lat " << flolspos[1]
377 << " alt " << flolspos[2] << endl;*/
379 // set the offsets in metres
381 /* cout << "flols_x_offset " << flols_x_offset << endl
382 << "flols_y_offset " << flols_y_offset << endl
383 << "flols_z_offset " << flols_z_offset << endl;*/
385 in[0] = flols_off.x();
386 in[1] = flols_off.y();
387 in[2] = flols_off.z();
389 // pre-process the trig functions
391 cosRx = cos(roll * SG_DEGREES_TO_RADIANS);
392 sinRx = sin(roll * SG_DEGREES_TO_RADIANS);
393 cosRy = cos(pitch * SG_DEGREES_TO_RADIANS);
394 sinRy = sin(pitch * SG_DEGREES_TO_RADIANS);
395 cosRz = cos(hdg * SG_DEGREES_TO_RADIANS);
396 sinRz = sin(hdg * SG_DEGREES_TO_RADIANS);
398 // set up the transform matrix
400 trans[0][0] = cosRy * cosRz;
401 trans[0][1] = -1 * cosRx * sinRz + sinRx * sinRy * cosRz ;
402 trans[0][2] = sinRx * sinRz + cosRx * sinRy * cosRz;
404 trans[1][0] = cosRy * sinRz;
405 trans[1][1] = cosRx * cosRz + sinRx * sinRy * sinRz;
406 trans[1][2] = -1 * sinRx * cosRx + cosRx * sinRy * sinRz;
408 trans[2][0] = -1 * sinRy;
409 trans[2][1] = sinRx * cosRy;
410 trans[2][2] = cosRx * cosRy;
412 // multiply the input and transform matrices
414 out[0] = in[0] * trans[0][0] + in[1] * trans[0][1] + in[2] * trans[0][2];
415 out[1] = in[0] * trans[1][0] + in[1] * trans[1][1] + in[2] * trans[1][2];
416 out[2] = in[0] * trans[2][0] + in[1] * trans[2][1] + in[2] * trans[2][2];
418 // convert meters to ft to degrees of latitude
419 out[0] = (out[0] * 3.28083989501) /(366468.96 - 3717.12 * cos(flolspos[0] * SG_DEGREES_TO_RADIANS));
421 // convert meters to ft to degrees of longitude
422 out[1] = (out[1] * 3.28083989501)/(365228.16 * cos(flolspos[1] * SG_DEGREES_TO_RADIANS));
424 //print out the result
425 /* cout << "lat adjust deg" << out[0]
426 << " lon adjust deg " << out[1]
427 << " alt adjust m " << out[2] << endl;*/
429 // adjust Flols position
430 flolspos[0] += out[0];
431 flolspos[1] += out[1];
432 flolspos[2] += out[2];
434 // convert flols position to cartesian co-ordinates
436 sgGeodToCart(flolspos[1] * SG_DEGREES_TO_RADIANS,
437 flolspos[0] * SG_DEGREES_TO_RADIANS,
438 flolspos[2] , flolsXYZ );
441 /* cout << "flols X " << flolsXYZ[0]
442 << " Y " << flolsXYZ[1]
443 << " Z " << flolsXYZ[2] << endl;
445 // check the conversion
447 sgCartToGeod(flolsXYZ, &lat, &lon, &alt);
449 cout << "flols check lon " << lon
451 << " alt " << alt << endl; */
453 //get the current position of the pilot's eyepoint (cartesian cordinates)
455 sgdCopyVec3( eyeXYZ, globals->get_current_view()->get_absolute_view_pos() );
457 /* cout << "Eye_X " << eyeXYZ[0]
458 << " Eye_Y " << eyeXYZ[1]
459 << " Eye_Z " << eyeXYZ[2] << endl; */
461 sgCartToGeod(eyeXYZ, &lat, &lon, &alt);
463 eyepos[0] = lon * SG_RADIANS_TO_DEGREES;
464 eyepos[1] = lat * SG_RADIANS_TO_DEGREES;
467 /* cout << "eye lon " << eyepos[0]
468 << " eye lat " << eyepos[1]
469 << " eye alt " << eyepos[2] << endl; */
471 //calculate the ditance from eye to flols
473 dist = sgdDistanceVec3( flolsXYZ, eyeXYZ );
475 //apply an index error
479 //cout << "distance " << dist << endl;
482 // calculate height above FLOLS
483 double y = eyepos[2] - flolspos[2];
485 // calculate the angle from the flols to eye
486 // above the horizontal
490 angle = asin( y / dist );
495 angle *= SG_RADIANS_TO_DEGREES;
498 // cout << " height " << y << " angle " << angle ;
500 // set the value of source
502 if ( angle <= 4.35 && angle > 4.01 )
504 else if ( angle <= 4.01 && angle > 3.670 )
506 else if ( angle <= 3.670 && angle > 3.330 )
508 else if ( angle <= 3.330 && angle > 2.990 )
510 else if ( angle <= 2.990 && angle > 2.650 )
512 else if ( angle <= 2.650 )
517 // cout << " source " << source << endl;
522 int FGAICarrierHardware::unique_id = 1;