void Robots::AddRobot(std::string robotfilename) { Robot* robot = new Robot(); // robot->fi = new ModRobotInterface(); robot->fi->loadAirplane(robotfilename.c_str(), (FDMEnviroment*)0, (SimpleXMLTransfer*)0); if (robot->fi->robot) { SimpleXMLTransfer* header = robot->fi->robot->GetHeader(); std::string filename = FileSysTools::getDataPath(header->getString("airplane.file")); SimpleXMLTransfer* xml = new SimpleXMLTransfer(filename); XMLModelFile::SetGraphics(xml, header->getInt("airplane.graphics")); SimpleXMLTransfer* graphics = XMLModelFile::getGraphics(xml); // robot->vis_id = Video::new_visualization("objects/" + graphics->attribute("model"), "textures", CRRCMath::Vector3(), // todo xml); list.push_back(robot); } }
void T_AxisMapper::init(SimpleXMLTransfer* cfgfile, std::string childname) { #if DEBUG_TX_INTERFACE > 0 printf("T_AxisMapper::init(cfg, child)\n"); printf(" <-- %s\n", childname.c_str()); #endif SimpleXMLTransfer* inter; SimpleXMLTransfer* bindings; SimpleXMLTransfer* group; SimpleXMLTransfer* item; child_in_cfg = childname; // try to load config try { inter = cfgfile->getChild(childname, true); bindings = inter->getChild("bindings", true); group = bindings->getChild("axes", true); for (int i = T_AxisMapper::AILERON; i <= T_AxisMapper::PITCH; i++) { int default_axis = -1; float default_polarity = 1.0; // special handling for some default values if (i == T_AxisMapper::AILERON) { default_axis = 0; } else if (i == T_AxisMapper::ELEVATOR) { default_axis = 1; if (iface->inputMethod() != T_TX_Interface::eIM_joystick) { default_polarity = -1.0; } } item = group->getChild(Global::inputDev->AxisStringsXML[i], true); c_func[i] = item->attributeAsInt("axis", default_axis); c_inv[i] = item->attributeAsDouble("polarity", default_polarity); } std::string radio = strU(bindings->attribute("radio_type", RadioTypeStrings[CUSTOM])); for (int n=0; n < NR_OF_RADIO_TYPES; n++) { if (radio.compare(strU(RadioTypeStrings[n])) == 0) { setRadioType(n); } } } catch (XMLException e) { fprintf(stderr, "*** T_AxisMapper: XMLException: %s\n", e.what()); } }
void T_AxisMapper::putBackIntoCfg(SimpleXMLTransfer* cfgfile) { #if DEBUG_TX_INTERFACE > 0 printf("T_AxisMapper::putBackIntoCfg(SimpleXMLTransfer* config)\n"); printf(" --> %s\n", child_in_cfg.c_str()); #endif SimpleXMLTransfer* item; SimpleXMLTransfer* group; SimpleXMLTransfer* item2; try { item = cfgfile->getChild(child_in_cfg); group = item->getChild("bindings.axes"); for (int i = T_AxisMapper::AILERON; i <= T_AxisMapper::PITCH; i++) { item2 = group->getChild(Global::inputDev->AxisStringsXML[i], true); item2->setAttributeOverwrite("axis", c_func[i]); item2->setAttributeOverwrite("polarity", doubleToString(c_inv[i])); } item2 = item->getChild("bindings"); item2->setAttributeOverwrite("radio_type", RadioTypeStrings[radio_type]); } catch (XMLException e) { fprintf(stderr, "*** T_AxisMapper: XMLException: %s\n", e.what()); } }
void Power::Propeller::ReloadParams(SimpleXMLTransfer* xml) { Gearing::ReloadParams(xml); SimpleXMLTransfer* prop; bool fExtern = true; if (xml->indexOfAttribute("filename") >= 0) { prop = new SimpleXMLTransfer(FileSysTools::getDataPath("models/propeller/" + xml->getString("filename") + ".xml", true)); } else { prop = xml; fExtern = false; } // Der Sturz wird in jedem Fall aus der Modelldatei gelesen, ansonsten muss man ja eine // Propellerdatei fuer jeden Sturz extra haben. CalcDownthrust(xml); D = prop->getDouble("D"); H = prop->getDouble("H"); J = prop->getDouble("J"); omega_fold = prop->attributeAsDouble("n_fold", -1)*2*M_PI; std::cout << " Propeller: D=" << D << " m, H=" << H << " m, J=" << J << " kg m^2"; if (fExtern) delete prop; }
void Power::Propeller::CalcDownthrust(SimpleXMLTransfer* xml) { int idx = xml->indexOfChild("pos"); if (idx < 0) { dirThrust = CRRCMath::Vector3(1, 0, 0); mulForce = CRRCMath::Vector3(1, 0, 0); mulMoment = CRRCMath::Vector3(0, 0, 0); } else { SimpleXMLTransfer* sxtpos = xml->getChildAt(idx); double downthrust = M_PI * sxtpos->getDouble("downthrust", 0) / 180; dirThrust = CRRCMath::Vector3(cos(downthrust), 0, sin(downthrust)); CRRCMath::Vector3 pos = CRRCMath::Vector3(sxtpos->getDouble("x", 0), 0, sxtpos->getDouble("z", 0)); CRRCMath::Vector3 dirForce = pos * (1/pos.length()); // Split thrust vector into a part parallel to dirForce and a part parallel to dirMoment: // dirThrust = a * dirForce + b * dirMoment // After simplifying all this (and using the variable expressions above) the solution boils down to: double a = sin(downthrust) * dirForce.r[2] + cos(downthrust) * dirForce.r[0]; double b = cos(downthrust) * dirForce.r[2] - sin(downthrust) * dirForce.r[0]; mulForce = dirForce * a; mulMoment = CRRCMath::Vector3(0, b * pos.length(), 0); } mulForce.print("mulForce=", ", "); mulMoment.print("mulMoment=", "\n"); }
void Power::Propeller::ReloadParams_automagic(SimpleXMLTransfer* xml) { SimpleXMLTransfer* p = xml->getChild("battery.shaft.propeller"); D = p->getDouble("D"); H = p->getDouble("H"); J = p->getDouble("J"); double F = xml->getDouble("F"); double V = xml->getDouble("V"); // Der Sturz wird in jedem Fall aus der Modelldatei gelesen, ansonsten muss man ja eine // Propellerdatei fuer jeden Sturz extra haben. CalcDownthrust(p); { // Calculate rotational speed and torque needed: // F = M_PI * 0.25 * D*D * RHO * (V_X + filter.val/2) * filter.val * ETA_PROP; // F = M_PI * 0.25 * D*D * RHO * (V + (Hn-V)/2) * (Hn-V) * ETA_PROP; // F = M_PI * 0.25 * D*D * RHO * (V/2 + Hn/2) * (Hn-V) * ETA_PROP; double n = sqrt( (8*F/(M_PI*D*D*RHO*ETA_PROP)) + V*V)/H; double M = F * (V + (V + H*n)/2) / (2*M_PI*n) * i; // Save these values so the engine can adjust itself to them: p->setAttribute("automagic.n_P", doubleToString(n)); p->setAttribute("automagic.M_P", doubleToString(M)); } omega_fold = p->attributeAsDouble("n_fold", -1)*2*M_PI; }
/** \brief Add animations to a model * * This method reads animation description tags from a model file * and tries to add the corresponding animations to the 3D model. * * \todo Right now there's only one type of animation: movable control * surfaces. Therefore this method receives a pointer to the control * input class. If animations are added that need a different kind of * input for their update() method, we need to decide how to pass all * this stuff to initAnimations(). * * \param model_file XML model description file * \param model scenegraph of the 3D model */ void initAnimations(SimpleXMLTransfer *model_file, ssgEntity* model) { SimpleXMLTransfer *animations = model_file->getChild("animations", true); int num_anims = animations->getChildCount(); std::cout << "initAnimations: found " << num_anims << " children" << std::endl; for (int i = 0; i < num_anims; i++) { SimpleXMLTransfer *animation = animations->getChildAt(i); createAnimation(animation, model); } }
void T_TX_InterfaceSerPIC::putBackIntoCfg (SimpleXMLTransfer *config)/*{{{*/ { #if DEBUG_TX_INTERFACE > 0 std::cout << "T_TX_InterfaceSerPIC::putBackIntoCfg(SimpleXMLTransfer *config)" << std::endl; #endif // Store the port settings T_TX_InterfaceSerial::putBackIntoCfg(config); // Store additional settings SimpleXMLTransfer *port = config->getChild(getXmlChildName() + ".port", true); port->setAttributeOverwrite ("sync", ucSyncByte); port->setAttributeOverwrite ("button_channel", iSPIC_ButtonChannel); }
void T_Calibration::putBackIntoCfg(SimpleXMLTransfer* cfgfile) { #if DEBUG_TX_INTERFACE > 0 printf("T_Calibration::putBackIntoCfg(cfg)\n"); printf(" --> %s\n", child_in_cfg.c_str()); #endif int size; SimpleXMLTransfer* item; SimpleXMLTransfer* group; SimpleXMLTransfer* item2; item = cfgfile->getChild(child_in_cfg); group = item->getChild("calibration"); group->setAttributeOverwrite("version", "2"); // clean list size = group->getChildCount(); for (int n = 0; n < size; n++) { item2 = group->getChildAt(0); group->removeChildAt(0); delete item2; } // create new list for (int n = 0; n < TX_MAXAXIS; n++) { item2 = new SimpleXMLTransfer(); item2->setName("axis"); item2->addAttribute("val_min", doubleToString(val_min[n])); item2->addAttribute("val_mid", doubleToString(val_mid[n])); item2->addAttribute("val_max", doubleToString(val_max[n])); group->addChild(item2); } }
Power::Shaft::Shaft(SimpleXMLTransfer* xml) { for (int n=0; n<xml->getChildCount(); n++) { SimpleXMLTransfer* it = xml->getChildAt(n); Gearing* s = (Gearing*)0; if (it->getName().compare("engine") == 0) s = new Engine_DCM(); else if (it->getName().compare("propeller") == 0) s = new Propeller(); else if (it->getName().compare("simplethrust") == 0) s = new SimpleThrust(); if (s != (Gearing*)0) gear.push_back(s); } }
CRRCAirplaneV2::CRRCAirplaneV2(SimpleXMLTransfer* xml) { printf("CRRCAirplaneV2(xml)\n"); // initialize the airplane's sound initSound(xml); // initialize the visual representation // first collect all relevant information from the model file std::string s; s = XMLModelFile::getGraphics(xml)->getString("model"); // Offset of center of gravity CRRCMath::Vector3 pCG; pCG = CRRCMath::Vector3(0, 0, 0); if (xml->indexOfChild("CG") >= 0) { SimpleXMLTransfer* i; i = xml->getChild("CG"); pCG.r[0] = i->attributeAsDouble("x", 0); pCG.r[1] = i->attributeAsDouble("y", 0); pCG.r[2] = i->attributeAsDouble("z", 0); if (i->attributeAsInt("units") == 1) pCG *= M_TO_FT; } // plib automatically loads the texture file, but it does not know which directory to use. // where is the object file? std::string of = FileSysTools::getDataPath("objects/" + s); // compile and set relative texture path std::string tp = of.substr(0, of.length()-s.length()-1-7) + "textures"; lVisID = Video::new_visualization(of, tp, pCG, xml); if (lVisID == INVALID_AIRPLANE_VISUALIZATION) { std::string msg = "Unable to open airplane model file \""; msg += s; msg += "\"\nspecified in \""; msg += xml->getSourceDescr(); msg += "\""; throw std::runtime_error(msg); } }
/** * Load the airplane specified in configfile. Throw a * std::runtime_error on failure. */ void Aircraft::load(SimpleXMLTransfer *configfile, FDMEnviroment* fdmEnvironment) { cleanup(); fdmInterface = new ModFDMInterface(); std::string filename = configfile->getString("airplane.file", "models/allegro.xml"); filename = air_to_xml_file_load(filename); try { SimpleXMLTransfer* xml = new SimpleXMLTransfer(filename); SimpleXMLTransfer* ap = configfile->getChild("airplane"); // Here we copy graphics and config preferences from crrcsim's config file // into the in-memory-copy of the airplane. This is because an airplane file // should not be altered by user preferences. XMLModelFile::SetGraphics(xml, ap->attributeAsInt("graphics", 0)); XMLModelFile::SetConfig (xml, ap->attributeAsInt("config", 0)); fdmInterface->loadAirplane(xml, fdmEnvironment, configfile); if (configfile->getInt("video.enabled", 1)) { model_ = new CRRCAirplaneLaRCSimSSG(xml, scene); } getFDM()->registerAnimations(getModel()->getAnimations()); delete xml; } catch (XMLException e) { std::string msg = "Error opening airplane specification file: "; msg += filename; msg += ": "; msg += e.what(); throw std::runtime_error(msg); } if (getFDM() == NULL) { throw std::runtime_error("Unable to load airplane specification file."); } }
/** * Initialize the interface. * * The base class handles all hardware initialization for us, so we only * have to set up the correct control line states to power the interface. */ int T_TX_InterfaceSerPIC::init (SimpleXMLTransfer *config) { #if DEBUG_TX_INTERFACE > 0 std::cout << "T_TX_InterfaceSerPIC::init ()\n"; #endif int ret = T_TX_InterfaceSerial::init (config); if (ret == 0) { // initialized successfully, now turn on the power supply for the // interface hardware (careful, could throw an exception) try { setRts (true); setDtr (false); } catch (CharDevice::ConfigureDeviceException e) { setErrMsg ("Setting Rts/Dtr states failed."); cerr << "Serial interface initialization: " << getErrMsg () << endl; ret = 1; } int default_sync_byte = DEFAULT_SYNC_BYTE_19200; int default_button_channel = DEFAULT_BUTTON_CHANNEL_19200; if (T_TX_InterfaceSerial::getBaudRate() == 9600) { default_sync_byte = DEFAULT_SYNC_BYTE_9600; default_button_channel = DEFAULT_BUTTON_CHANNEL_9600; } // read sync and button byte settings from config file SimpleXMLTransfer *port = config->getChild(getXmlChildName() + ".port", true); ucSyncByte = port->attributeAsInt("sync", default_sync_byte); iSPIC_ButtonChannel = port->attributeAsInt("button_channel", default_button_channel); #if DEBUG_TX_INTERFACE > 0 std::cout << " Configured sync byte: 0x" << std::hex << int(ucSyncByte) << std::dec; std::cout << ", " << std::string((iSPIC_ButtonChannel == 0) ? "no" : "has") << " button channel"; std::cout << std::endl; #endif } return ret; }
void T_Calibration::init(SimpleXMLTransfer* cfgfile, std::string childname) { #if DEBUG_TX_INTERFACE > 0 printf("T_Calibration::init(cfg, child)\n"); printf(" <-- %s\n", childname.c_str()); #endif int size; SimpleXMLTransfer* item; SimpleXMLTransfer* group; SimpleXMLTransfer* item2; child_in_cfg = childname; // try to load config printf("Loading calibration settings from %s:\n", childname.c_str()); try { item = cfgfile->getChild(childname, true); group = item->getChild("calibration", true); int nVer = group->getInt("version", 1); size = group->getChildCount(); if (size > TX_MAXAXIS) size = TX_MAXAXIS; for (int n = 0; n < size; n++) { item2 = group->getChildAt(n); switch (nVer) { case 1: { float scale = item2->getDouble("scale", 1.0); float off = item2->getDouble("offset", 0.0); // old: out = scale * in + offset val_min[n] = (-0.5 - off) / scale; val_max[n] = ( 0.5 - off) / scale; val_mid[n] = 0.5 * (val_min[n] + val_max[n]); printf(" (1)"); } break; case 2: val_min[n] = item2->getDouble("val_min", -1.0); val_mid[n] = item2->getDouble("val_mid", 0.0); val_max[n] = item2->getDouble("val_max", 1.0); printf(" (2)"); break; } printf(" axis=%i val_min=%f val_mid=%f val_max=%f\n", n, val_min[n], val_mid[n], val_max[n]); } } catch (XMLException e) { fprintf(stderr, "*** T_Calibration::init(): %s\n", e.what()); } }
void Power::Shaft::ReloadParams(SimpleXMLTransfer* xml) { int nChildCnt = 0; double J_ges; fBrake = (xml->attributeAsInt("brake", 1) != 0); J_ges = xml->attributeAsDouble("J", 0); std::cout << " Shaft: J=" << J_ges << " kg m^2\n"; for (int n=0; n<xml->getChildCount(); n++) { SimpleXMLTransfer* it = xml->getChildAt(n); if (it->getName().compare("engine") == 0 || it->getName().compare("propeller") == 0 || it->getName().compare("simplethrust") == 0 ) { gear[nChildCnt]->ReloadParams(it); J_ges += gear[nChildCnt++]->getJ(); } } J_inv = 1/J_ges; }
/** * Create a CRRCControlSurfaceAnimation object * * Initialize the animation from an * <animation type="ControlSurface"> tag */ CRRCControlSurfaceAnimation::CRRCControlSurfaceAnimation(SimpleXMLTransfer *xml) : CRRCAnimation(new ssgTransform()), fallback_data(0.0f), eventAdapter(this, &CRRCControlSurfaceAnimation::axisValueCallback, Event::Input), aileron(0.0f), elevator(0.0f), rudder(0.0f), throttle(0.0f), spoiler(0.0f), flap(0.0f), retract(0.0f), pitch(0.0f) { bool failed = false; // evaluate <object> tag SimpleXMLTransfer *map = xml->getChild("object", true); symbolic_name = map->getString("name", "no_name_set"); max_angle = (float)(map->getDouble("max_angle", 0.0) * SG_RADIANS_TO_DEGREES); abs_max_angle = (float)fabs((double)max_angle); // find hinges and evaluate all <control> tags int num_controls = 0; int num_hinges = 0; for (int i = 0; i < xml->getChildCount(); i++) { SimpleXMLTransfer *child = xml->getChildAt(i); if (child->getName() == "hinge") { // found a <hinge> child sgVec3 pos; pos[SG_X] = (float)(-1 * child->getDouble("y", 0.0)); pos[SG_Y] = (float)(-1 * child->getDouble("x", 0.0)); pos[SG_Z] = (float)(-1 * child->getDouble("z", 0.0)); if (num_hinges == 0) { sgCopyVec3(hinge_1, pos); } else if (num_hinges == 1) { sgCopyVec3(hinge_2, pos); } num_hinges++; } else if (child->getName() == "control") { // found a <control> child // The "*2" factor for each gain value scales the control input // values from -0.5...+0.5 to -1.0...+1.0. This saves one // float multiplication per mapping in the runtime update() routine. std::string mapping = child->getString("mapping", "NOTHING"); float gain = (float)child->getDouble("gain", 1.0); if (mapping == "ELEVATOR") { datasource.push_back(&elevator); source_gain.push_back(gain * 2); num_controls++; } else if (mapping == "AILERON") { datasource.push_back(&aileron); source_gain.push_back(gain * 2); num_controls++; } else if (mapping == "THROTTLE") { datasource.push_back(&throttle); source_gain.push_back(gain * 2); num_controls++; } else if (mapping == "RUDDER") { datasource.push_back(&rudder); source_gain.push_back(gain * 2); num_controls++; } else if (mapping == "FLAP") { datasource.push_back(&flap); source_gain.push_back(gain * 2); num_controls++; } else if (mapping == "SPOILER") { datasource.push_back(&spoiler); source_gain.push_back(gain * 2); num_controls++; } else if (mapping == "RETRACT") { datasource.push_back(&retract); source_gain.push_back(gain * 2); num_controls++; } else if (mapping == "PITCH") { datasource.push_back(&pitch); source_gain.push_back(gain * 2); num_controls++; } else { std::cerr << "ControlSurfaceAnimation: ignoring <control> tag without mapping." << std::endl; } } } if (num_controls < 1) { std::cerr << "ControlSurfaceAnimation: found animation without proper <control> tag. Animation disabled." << std::endl; failed = true; } if (num_hinges < 2) { std::cerr << "ControlSurfaceAnimation: Must specify exactly two hinges!" << std::endl; failed = true; } else { if (num_hinges > 2) { std::cerr << "ControlSurfaceAnimation: Must specify exactly two hinges!" << std::endl; std::cerr << "ControlSurfaceAnimation: Ignoring excessive hinge tag(s)." << std::endl; } sgSubVec3(axis, hinge_2, hinge_1); if (sgLengthVec3(axis) < 0.001) { std::cerr << "ControlSurfaceAnimation: Insufficient spacing between hinges!" << std::endl; failed = true; } } if (failed) { std::cerr << "ControlSurfaceAnimation: Animation setup failed." << std::endl; // set to some non-critical defaults datasource.resize(1); datasource[0] = &fallback_data; source_gain.resize(1); source_gain[0] = 1.0; sgSetVec3(hinge_1, 0.0f, 0.0f, 0.0f); sgSetVec3(hinge_2, 1.0f, 0.0f, 0.0f); sgSubVec3(axis, hinge_2, hinge_1); } sgMakeIdentMat4(move_to_origin); move_to_origin[3][0] = -hinge_1[0]; move_to_origin[3][1] = -hinge_1[1]; move_to_origin[3][2] = -hinge_1[2]; sgMakeTransMat4(move_back, hinge_1); realInit(); }
/** \brief Initialize the mixer from a config file. * * The mixer object will be initialized from the given config file. * This file may contain more than one branch with interface settings, * so the name of the child has to be specified. */ int T_TX_Mixer::init(SimpleXMLTransfer* cfg, std::string child) { #if DEBUG_TX_INTERFACE > 0 printf("T_TX_Mixer::init(cfg, child)\n"); printf(" <-- %s\n", child.c_str()); #endif SimpleXMLTransfer* mixer; SimpleXMLTransfer* item; SimpleXMLTransfer* inter; // store the child's name for writing back the settings child_in_cfg = child; printf("Loading mixer settings from %s:\n", child.c_str()); try { inter = cfg->getChild(child, true); mixer = inter->getChild("mixer", true); enabled = mixer->attributeAsInt("enabled", 1); dr_enabled = mixer->attributeAsInt("dr_enabled", 0); for (int i = T_AxisMapper::AILERON; i <= T_AxisMapper::PITCH; i++) { item = mixer->getChild(Global::inputDev->AxisStringsXML[i], true); trim_val[i] = item->getDouble("trim", 0.0); nrate_val[i] = item->getDouble("nrate", 1.0); srate_val[i] = item->getDouble("srate", 1.0); exp_val[i] = item->getDouble("exp", 0.0); mtravel_val[i] = item->getDouble("mtravel", -0.5); ptravel_val[i] = item->getDouble("ptravel", 0.5); } for (int i = 0; i < T_TX_Mixer::NUM_MIXERS; i++) { item = mixer->getChild(Global::inputDev->MixerStringsXML[i], true); mixer_enabled[i] = item->attributeAsInt("enabled", 0); mixer_src[i] = item->getDouble("src", T_AxisMapper::NOTHING); mixer_dst[i] = item->getDouble("dst", T_AxisMapper::NOTHING); mixer_val[i] = item->getDouble("val", 0.0); } } catch (XMLException e) { errMsg = e.what(); return 1; } return 0; }
/** \brief Transfers all settings back to the config file. * * The mixer settings will be stored in the same branch of the config * file that they were read from on initialization. * * \param config Pointer to the config file's SimpleXMLTransfer. */ void T_TX_Mixer::putBackIntoCfg(SimpleXMLTransfer* config) { #if DEBUG_TX_INTERFACE > 0 printf("T_TX_Mixer::putBackIntoCfg(SimpleXMLTransfer* config)\n"); printf(" --> %s\n", child_in_cfg.c_str()); #endif SimpleXMLTransfer* inter = config->getChild(child_in_cfg); SimpleXMLTransfer* mixer = inter->getChild("mixer"); SimpleXMLTransfer* item; mixer->setAttributeOverwrite("enabled", enabled); mixer->setAttributeOverwrite("dr_enabled", dr_enabled); item = mixer->getChild(Global::inputDev->AxisStringsXML[T_AxisMapper::AILERON], true); item->setAttributeOverwrite("trim", doubleToString(trim_val[T_AxisMapper::AILERON])); item->setAttributeOverwrite("nrate", doubleToString(nrate_val[T_AxisMapper::AILERON])); item->setAttributeOverwrite("srate", doubleToString(srate_val[T_AxisMapper::AILERON])); item->setAttributeOverwrite("exp", doubleToString(exp_val[T_AxisMapper::AILERON])); item = mixer->getChild(Global::inputDev->AxisStringsXML[T_AxisMapper::ELEVATOR], true); item->setAttributeOverwrite("trim", doubleToString(trim_val[T_AxisMapper::ELEVATOR])); item->setAttributeOverwrite("nrate", doubleToString(nrate_val[T_AxisMapper::ELEVATOR])); item->setAttributeOverwrite("srate", doubleToString(srate_val[T_AxisMapper::ELEVATOR])); item->setAttributeOverwrite("exp", doubleToString(exp_val[T_AxisMapper::ELEVATOR])); item = mixer->getChild(Global::inputDev->AxisStringsXML[T_AxisMapper::RUDDER], true); item->setAttributeOverwrite("trim", doubleToString(trim_val[T_AxisMapper::RUDDER])); item->setAttributeOverwrite("nrate", doubleToString(nrate_val[T_AxisMapper::RUDDER])); item->setAttributeOverwrite("srate", doubleToString(srate_val[T_AxisMapper::RUDDER])); item->setAttributeOverwrite("exp", doubleToString(exp_val[T_AxisMapper::RUDDER])); item = mixer->getChild(Global::inputDev->AxisStringsXML[T_AxisMapper::FLAP], true); item->setAttributeOverwrite("trim", doubleToString(trim_val[T_AxisMapper::FLAP])); item->setAttributeOverwrite("mtravel", doubleToString(mtravel_val[T_AxisMapper::FLAP])); item->setAttributeOverwrite("ptravel", doubleToString(ptravel_val[T_AxisMapper::FLAP])); for (int i = 0; i < T_TX_Mixer::NUM_MIXERS; i++) { item = mixer->getChild(Global::inputDev->MixerStringsXML[i], true); item->setAttributeOverwrite("enabled", mixer_enabled[i]); item->setAttributeOverwrite("src", mixer_src[i]); item->setAttributeOverwrite("dst", mixer_dst[i]); item->setAttributeOverwrite("val", doubleToString(mixer_val[i])); } }
/** \brief Add animations to a model * * This method reads animation description tags from a model file * and tries to add the corresponding animations to the 3D model. * * \todo Right now there's only one type of animation: movable control * surfaces. Therefore this method receives a pointer to the control * input class. If animations are added that need a different kind of * input for their update() method, we need to decide how to pass all * this stuff to initAnimations(). * * \param model_file XML model description file * \param model scenegraph of the 3D model * \param fInputs pointer to the control input class * \param anim_list list of all created CRRCAnimation objects */ void initAnimations(SimpleXMLTransfer *model_file, ssgEntity* model, TSimInputs *fInput, std::vector<CRRCAnimation*>& anim_list) { SimpleXMLTransfer *animations = model_file->getChild("animations", true); int num_anims = animations->getChildCount(); fprintf(stdout, "initAnimations: found %d children\n", num_anims); for (int i = 0; i < num_anims; i++) { SimpleXMLTransfer *animation = animations->getChildAt(i); ssgEntity *node; if (animation->getName() != "animation") { fprintf(stderr, "initAnimations: invalid child <%s>\n", animation->getName().c_str()); } else { std::string node_name = animation->getString("object.name", "default"); std::string type = animation->getString("type", "default"); node = SSGUtil::findNamedNode(model, node_name.c_str()); if (node != NULL) { CRRCAnimation *anim = NULL; printf("initAnimations: found animation node %s, type %s\n", node_name.c_str(), type.c_str()); if (type == "ControlSurface") { anim = new CRRCControlSurfaceAnimation(animation, fInput); } else { fprintf(stderr, "initAnimations: unknown animation type '%s'\n", type.c_str()); } if (anim != NULL) { if (anim->getBranch() == NULL) { fprintf(stderr, "initAnimations: defunct animation class (animation branch is <NULL>)\n"); exit(0); } else { SSGUtil::spliceBranch(anim->getBranch(), node); anim->init(); anim->setName("Animation"); anim->getBranch()->setUserData(anim); anim->getBranch()->setTravCallback(SSG_CALLBACK_PRETRAV, animation_callback); anim_list.push_back(anim); } } } else { fprintf(stderr, "initAnimations: node '%s' not found in 3D model\n", node_name.c_str()); } } } }
void CRRC_AirplaneSim_MCopter01::LoadFromXML(SimpleXMLTransfer* xml, int nVerbosity) { if (xml->getString("type").compare("mcopter01") != 0 || xml->getInt("version") != 1) { throw XMLException("file is not for mcopter01"); } SimpleXMLTransfer* i; SimpleXMLTransfer* cfg = XMLModelFile::getConfig(xml); { double to_slug; double to_slug_ft_ft; i = cfg->getChild("mass_inertia"); switch (i->getInt("units")) { case 0: to_slug = 1; to_slug_ft_ft = 1; break; case 1: to_slug = KG_TO_SLUG; to_slug_ft_ft = KG_M_M_TO_SLUG_FT_FT; break; default: { throw std::runtime_error("Unknown units in mass_inertia"); } break; } Mass = i->getDouble("Mass") * to_slug; I_xx = i->getDouble("I_xx") * to_slug_ft_ft; I_yy = i->getDouble("I_yy") * to_slug_ft_ft; I_zz = i->getDouble("I_zz") * to_slug_ft_ft; I_xz = i->getDouble("I_xz") * to_slug_ft_ft; } { speed_damp = cfg->getDouble("aero.speed.damp"); roll_damp1 = cfg->getDouble("aero.roll.damp1", 0); yaw_damp1 = cfg->getDouble("aero.yaw.damp1", 0); roll_damp2 = cfg->getDouble("aero.roll.damp2", 0); yaw_damp2 = cfg->getDouble("aero.yaw.damp2", 0); yaw_dist = cfg->getDouble("aero.yaw.dist", 0); roll_dist = cfg->getDouble("aero.roll.dist", 0); pitch_dist = cfg->getDouble("aero.pitch.dist", roll_dist); // The ground effect parameters should be quite independent of the helicopter // parameters...shouldn't they? However, they can be adjusted. dGEDistMul = xml->getDouble("GroundEffect.dist.mul", 1.5); { double tau = xml->getDouble("Disturbance.tau_filter", 0.2); dist_t_init = xml->getDouble("Disturbance.time", 0.2); filt_rnd_yaw.SetTau(tau); filt_rnd_roll.SetTau(tau); filt_rnd_pitch.SetTau(tau); } } wheels.init(xml, 0); dRotorRadius = wheels.getWingspan()*0.5; dRotorZ = wheels.getZHigh(); wheels.init(xml, 0); dRotorRadius = wheels.getWingspan()*0.5; dRotorZ = wheels.getZHigh(); props.clear(); i = cfg->getChild("aero.props"); for (int n=0; n<i->getChildCount(); n++) props.push_back(Propdata(i->getChildAt(n))); if (power.size() == 0) { for (unsigned int n=0; n<props.size(); n++) power.push_back(new Power::Power(cfg, nVerbosity)); dURef = 0.7 * cfg->getDouble("power.battery.U_0"); } else { for (unsigned int n=0; n<power.size(); n++) power[n]->ReloadParams(cfg, nVerbosity); } controllers.clear(); Controller::LoadList(cfg->getChild("controllers"), controllers); }
/** \brief The constructor. * * Loads an airplane model from an xml description. The 3D model * will be added to the specified scenegraph. * * \param xml XML model description file * \param graph Pointer to the scenegraph which shall render the model */ CRRCAirplaneLaRCSimSSG::CRRCAirplaneLaRCSimSSG(SimpleXMLTransfer* xml, ssgBranch *graph) : CRRCAirplaneLaRCSim(xml), initial_trans(NULL), model_trans(NULL), model(NULL), shadow(NULL), shadow_trans(NULL) { printf("CRRCAirplaneLaRCSimSSG(xml, branch)\n"); std::string s; s = XMLModelFile::getGraphics(xml)->getString("model"); // plib automatically loads the texture file, but it does not know which directory to use. { // where is the object file? std::string of = FileSysTools::getDataPath("objects/" + s); // compile and set relative texture path std::string tp = of.substr(0, of.length()-s.length()-1-7) + "textures"; ssgTexturePath(tp.c_str()); // load model model = ssgLoad(of.c_str()); } if (model != NULL) { // Offset of center of gravity CRRCMath::Vector3 pCG; pCG = CRRCMath::Vector3(0, 0, 0); if (xml->indexOfChild("CG") >= 0) { SimpleXMLTransfer* i; i = xml->getChild("CG"); pCG.r[0] = i->attributeAsDouble("x", 0); pCG.r[1] = i->attributeAsDouble("y", 0); pCG.r[2] = i->attributeAsDouble("z", 0); if (i->attributeAsInt("units") == 1) pCG *= M_TO_FT; } // transform model from SSG coordinates to CRRCsim coordinates initial_trans = new ssgTransform(); model_trans = new ssgTransform(); graph->addKid(model_trans); model_trans->addKid(initial_trans); initial_trans->addKid(model); sgMat4 it = { {1.0, 0.0, 0.0, 0}, {0.0, 0.0, -1.0, 0}, {0.0, 1.0, 0.0, 0}, {pCG.r[1], pCG.r[2], -pCG.r[0], 1.0} }; initial_trans->setTransform(it); // add a simple shadow shadow = (ssgEntity*)initial_trans->clone(SSG_CLONE_RECURSIVE | SSG_CLONE_GEOMETRY | SSG_CLONE_STATE); makeShadow(shadow); shadow_trans = new ssgTransform(); graph->addKid(shadow_trans); shadow_trans->addKid(shadow); // add animations ("real" model only, without shadow) initAnimations(xml, model, &Global::inputs, animations); } else { std::string msg = "Unable to open airplane model file \""; msg += s; msg += "\"\nspecified in \""; msg += xml->getSourceDescr(); msg += "\""; throw std::runtime_error(msg); } }
ModelBasedScenery::ModelBasedScenery(SimpleXMLTransfer *xml, int sky_variant) : Scenery(xml, sky_variant), location(Scenery::MODEL_BASED) { ssgEntity *model = NULL; SimpleXMLTransfer *scene = xml->getChild("scene", true); getHeight_mode = scene->attributeAsInt("getHeight_mode", DEFAULT_HEIGHT_MODE); //std::cout << "----getHeight_mode : " << getHeight_mode <<std::endl; SceneGraph = new ssgRoot(); // Create an "invisible" state. This state actually makes a node // visible in a predefined way. This is used to visualize invisible // objects (e.g. collision boxes). invisible_state = new ssgSimpleState(); invisible_state->disable(GL_COLOR_MATERIAL); invisible_state->disable(GL_TEXTURE_2D); invisible_state->enable(GL_LIGHTING); invisible_state->enable(GL_BLEND); //invisible_state->setShadeModel(GL_SMOOTH); //invisible_state->setShininess(0.0f); invisible_state->setMaterial(GL_EMISSION, 0.0, 0.0, 0.0, 0.0); invisible_state->setMaterial(GL_AMBIENT, 1.0, 0.0, 0.0, 0.5); invisible_state->setMaterial(GL_DIFFUSE, 1.0, 0.0, 0.0, 0.5); invisible_state->setMaterial(GL_SPECULAR, 1.0, 0.0, 0.0, 0.5); // transform everything from SSG coordinates to CRRCsim coordinates initial_trans = new ssgTransform(); SceneGraph->addKid(initial_trans); //10.76 sgMat4 it = { {1, 0.0, 0.0, 0}, {0.0, 0.0, -1, 0}, {0.0, 1, 0.0, 0}, {0.0, 0.0, 0.0, 1.0} }; initial_trans->setTransform(it); // find all "objects" defined in the file int num_children = scene->getChildCount(); for (int cur_child = 0; cur_child < num_children; cur_child++) { SimpleXMLTransfer *kid = scene->getChildAt(cur_child); // only use "object" tags if (kid->getName() == "object") { std::string filename = kid->attribute("filename", "not_specified"); bool is_terrain = (kid->attributeAsInt("terrain", 1) != 0); bool is_visible = (kid->attributeAsInt("visible", 1) != 0); // PLIB automatically loads the texture file, // but it does not know which directory to use. // Where is the object file? std::string of = FileSysTools::getDataPath("objects/" + filename, TRUE); // compile and set relative texture path std::string tp = of.substr(0, of.length()-filename.length()-1-7) + "textures"; ssgTexturePath(tp.c_str()); // load model std::cout << "Loading 3D object \"" << of.c_str() << "\""; if (is_terrain) { std::cout << " (part of terrain)"; } if (!is_visible) { std::cout << " (invisible)"; } std::cout << std::endl; model = ssgLoad(of.c_str()); if (model != NULL) { if (!is_visible) { setToInvisibleState(model); } // The model may contain internal node attributes (e.g. for // integrated collision boxes). Parse these attributes now. evaluateNodeAttributes(model); // now parse the instances and place the model in the SceneGraph for (int cur_instance = 0; cur_instance < kid->getChildCount(); cur_instance++) { SimpleXMLTransfer *instance = kid->getChildAt(cur_instance); if (instance->getName() == "instance") { sgCoord coord; // try north/east/height first, then fallback to x/y/z try { coord.xyz[SG_X] = instance->attributeAsDouble("east"); } catch (XMLException &e) { coord.xyz[SG_X] = instance->attributeAsDouble("y", 0.0); } try { coord.xyz[SG_Y] = instance->attributeAsDouble("north"); } catch (XMLException &e) { coord.xyz[SG_Y] = instance->attributeAsDouble("x", 0.0); } try { coord.xyz[SG_Z] = instance->attributeAsDouble("height"); } catch (XMLException &e) { coord.xyz[SG_Z] = instance->attributeAsDouble("z", 0.0); } coord.hpr[0] = 180 - instance->attributeAsDouble("h", 0.0); coord.hpr[1] = -instance->attributeAsDouble("p", 0.0); coord.hpr[2] = -instance->attributeAsDouble("r", 0.0); std::cout << std::setprecision(1); std::cout << " Placing instance at " << coord.xyz[SG_X] << ";" << coord.xyz[SG_Y] << ";" << coord.xyz[SG_Z]; std::cout << ", orientation " << (180-coord.hpr[0]) << ";" << -coord.hpr[1] << ";" << -coord.hpr[2] << std::endl; std::cout << std::setprecision(6); ssgTransform *trans = new ssgTransform(); trans->setTransform(&coord); // In PLIB::SSG, intersection testing is done by a tree-walking // function. This can be influenced by the tree traversal mask // bits. The HOT and LOS flags are cleared for objects that are // not part of the terrain, so that the height-of-terrain and // line-of-sight algorithms ignore this branch of the tree. if (!is_terrain) { trans->clrTraversalMaskBits(SSGTRAV_HOT | SSGTRAV_LOS); } // Objects are made invisible by clearing the CULL traversal flag. // This means that ssgCullAndDraw will ignore this branch. if (!is_visible) { trans->clrTraversalMaskBits(SSGTRAV_CULL); } initial_trans->addKid(trans); trans->addKid(model); } } } } } // create actual terrain height model if (getHeight_mode == 1) { heightdata = new HD_TabulatedTerrain(SceneGraph); } else if (getHeight_mode == 2) { heightdata = new HD_TilingTerrain(SceneGraph); } else { heightdata = new HD_SsgLOSTerrain(SceneGraph); } //wind SimpleXMLTransfer *wind = xml->getChild("wind", true); std::string wind_filename = wind->attribute("filename",""); #if WINDDATA3D == 1 wind_data = 0;//default : no wind_data std::string wind_position_unit = wind->attribute("unit",""); try { flDefaultWindDirection = wind->attributeAsInt("direction"); ImposeWindDirection = true; } catch (XMLException) { // if not attribut "direction", normal mode } if (wind_position_unit.compare("m")==0) { wind_position_coef = FT_TO_M; } else { wind_position_coef = 1; } std::cout << "wind file name : " << wind_filename.c_str()<< std::endl; if (wind_filename.length() > 0) { wind_filename = FileSysTools::getDataPath(wind_filename); std::cout << "init wind ---------"; int n = init_wind_data((wind_filename.c_str())); std::cout << n << " points processed" << std::endl; } #else if (wind_filename.length() > 0) { new CGUIMsgBox("Insufficient configuration to read windfields."); } #endif }
/** * Initialize a WheelSystem from an XML file. * * \param ModelFile pointer to file class * \param def_span default span if no hardpoints are found to calculate it */ void WheelSystem::init(SimpleXMLTransfer *ModelFile, SCALAR def_span) { Wheel wheel(this); SimpleXMLTransfer *e, *i; unsigned int uSize; double x, y, z; double dist; double to_ft; double to_lbf_per_ft; double to_lbf_s_per_ft; double to_lbf; CRRCMath::Vector3 pCG; /** * Tracks wingspan [m] */ double span = 0; span_ft = 0.0; // pCG = CRRCMath::Vector3(0, 0, 0); if (ModelFile->indexOfChild("CG") >= 0) { i = ModelFile->getChild("CG"); pCG.r[0] = i->attributeAsDouble("x", 0); pCG.r[1] = i->attributeAsDouble("y", 0); pCG.r[2] = i->attributeAsDouble("z", 0); if (i->attributeAsInt("units") == 1) pCG *= M_TO_FT; } // let's assume that there is nothing below/above the CG: dZLow = 0; dZHigh = 0; // let's assume that there is nothing distant from the CG: dMaxSize = 0; wheels.clear(); i = ModelFile->getChild("wheels"); switch (i->getInt("units")) { case 0: to_ft = 1; to_lbf_per_ft = 1; to_lbf_s_per_ft = 1; to_lbf = 1; break; case 1: to_ft = M_TO_FT; to_lbf_per_ft = FT_TO_M / LBF_TO_N; to_lbf_s_per_ft = FT_TO_M / LBF_TO_N; to_lbf = N_TO_LBF; break; default: { throw std::runtime_error("Unknown units in wheels"); } break; } uSize = i->getChildCount(); for (unsigned int n=0; n<uSize; n++) { // we assign the child number as a unique ID for // debugging wheel.nID = n; e = i->getChildAt(n); x = e->getDouble("pos.x") * to_ft - pCG.r[0]; y = e->getDouble("pos.y") * to_ft - pCG.r[1]; z = e->getDouble("pos.z") * to_ft - pCG.r[2]; wheel.v_P = CRRCMath::Vector3(x, y, z); // let's see if this wheel is coupled to an animation wheel.anim_name = e->getString("pos.animation", ""); if (wheel.anim_name != "") { std::cout << "WheelSystem::init: hardpoint is coupled to anim "; std::cout << wheel.anim_name << std::endl; } wheel.hpt = NULL; if (ModelFile->indexOfChild("animations") >= 0) { SimpleXMLTransfer *a = ModelFile->getChild("animations"); unsigned int numAnims = a->getChildCount(); for (unsigned int animIndex = 0; animIndex < numAnims; animIndex++) { SimpleXMLTransfer *an = a->getChildAt(animIndex); if (an->getString("object.name", "") == wheel.anim_name) { printf("Found %s animation for wheel %s\n", an->getString("type", "<unknown>").c_str(), wheel.anim_name.c_str()); wheel.hpt = new HardPointRotation(an, wheel_inputs); break; } } } wheel.spring_constant = e->getDouble("spring.constant") * to_lbf_per_ft; wheel.spring_damping = e->getDouble("spring.damping") * to_lbf_s_per_ft; wheel.max_force = e->getDouble("spring.max_force", 9999) * to_lbf; wheel.percent_brake = e->getDouble("percent_brake"); wheel.caster_angle_rad = e->getDouble("caster_angle_rad"); if (e->indexOfChild("steering") >= 0) { std::string s = e->getString("steering.mapping", "NOTHING"); wheel.steering_max_angle = e->getDouble("steering.max_angle", 1.0); wheel.steering_mapping = XMLModelFile::GetSteering(s); } else { wheel.steering_mapping = TSimInputs::smNOTHING; wheel.steering_max_angle = 0; } wheels.push_back(wheel); // track wingspan if (span < y) span = y; // lowest point? if (dZLow < z) dZLow = z; // highest point? if (dZHigh > z) dZHigh = z; // far away (Z distance is assumed to be low)? dist = x*x + y*y; if (dist > dMaxSize) dMaxSize = dist; } dMaxSize = sqrt(dMaxSize); span_ft = 2 * span; // just in case: if there were no hardpoints, use the reference span if (span_ft == 0.0) { span_ft = def_span; } }
void CRRCAirplaneLaRCSim::initSound(SimpleXMLTransfer* xml) { SimpleXMLTransfer* cfg = XMLModelFile::getConfig(xml); SimpleXMLTransfer* sndcfg = cfg->getChild("sound", true); int children = sndcfg->getChildCount(); int units = sndcfg->getInt("units", 0); for (int i = 0; i < children; i++) { SimpleXMLTransfer *child = sndcfg->getChildAt(i); std::string name = child->getName(); if (name.compare("sample") == 0) { T_AirplaneSound *sample; // assemble relative path std::string soundfile; soundfile = child->attribute("filename"); // other sound attributes int sound_type = child->getInt("type", SOUND_TYPE_GLIDER); double dPitchFactor = child->getDouble("pitchfactor", 0.002); double dMaxVolume = child->getDouble("maxvolume", 1.0); if (dMaxVolume < 0.0) { dMaxVolume = 0.0; } else if (dMaxVolume > 1.0) { dMaxVolume = 1.0; } //~ if (cfg->indexOfChild("power") < 0) //~ max_thrust = 0; //~ else //~ max_thrust = 1; if (soundfile != "") { // Get full path (considering search paths). soundfile = FileSysTools::getDataPath("sounds/" + soundfile); } // File ok? Use default otherwise. if (!FileSysTools::fileExists(soundfile)) soundfile = FileSysTools::getDataPath("sounds/fan.wav"); std::cout << "soundfile: " << soundfile << "\n"; //~ std::cout << "max_thrust: " << max_thrust << "\n"; std::cout << "soundserver: " << Global::soundserver << "\n"; // Only make noise if a sound file is available if (soundfile != "" && Global::soundserver != (CRRCAudioServer*)0) { std::cout << "Using airplane sound " << soundfile << ", type " << sound_type << ", max vol " << dMaxVolume << std::endl; if (sound_type == SOUND_TYPE_GLIDER) { T_GliderSound *glidersound; float flMinRelV, flMaxRelV, flMaxDist; flMinRelV = (float)child->getDouble("v_min", 1.5); flMaxRelV = (float)child->getDouble("v_max", 4.0); flMaxDist = (float)child->getDouble("dist_max", 300); if (units == 1) { // convert from metric units to ft. flMaxDist *= M_TO_FT; } glidersound = new T_GliderSound(soundfile.c_str(), Global::soundserver->getAudioSpec()); glidersound->setMinRelVelocity(flMinRelV); glidersound->setMaxRelVelocity(flMaxRelV); glidersound->setMaxDistanceFeet(flMaxDist); sample = glidersound; } else { sample = new T_EngineSound(soundfile.c_str(), Global::soundserver->getAudioSpec()); } sample->setType(sound_type); sample->setPitchFactor(dPitchFactor); sample->setMaxVolume(dMaxVolume); sample->setChannel(Global::soundserver->playSample((T_SoundSample*)sample)); sound.push_back(sample); } } } }
/** * Create a HardPointRotation * * \param xml <animation> part of the model file that contains the * description of the animation */ HardPointRotation::HardPointRotation(SimpleXMLTransfer *xml, TSimInputs const& in) { bool failed = false; // evaluate <object> tag SimpleXMLTransfer *map = xml->getChild("object", true); symbolic_name = map->getString("name", "no_name_set"); max_angle_rad = (float)map->getDouble("max_angle", 0.0); abs_max_angle_rad = (float)fabs((double)max_angle_rad); // find hinges and evaluate all <control> tags int num_controls = 0; int num_hinges = 0; for (int i = 0; i < xml->getChildCount(); i++) { SimpleXMLTransfer *child = xml->getChildAt(i); if (child->getName() == "hinge") { // found a <hinge> child CRRCMath::Vector3 pos; pos.r[0] = (float)(child->getDouble("x", 0.0)); pos.r[1] = (float)(child->getDouble("y", 0.0)); pos.r[2] = (float)(child->getDouble("z", 0.0)); if (num_hinges < 2) { hinge[num_hinges] = pos; } num_hinges++; } else if (child->getName() == "control") { // found a <control> child // The "*2" factor for each gain value scales the control input // values from -0.5...+0.5 to -1.0...+1.0. This saves one // float multiplication per mapping in the runtime update() routine. // NB: unsigned control (throttle, spoiler, retract) are not scaled, // since control input is already in range 0.0...+1.0 std::string mapping = child->getString("mapping", "NOTHING"); float gain = (float)child->getDouble("gain", 1.0); std::cout << " mapped to " << mapping << " with gain " << gain; std::cout << " and max_angle_rad " << max_angle_rad << std::endl; if (mapping == "ELEVATOR") { datasource.push_back(&in.elevator); source_gain.push_back(gain * 2); num_controls++; } else if (mapping == "AILERON") { datasource.push_back(&in.aileron); source_gain.push_back(gain * 2); num_controls++; } else if (mapping == "THROTTLE") { datasource.push_back(&in.throttle); source_gain.push_back(gain); num_controls++; } else if (mapping == "RUDDER") { datasource.push_back(&in.rudder); source_gain.push_back(gain * 2); num_controls++; } else if (mapping == "FLAP") { datasource.push_back(&in.flap); source_gain.push_back(gain * 2); num_controls++; } else if (mapping == "SPOILER") { datasource.push_back(&in.spoiler); source_gain.push_back(gain); num_controls++; } else if (mapping == "RETRACT") { datasource.push_back(&in.retract); source_gain.push_back(gain); num_controls++; } else if (mapping == "PITCH") { datasource.push_back(&in.pitch); source_gain.push_back(gain * 2); num_controls++; } else { fprintf(stderr, "HardPointRotation: ignoring <control> tag without mapping.\n"); } } } if (num_controls < 1) { fprintf(stderr, "HardPointRotation: found animation without proper <control> tag. Animation disabled.\n"); failed = true; } if (num_hinges < 2) { fprintf(stderr, "HardPointRotation: Must specify exactly two hinges!\n"); failed = true; } else { if (num_hinges > 2) { fprintf(stderr, "HardPointRotation: Must specify exactly two hinges!\n"); fprintf(stderr, "HardPointRotation: Ignoring excessive hinge tag(s).\n"); } axis = hinge[1] - hinge[0]; if (axis.length() < 0.001) { fprintf(stderr, "HardPointRotation: Insufficient spacing between hinges!\n"); failed = true; } } if (failed) { fprintf(stderr, "HardPointRotation: Animation setup failed.\n"); // set to some non-critical defaults datasource.resize(1); datasource[0] = &fallback_data; source_gain.resize(1); source_gain[0] = 1.0; hinge[0] = CRRCMath::Vector3(0.0, 0.0, 0.0); hinge[1] = CRRCMath::Vector3(1.0, 0.0, 0.0); axis = hinge[1] - hinge[0]; } else { std::cerr << "HardPointRotation: set up animated hardpoint "; std::cerr << symbolic_name << std::endl; } move_orig.makeTranslation(hinge[0] * -1); move_back.makeTranslation(hinge[0]); //~ realInit(); }