Example #1
0
vector<SurfaceSample> SurfaceSample::loadFromXML(const std::string& filename) {
    vector<SurfaceSample> samples;

    try {
        PTree tree;
        read_xml(filename, tree);

        PTree root = tree.get_child("SurfaceSamples");

        for (CI p = root.begin(); p != root.end(); ++p) {
            if (p->first == "Sample") {
                Q posq = XMLHelpers::readQ(p->second.get_child("Pos"));
                Q rpyq = XMLHelpers::readQ(p->second.get_child("RPY"));
                double graspW = XMLHelpers::readDouble(p->second.get_child("GraspW"));
                //cout << "pos=" << posq << " rot=" << rpyq << " graspW=" << graspW << endl;

                Vector3D<> pos(posq[0], posq[1], posq[2]);
                RPY<> rpy(rpyq[0], rpyq[1], rpyq[2]);
                samples.push_back(SurfaceSample(Transform3D<>(pos, rpy.toRotation3D()), graspW));
            }
        }
    } catch (const ptree_error& e) {
        RW_THROW(e.what());
    }

    return samples;
}
Example #2
0
static void write_ini(const PTree & p, std::ostream & out, std::string key_name)
{
	for (PTree::const_iterator i = p.begin(), e = p.end(); i != e; ++i)
	{
		if (i->second.size() == 0)
		{
			out << i->first << " = " << i->second.value() << "\n";
		}
	}
	out << "\n";

	for (PTree::const_iterator i = p.begin(), e = p.end(); i != e; ++i)
	{
		if (i->second.size() > 0)
		{
			out << "[" << key_name + i->first << "]\n";
			write_ini(i->second, out, key_name + i->first + ".");
		}
	}
}
Example #3
0
static void write_xml(const PTree & p, std::ostream & out, std::string indent)
{
	for (PTree::const_iterator i = p.begin(), e = p.end(); i != e; ++i)
	{
		if (i->second.size() == 0)
		{
			out << indent << "<" << i->first << ">" << i->second.value() << "</" << i->first << ">\n";
			write_xml(i->second, out, indent+"\t");
		}
		else
		{
			out << indent << "<" << i->first << ">\n";
			write_xml(i->second, out, indent+"\t");
			out << indent << "</" << i->first << ">\n";
		}
	}
}
Example #4
0
bool CAR::LoadSounds(
	const std::string & carpath,
	const std::string & carname,
	SOUND & sound,
	ContentManager & content,
	std::ostream & error_output)
{
	psound = &sound;

	// check for sound specification file
	std::string path_aud = carpath + "/" + carname + ".aud";
	std::ifstream file_aud(path_aud.c_str());
	if (file_aud.good())
	{
		PTree aud;
		read_ini(file_aud, aud);
		enginesounds.reserve(aud.size());
		for (PTree::const_iterator i = aud.begin(); i != aud.end(); ++i)
		{
			const PTree & audi = i->second;

			std::string filename;
			std::tr1::shared_ptr<SOUNDBUFFER> soundptr;
			if (!audi.get("filename", filename, error_output)) return false;

			enginesounds.push_back(ENGINESOUNDINFO());
			ENGINESOUNDINFO & info = enginesounds.back();

			if (!audi.get("MinimumRPM", info.minrpm, error_output)) return false;
			if (!audi.get("MaximumRPM", info.maxrpm, error_output)) return false;
			if (!audi.get("NaturalRPM", info.naturalrpm, error_output)) return false;

			bool powersetting;
			if (!audi.get("power", powersetting, error_output)) return false;
			if (powersetting)
				info.power = ENGINESOUNDINFO::POWERON;
			else if (!powersetting)
				info.power = ENGINESOUNDINFO::POWEROFF;
			else
				info.power = ENGINESOUNDINFO::BOTH;

			info.sound_source = sound.AddSource(soundptr, 0, true, true);
			sound.SetSourceGain(info.sound_source, 0);
		}

		// set blend start and end locations -- requires multiple passes
		std::map <ENGINESOUNDINFO *, ENGINESOUNDINFO *> temporary_to_actual_map;
		std::list <ENGINESOUNDINFO> poweron_sounds, poweroff_sounds;
		for (std::vector <ENGINESOUNDINFO>::iterator i = enginesounds.begin(); i != enginesounds.end(); ++i)
		{
			if (i->power == ENGINESOUNDINFO::POWERON)
			{
				poweron_sounds.push_back(*i);
				temporary_to_actual_map[&poweron_sounds.back()] = &*i;
			}
			else if (i->power == ENGINESOUNDINFO::POWEROFF)
			{
				poweroff_sounds.push_back(*i);
				temporary_to_actual_map[&poweroff_sounds.back()] = &*i;
			}
		}

		poweron_sounds.sort();
		poweroff_sounds.sort();

		// we only support 2 overlapping sounds at once each for poweron and poweroff; this
		// algorithm fails for other cases (undefined behavior)
		std::list <ENGINESOUNDINFO> * cursounds = &poweron_sounds;
		for (int n = 0; n < 2; n++)
		{
			if (n == 1)
				cursounds = &poweroff_sounds;

			for (std::list <ENGINESOUNDINFO>::iterator i = (*cursounds).begin(); i != (*cursounds).end(); ++i)
			{
				// set start blend
				if (i == (*cursounds).begin())
					i->fullgainrpmstart = i->minrpm;

				// set end blend
				std::list <ENGINESOUNDINFO>::iterator inext = i;
				inext++;
				if (inext == (*cursounds).end())
					i->fullgainrpmend = i->maxrpm;
				else
				{
					i->fullgainrpmend = inext->minrpm;
					inext->fullgainrpmstart = i->maxrpm;
				}
			}

			// now assign back to the actual infos
			for (std::list <ENGINESOUNDINFO>::iterator i = (*cursounds).begin(); i != (*cursounds).end(); ++i)
			{
				assert(temporary_to_actual_map.find(&(*i)) != temporary_to_actual_map.end());
				*temporary_to_actual_map[&(*i)] = *i;
			}
		}
	}
	else
	{
		std::tr1::shared_ptr<SOUNDBUFFER> soundptr;
		if (!content.load(carpath, "engine", soundptr)) return false;
		enginesounds.push_back(ENGINESOUNDINFO());
		enginesounds.back().sound_source = sound.AddSource(soundptr, 0, true, true);
	}

	//set up tire squeal sounds
	for (int i = 0; i < 4; ++i)
	{
		std::tr1::shared_ptr<SOUNDBUFFER> soundptr;
		if (!content.load(carpath, "tire_squeal", soundptr)) return false;
		tiresqueal[i] = sound.AddSource(soundptr, i * 0.25, true, true);
	}

	//set up tire gravel sounds
	for (int i = 0; i < 4; ++i)
	{
		std::tr1::shared_ptr<SOUNDBUFFER> soundptr;
		if (!content.load(carpath, "gravel", soundptr)) return false;
		gravelsound[i] = sound.AddSource(soundptr, i * 0.25, true, true);
	}

	//set up tire grass sounds
	for (int i = 0; i < 4; ++i)
	{
		std::tr1::shared_ptr<SOUNDBUFFER> soundptr;
		if (!content.load(carpath, "grass", soundptr)) return false;
		grasssound[i] = sound.AddSource(soundptr, i * 0.25, true, true);
	}

	//set up bump sounds
	for (int i = 0; i < 4; ++i)
	{
		std::tr1::shared_ptr<SOUNDBUFFER> soundptr;
		if (i >= 2)
		{
			if (!content.load(carpath, "bump_rear", soundptr)) return false;
		}
		else
		{
			if (!content.load(carpath, "bump_front", soundptr)) return false;
		}
		tirebump[i] = sound.AddSource(soundptr, 0, true, false);
	}

	//set up crash sound
	{
		std::tr1::shared_ptr<SOUNDBUFFER> soundptr;
		if (!content.load(carpath, "crash", soundptr)) return false;
		crashsound = sound.AddSource(soundptr, 0, true, false);
	}

	//set up gear sound
	{
		std::tr1::shared_ptr<SOUNDBUFFER> soundptr;
		if (!content.load(carpath, "gear", soundptr)) return false;
		gearsound = sound.AddSource(soundptr, 0, true, false);
	}

	//set up brake sound
	{
		std::tr1::shared_ptr<SOUNDBUFFER> soundptr;
		if (!content.load(carpath, "brake", soundptr)) return false;
		brakesound = sound.AddSource(soundptr, 0, true, false);
	}

	//set up handbrake sound
	{
		std::tr1::shared_ptr<SOUNDBUFFER> soundptr;
		if (!content.load(carpath, "handbrake", soundptr)) return false;
		handbrakesound = sound.AddSource(soundptr, 0, true, false);
	}

	{
		std::tr1::shared_ptr<SOUNDBUFFER> soundptr;
		if (!content.load(carpath, "wind", soundptr)) return false;
		roadnoise = sound.AddSource(soundptr, 0, true, true);
	}

	return true;
}
Example #5
0
bool CAR::LoadGraphics(
	const PTree & cfg,
	const std::string & partspath,
	const std::string & carpath,
	const std::string & carname,
	const std::string & carpaint,
	const MATHVECTOR <float, 3> & carcolor,
	const int anisotropy,
	const float camerabounce,
	const bool damage,
	const bool debugmode,
	ContentManager & content,
	std::ostream & info_output,
	std::ostream & error_output)
{
	//write_inf(cfg, std::cerr);
	cartype = carname;
	LoadDrawable loadDrawable(carpath, anisotropy, content, models, error_output);

	// load body first
	const PTree * cfg_body;
	std::string meshname;
	std::vector<std::string> texname;
	if (!cfg.get("body", cfg_body, error_output))
	{
		error_output << "there is a problem with the .car file" << std::endl;
		return false;
	}
	if (!cfg_body->get("mesh", meshname, error_output)) return false;
	if (!cfg_body->get("texture", texname, error_output)) return false;
	if (carpaint != "default") texname[0] = carpaint;
	if (!loadDrawable(meshname, texname, *cfg_body, topnode, &bodynode)) return false;

	// load wheels
	const PTree * cfg_wheel;
	if (!cfg.get("wheel", cfg_wheel, error_output)) return false;
	for (PTree::const_iterator i = cfg_wheel->begin(); i != cfg_wheel->end(); ++i)
	{
		if (!LoadWheel(i->second, loadDrawable, topnode, error_output))
		{
			error_output << "Failed to load wheels." << std::endl;
			return false;
		}
	}

	// load drawables
	LoadBody loadBody(topnode, bodynode, loadDrawable);
	for (PTree::const_iterator i = cfg.begin(); i != cfg.end(); ++i)
	{
		if (i->first != "body" &&
			i->first != "steering" &&
			i->first != "light-brake" &&
			i->first != "light-reverse")
		{
			loadBody(i->second);
		}
	}

	// load steering wheel
	const PTree * cfg_steer;
	if (cfg.get("steering", cfg_steer))
	{
		SCENENODE & bodynoderef = topnode.GetNode(bodynode);
		if (!loadDrawable(*cfg_steer, bodynoderef, &steernode, 0))
		{
			error_output << "Failed to load steering wheel." << std::endl;
			return false;
		}
		cfg_steer->get("max-angle", steer_angle_max);
		steer_angle_max = steer_angle_max / 180.0 * M_PI;
		SCENENODE & steernoderef = bodynoderef.GetNode(steernode);
		steer_orientation = steernoderef.GetTransform().GetRotation();
	}

	// load brake/reverse light point light sources (optional)
	int i = 0;
	std::string istr = "0";
	const PTree * cfg_light;
	while (cfg.get("light-brake-"+istr, cfg_light))
	{
		if (!LoadLight(*cfg_light, content, error_output))
		{
			error_output << "Failed to load lights." << std::endl;
			return false;
		}

		std::stringstream sstr;
		sstr << ++i;
		istr = sstr.str();
	}
	i = 0;
	istr = "0";
	while (cfg.get("light-reverse-"+istr, cfg_light))
	{
		if (!LoadLight(*cfg_light, content, error_output))
		{
			error_output << "Failed to load lights." << std::endl;
			return false;
		}

		std::stringstream sstr;
		sstr << ++i;
		istr = sstr.str();
	}

	// load car brake/reverse graphics (optional)
	if (cfg.get("light-brake", cfg_light))
	{
		SCENENODE & bodynoderef = topnode.GetNode(bodynode);
		if (!loadDrawable(*cfg_light, bodynoderef, 0, &brakelights))
		{
			error_output << "Failed to load lights." << std::endl;
			return false;
		}
	}
	if (cfg.get("light-reverse", cfg_light))
	{
		SCENENODE & bodynoderef = topnode.GetNode(bodynode);
		if (!loadDrawable(*cfg_light, bodynoderef, 0, &reverselights))
		{
			error_output << "Failed to load lights." << std::endl;
			return false;
		}
	}

	const PTree * cfg_cams;
	if (!cfg.get("camera", cfg_cams))
	{
		return false;
	}
	if (!cfg_cams->size())
	{
		error_output << "No cameras defined." << std::endl;
		return false;
	}
	cameras.reserve(cfg_cams->size());
	for (PTree::const_iterator i = cfg_cams->begin(); i != cfg_cams->end(); ++i)
	{
		CAMERA * cam = LoadCamera(i->second, camerabounce, error_output);
		if (!cam) return false;
		cameras.push_back(cam);
	}

	SetColor(carcolor[0], carcolor[1], carcolor[2]);

	return true;
}
Example #6
0
int main(int argc, char* argv[]) {
	
	if (argc != 4) {
		std::cout << "Aufruf mit Parametern: <französiche Trainigsdaten> <englische Trainingsdaten> <Alignment der Trainingsdaten>\n"
			<< "Folgende Ausgabe: relfreq_f relfreq_e # quellphrase # zielphrase # singlecf singlece # source_to_target target_to_source # unigram-sprachmodell\n";
		return 0;
	}
	Lexicon flex(french);
	Lexicon elex(english);
	PTree<std::pair<int, PTree<int> > > pTree;
	PTree<unsigned int> eSinglecount;

	uint eCount = 0;				//Gesamtzahl der englischen Wörter

	std::unordered_map<uint,Wordinfo> ef_pair, fe_pair;	//Einzelwortbasierte Übersetzungshäufigkeit von e nach f (und umgekehrt)
	
	igzstream f_in(argv[1]), e_in(argv[2]), a_in(argv[3]);
	std::string f_line, e_line, a_line;

	while (getline(f_in, f_line) && getline(e_in, e_line)) {
		
		/*==========Lies Wörter beider Sätze, sowie zugehörige Alignments aus entsprechenden Dateien aus==========*/
		std::string token;
		std:: istringstream f_ist(f_line), e_ist(e_line);
		std::vector<std::pair<uint, std::vector<unsigned int> > > f_vec, e_vec;	//Speichern alle Wörter jeweiliger Sprache und ihre Alignments
		
		//Füge alle französichen Wörter in ein Lexicon ein und schreibe ihre IDs in einen Vektor
		while(f_ist >> token) {
			uint id = flex.getWord_or_add(token);
			std::pair<uint, std::vector<unsigned int> > pair_tmp;
			pair_tmp.first = id;
			f_vec.push_back(pair_tmp);
		}

		//Füge alle englischen Wörter in ein Lexicon ein und schreibe ihre IDs in einen Vektor
		while (e_ist >> token) {
			uint id = elex.getWord_or_add(token);
			std::pair<uint, std::vector<unsigned int> > pair_tmp;
			pair_tmp.first = id;
			e_vec.push_back(pair_tmp);
			eCount++;
		}
		
		getline(a_in, a_line);	//"SEND:" abfangen
		do {
			getline(a_in, a_line);
			if(a_line == "") break;	//Alignment eine Satzes zu Ende
			else {
				std::istringstream a_ist(a_line);
				int f_ind, e_ind;
				std::string s;
				a_ist >> s >> f_ind >> e_ind;
				f_vec[f_ind].second.push_back(e_ind);	//Speichere einzelnes Alignment in f_vec
				e_vec[e_ind].second.push_back(f_ind);	//Speichere einzelnes Alignment in e_vec

				uint& f_id = f_vec[f_ind].first, e_id = e_vec[e_ind].first;
				fe_pair[f_id].pairs[e_id]++;		//Paircount für f nach e erhöhen
				fe_pair[f_id].singlecount++;		//Singlecount für f nach e erhöhen
				ef_pair[e_id].pairs[f_id]++;		//Paircount für e nach f erhöhen
				ef_pair[e_id].singlecount++;		//Singlecount für e nach f erhöhen
			}
		} while(true);

		/*==========Beide Sätze liegen nun in Vektoren gespeichert vor, die Alignments jedes Wortes sind in einem Vektor gespeichert==========
		 *==========Führe darauf nun den vorgegebenen Algorithmus aus, um alle Phrasen zu finden und im Präfixbaum zu speichern==========*/
		
		for(unsigned int j1 = 0; j1 < f_vec.size(); j1++)
			for(unsigned int j2 = j1; j2 < std::min(j1+3,(unsigned int)f_vec.size()); j2++) {	//Länge der Quellphrase maximal 3
				unsigned int i1, i2;
				bool set_i = false;						//hält mit, ob i1 und i2 gesetzt wurden, oder nicht.
				for(unsigned int k = j1; k <= j2; k++) 
					if(f_vec[k].second.size() && set_i) {
						i1 = std::min(i1, f_vec[k].second.front());	//Minimales Alignment innerhalb der Phrase finden => i1
						i2 = std::max(i2, f_vec[k].second.back());	//Maximales Alignment innerhalb der Phrase finden => i2
					} else if(f_vec[k].second.size() && !(set_i)) {
						i1 = f_vec[k].second.front();
						i2 = f_vec[k].second.back();
						set_i = true;
					}

				if (set_i){ 					//leere Phrasen werden nicht geprüft sondern direkt verworfen
					if(j1 == j2) {					//Einzelwortphrasen auf Quellseite werden IMMER extrahiert
						
						std::vector<uint> f_vec_tmp, e_vec_tmp;
						for (unsigned int k = j1; k <= j2; k++)
							f_vec_tmp.push_back(f_vec[k].first);	//Quellphrase in Vektor zusammenstellen
						for (unsigned int k = i1; k <= i2; k++)
							e_vec_tmp.push_back(e_vec[k].first);	//Zielphrase in Vektor zusammenstellen
						std::pair<int, PTree<int> > pair_tmp;
						pair_tmp.first = 0;
						pTree.traverse(f_vec_tmp,true,pair_tmp)->c.first++;				//Quellphrase in Baum einfügen
						pTree.traverse(f_vec_tmp,false)->c.second.traverse(e_vec_tmp,true,0)->c++;	//Zielphrase in "Unter-Baum" einfügen
						eSinglecount.traverse(e_vec_tmp,true,0)->c++;
					} else if (i2-i1 < 4) {	//Länge der Zielphrase maximal 4

						unsigned int j1_test, j2_test;
						bool set_j_test = false;			//hält mit, ob j1_test und j2_test gesetzt wurden, oder nicht
						for (unsigned int k = i1; k <= i2; k++)
							if (e_vec[k].second.size() && set_j_test) {
								j1_test = std::min(j1_test, e_vec[k].second.front());
								j2_test = std::max(j2_test, e_vec[k].second.back());
							} else if (e_vec[k].second.size() && !(set_j_test)) {
								j1_test = e_vec[k].second.front();
								j2_test = e_vec[k].second.back();
								set_j_test = true;
							}
						
						if (set_j_test)					//leere Phrasen werden nicht geprüft sondern sofort verworfen
						if ((j1_test >= j1) && (j2_test <= j2)) {	//Phrasen, die den Test bestehen, werden extrahiert
							
							std::vector<uint> f_vec_tmp, e_vec_tmp;
							for (unsigned int k = j1; k <= j2; k++)
								f_vec_tmp.push_back(f_vec[k].first);
							for (unsigned int k = i1; k <= i2; k++)
								e_vec_tmp.push_back(e_vec[k].first);
							std::pair<int, PTree<int> > pair_tmp;
							pair_tmp.first = 0;
							pTree.traverse(f_vec_tmp,true,pair_tmp)->c.first++;			//Quellphrase in Baum einfügen
							pTree.traverse(f_vec_tmp,false)->c.second.traverse(e_vec_tmp,true,0)->c++;	//Zielphrase in "Unter-Baum" einfügen
							eSinglecount.traverse(e_vec_tmp,true,0)->c++;
						}
					}
				}
				
			}
			/*==========Jetzt sind alle erlaubten Phrasen aus diesem Satzpaar im Präfixbaum gespeichert==========*/
			/*==========Damit ist die Bearbeitung dieses Satzpaares abgeschlossen und das nächste rückt nach==========*/
	}
	/*==========Nun sind alle erlaubten Phrasen aller Satzpaare - also der gesamten Testdaten - im Präfixbaum gespeichert==========*/
	/*==========Im Anschluss muss also der Präfixbaum in eine Phrasentabelle ausgegeben werden==========*/

	for (PTree<std::pair<int, PTree<int> > >::iterator itor1 = pTree.begin(); itor1 != pTree.end(); itor1++) {	//Durchlaufe den Baum

		int singlecount_f = (&*itor1) -> c.first;		//Zähler für Quellphrase auslesen
		if (singlecount_f) {
			std::vector<uint> source_id = (&*itor1) -> phrase();//Quellphrase (in IDs) auslesen
			std::string source_phrase = "";
			for (unsigned int k = 0; k < source_id.size(); k++)	//ID-Phrase in Stringphrase umwandeln
				source_phrase += flex.getString(source_id[k]) + " ";
			
			for(PTree<int>::iterator itor2 = (&*itor1) -> c.second.begin(); itor2 != (&*itor1) -> c.second.end(); itor2++) {	//Durchlaufe den "Unter-Baum"

				int paircount = (&*itor2) -> c;			//Zähler für Zielphrase auslesen
				if(paircount != 0) {
					std::vector<uint> target_id = (&*itor2) -> phrase();//Zielphrase (in IDs) auslesen
					std::string target_phrase = "";
					for (unsigned int k = 0; k < target_id.size(); k++)	//ID-Phrase in Stringphrase umwandeln
						target_phrase += elex.getString(target_id[k]) + " ";

					double source_to_target = 1;
					for (unsigned int k = 0; k < target_id.size(); k++) {
						double  sum_stt = 0;
						for (unsigned int l = 0; l < source_id.size(); l++) {
							sum_stt += (double) fe_pair[source_id[l]].pairs[target_id[k]] / (double) fe_pair[source_id[l]].singlecount;
						}
						source_to_target *= sum_stt / source_id.size();
					}
					source_to_target = -log(source_to_target);

					double target_to_source = 1;
					for (unsigned int k = 0; k < source_id.size(); k++) {
						double sum_tts = 0;
						for (unsigned int l = 0; l < target_id.size(); l++) {
							sum_tts += (double) ef_pair[target_id[l]].pairs[source_id[k]] /  (double) ef_pair[target_id[l]].singlecount;
						}
						target_to_source *= sum_tts / target_id.size();
					}
					target_to_source = -log(target_to_source);

					uint singlecount_e = eSinglecount.traverse(target_id)->c;
					double relFreqF = log(singlecount_f) - log(paircount);	//Bestimmen der relativen Wahrscheinlichkeit (negativer Logarithmus)
					double relFreqE = log(singlecount_e) - log(paircount);
					
					double unigram = log(eCount) - log(singlecount_e);
					
					std::cout << relFreqF << " " << relFreqE << " # " << source_phrase << "# " << target_phrase << "# " << singlecount_f << " "<< singlecount_e << " # " << source_to_target << " " << target_to_source << " # " << unigram << "\n";	//Ausgabe
				}
			}
		}
	}
	return 0;	
}
Example #7
0
bool CarGraphics::Load(
	const PTree & cfg,
	const std::string & carpath,
	const std::string & /*carname*/,
	const std::string & carwheel,
	const std::string & carpaint,
	const Vec3 & carcolor,
	const int anisotropy,
	const float camerabounce,
	ContentManager & content,
	std::ostream & error_output)
{
	assert(!loaded);

	// init drawable load functor
	LoadDrawable loadDrawable(carpath, anisotropy, content, models, textures, error_output);

	// load body first
	const PTree * cfg_body;
	std::string meshname;
	std::vector<std::string> texname;
	if (!cfg.get("body", cfg_body, error_output)) return false;
	if (!cfg_body->get("mesh", meshname, error_output)) return false;
	if (!cfg_body->get("texture", texname, error_output)) return false;
	if (carpaint != "default") texname[0] = carpaint;
	if (!loadDrawable(meshname, texname, *cfg_body, topnode, &bodynode)) return false;

	// load wheels
	const PTree * cfg_wheels;
	if (!cfg.get("wheel", cfg_wheels, error_output)) return false;

	std::shared_ptr<PTree> sel_wheel;
	if (carwheel != "default" && !content.load(sel_wheel, carpath, carwheel)) return false;

	for (PTree::const_iterator i = cfg_wheels->begin(); i != cfg_wheels->end(); ++i)
	{
		const PTree * cfg_wheel = &i->second;

		// override default wheel with selected, not very efficient, fixme
		PTree opt_wheel;
		if (sel_wheel.get())
		{
			opt_wheel.set(*sel_wheel);
			opt_wheel.merge(*cfg_wheel);
			cfg_wheel = &opt_wheel;
		}

		if (!LoadWheel(*cfg_wheel, loadDrawable, topnode, error_output))
		{
			error_output << "Failed to load wheels." << std::endl;
			return false;
		}
	}

	// load drawables
	LoadBody loadBody(topnode, bodynode, loadDrawable);
	for (PTree::const_iterator i = cfg.begin(); i != cfg.end(); ++i)
	{
		if (i->first != "body" &&
			i->first != "steering" &&
			i->first != "light-brake" &&
			i->first != "light-reverse")
		{
			loadBody(i->second);
		}
	}

	// load steering wheel
	const PTree * cfg_steer;
	if (cfg.get("steering", cfg_steer))
	{
		SceneNode & bodynoderef = topnode.GetNode(bodynode);
		if (!loadDrawable(*cfg_steer, bodynoderef, &steernode, 0))
		{
			error_output << "Failed to load steering wheel." << std::endl;
			return false;
		}
		cfg_steer->get("max-angle", steer_angle_max);
		steer_angle_max = steer_angle_max / 180.0 * M_PI;
		SceneNode & steernoderef = bodynoderef.GetNode(steernode);
		steer_orientation = steernoderef.GetTransform().GetRotation();
		steer_rotation = steer_orientation;
	}

	// load brake/reverse light point light sources (optional)
	int i = 0;
	std::string istr = "0";
	const PTree * cfg_light;
	while (cfg.get("light-brake-"+istr, cfg_light))
	{
		if (!LoadLight(*cfg_light, content, error_output))
		{
			error_output << "Failed to load lights." << std::endl;
			return false;
		}

		std::ostringstream sstr;
		sstr << ++i;
		istr = sstr.str();
	}
	i = 0;
	istr = "0";
	while (cfg.get("light-reverse-"+istr, cfg_light))
	{
		if (!LoadLight(*cfg_light, content, error_output))
		{
			error_output << "Failed to load lights." << std::endl;
			return false;
		}

		std::ostringstream sstr;
		sstr << ++i;
		istr = sstr.str();
	}

	// load car brake/reverse graphics (optional)
	if (cfg.get("light-brake", cfg_light))
	{
		SceneNode & bodynoderef = topnode.GetNode(bodynode);
		if (!loadDrawable(*cfg_light, bodynoderef, 0, &brakelights))
		{
			error_output << "Failed to load lights." << std::endl;
			return false;
		}
	}
	if (cfg.get("light-reverse", cfg_light))
	{
		SceneNode & bodynoderef = topnode.GetNode(bodynode);
		if (!loadDrawable(*cfg_light, bodynoderef, 0, &reverselights))
		{
			error_output << "Failed to load lights." << std::endl;
			return false;
		}
	}

	if (!LoadCameras(cfg, camerabounce, error_output))
	{
		return false;
	}

	SetColor(carcolor[0], carcolor[1], carcolor[2]);
	loaded = true;
	return true;
}