/** Recursively determines the distance the beginning (lower end) of the quads * have from the start of the track. * \param node The node index for which to set the distance from start. * \param new_distance The new distance for the specified graph node. */ void QuadGraph::computeDistanceFromStart(unsigned int node, float new_distance) { GraphNode *gn = m_all_nodes[node]; float current_distance = gn->getDistanceFromStart(); // If this node already has a distance defined, check if the new distance // is longer, and if so adjust all following nodes. Without this the // length of the track (as taken by the distance from start of the last // node) could be smaller than some of the paths. This can result in // incorrect results for the arrival time estimation of the AI karts. // See trac #354 for details. // Then there is no need to test/adjust any more nodes. if(current_distance>=0) { if(current_distance<new_distance) { float delta = new_distance - current_distance; updateDistancesForAllSuccessors(gn->getQuadIndex(), delta, 0); } return; } // Otherwise this node has no distance defined yet. Set the new // distance, and recursively update all following nodes. gn->setDistanceFromStart(new_distance); for(unsigned int i=0; i<gn->getNumberOfSuccessors(); i++) { GraphNode *gn_next = m_all_nodes[gn->getSuccessor(i)]; // The start node (only node with distance 0) is reached again, // recursion can stop now if(gn_next->getDistanceFromStart()==0) continue; computeDistanceFromStart(gn_next->getQuadIndex(), new_distance + gn->getDistanceToSuccessor(i)); } // for i } // computeDistanceFromStart
/** Loads a quad graph from a file. * \param filename Name of the file to load. */ void QuadGraph::load(const std::string &filename) { const XMLNode *xml = file_manager->createXMLTree(filename); if(!xml) { // No graph file exist, assume a default loop X -> X+1 // i.e. each quad is part of the graph exactly once. // First create an empty graph node for each quad: for(unsigned int i=0; i<QuadSet::get()->getNumberOfQuads(); i++) m_all_nodes.push_back(new GraphNode(i, (unsigned int) m_all_nodes.size())); // Then set the default loop: setDefaultSuccessors(); computeDirectionData(); if (m_all_nodes.size() > 0) { m_lap_length = m_all_nodes[m_all_nodes.size()-1]->getDistanceFromStart() + m_all_nodes[m_all_nodes.size()-1]->getDistanceToSuccessor(0); } else { Log::error("Quad Graph", "No node in driveline graph."); m_lap_length = 10.0f; } return; } // The graph file exist, so read it in. The graph file must first contain // the node definitions, before the edges can be set. for(unsigned int node_index=0; node_index<xml->getNumNodes(); node_index++) { const XMLNode *xml_node = xml->getNode(node_index); // First graph node definitions: // ----------------------------- if(xml_node->getName()=="node-list") { // A list of quads is connected to a list of graph nodes: unsigned int from, to; xml_node->get("from-quad", &from); xml_node->get("to-quad", &to); for(unsigned int i=from; i<=to; i++) { m_all_nodes.push_back(new GraphNode(i, (unsigned int) m_all_nodes.size())); } } else if(xml_node->getName()=="node") { // A single quad is connected to a single graph node. unsigned int id; xml_node->get("quad", &id); m_all_nodes.push_back(new GraphNode(id, (unsigned int) m_all_nodes.size())); } // Then the definition of edges between the graph nodes: // ----------------------------------------------------- else if(xml_node->getName()=="edge-loop") { // A closed loop: unsigned int from, to; xml_node->get("from", &from); xml_node->get("to", &to); for(unsigned int i=from; i<=to; i++) { assert(i!=to ? i+1 : from <m_all_nodes.size()); addSuccessor(i,(i!=to ? i+1 : from)); //~ m_all_nodes[i]->addSuccessor(i!=to ? i+1 : from); } } else if(xml_node->getName()=="edge-line") { // A line: unsigned int from, to; xml_node->get("from", &from); xml_node->get("to", &to); for(unsigned int i=from; i<to; i++) { addSuccessor(i,i+1); //~ m_all_nodes[i]->addSuccessor(i+1); } } else if(xml_node->getName()=="edge") { // Adds a single edge to the graph: unsigned int from, to; xml_node->get("from", &from); xml_node->get("to", &to); assert(to<m_all_nodes.size()); addSuccessor(from,to); //~ m_all_nodes[from]->addSuccessor(to); } // edge else { Log::error("Quad Graph", "Incorrect specification in '%s': '%s' ignored.", filename.c_str(), xml_node->getName().c_str()); continue; } // incorrect specification } delete xml; setDefaultSuccessors(); computeDistanceFromStart(getStartNode(), 0.0f); computeDirectionData(); // Define the track length as the maximum at the end of a quad // (i.e. distance_from_start + length till successor 0). m_lap_length = -1; for(unsigned int i=0; i<m_all_nodes.size(); i++) { float l = m_all_nodes[i]->getDistanceFromStart() + m_all_nodes[i]->getDistanceToSuccessor(0); if(l > m_lap_length) m_lap_length = l; } } // load
/** Loads a drive graph from a file. * \param filename Name of the quad file to load. * \param filename Name of the graph file to load. */ void DriveGraph::load(const std::string &quad_file_name, const std::string &filename) { XMLNode *quad = file_manager->createXMLTree(quad_file_name); if (!quad || quad->getName() != "quads") { Log::error("DriveGraph : Quad xml '%s' not found.", filename.c_str()); delete quad; return; } // Each quad is part of the graph exactly once now. for (unsigned int i = 0; i < quad->getNumNodes(); i++) { const XMLNode *xml_node = quad->getNode(i); if (xml_node->getName() != "quad") { Log::warn("DriveGraph: Unsupported node type '%s' found in '%s' - ignored.", xml_node->getName().c_str(), filename.c_str()); continue; } // Note that it's not easy to do the reading of the parameters here // in quad, since the specification in the xml can contain references // to previous points. E.g.: // <quad p0="40:3" p1="40:2" p2="25.396030 0.770338 64.796539" ... Vec3 p0, p1, p2, p3; getPoint(xml_node, "p0", &p0); getPoint(xml_node, "p1", &p1); getPoint(xml_node, "p2", &p2); getPoint(xml_node, "p3", &p3); bool invisible = false; xml_node->get("invisible", &invisible); bool ai_ignore = false; xml_node->get("ai-ignore", &ai_ignore); bool ignored = false; std::string direction; xml_node->get("direction", &direction); if (direction == "forward" && race_manager->getReverseTrack()) { ignored = true; invisible = true; ai_ignore = true; } else if (direction == "reverse" && !race_manager->getReverseTrack()) { ignored = true; invisible = true; ai_ignore = true; } createQuad(p0, p1, p2, p3, m_all_nodes.size(), invisible, ai_ignore, false/*is_arena*/, ignored); } delete quad; const XMLNode *xml = file_manager->createXMLTree(filename); if(!xml) { // No graph file exist, assume a default loop X -> X+1 // Set the default loop: setDefaultSuccessors(); computeDirectionData(); if (m_all_nodes.size() > 0) { m_lap_length = getNode(m_all_nodes.size()-1)->getDistanceFromStart() + getNode(m_all_nodes.size()-1)->getDistanceToSuccessor(0); } else { Log::error("DriveGraph", "No node in driveline graph."); m_lap_length = 10.0f; } return; } // The graph file exist, so read it in. The graph file must first contain // the node definitions, before the edges can be set. for(unsigned int node_index=0; node_index<xml->getNumNodes(); node_index++) { const XMLNode *xml_node = xml->getNode(node_index); // Load the definition of edges between the graph nodes: // ----------------------------------------------------- if (xml_node->getName() == "node-list") { // Each quad is part of the graph exactly once now. unsigned int to = 0; xml_node->get("to-quad", &to); assert(to + 1 == m_all_nodes.size()); continue; } else if(xml_node->getName()=="edge-loop") { // A closed loop: unsigned int from, to; xml_node->get("from", &from); xml_node->get("to", &to); for(unsigned int i=from; i<=to; i++) { assert(i!=to ? i+1 : from <m_all_nodes.size()); addSuccessor(i,(i!=to ? i+1 : from)); //~ m_all_nodes[i]->addSuccessor(i!=to ? i+1 : from); } } else if(xml_node->getName()=="edge-line") { // A line: unsigned int from, to; xml_node->get("from", &from); xml_node->get("to", &to); for(unsigned int i=from; i<to; i++) { addSuccessor(i,i+1); //~ m_all_nodes[i]->addSuccessor(i+1); } } else if(xml_node->getName()=="edge") { // Adds a single edge to the graph: unsigned int from, to; xml_node->get("from", &from); xml_node->get("to", &to); assert(to<m_all_nodes.size()); addSuccessor(from,to); //~ m_all_nodes[from]->addSuccessor(to); } // edge else { Log::error("DriveGraph", "Incorrect specification in '%s': '%s' ignored.", filename.c_str(), xml_node->getName().c_str()); continue; } // incorrect specification } delete xml; setDefaultSuccessors(); computeDistanceFromStart(getStartNode(), 0.0f); computeDirectionData(); // Define the track length as the maximum at the end of a quad // (i.e. distance_from_start + length till successor 0). m_lap_length = -1; for(unsigned int i=0; i<m_all_nodes.size(); i++) { float l = getNode(i)->getDistanceFromStart() + getNode(i)->getDistanceToSuccessor(0); if(l > m_lap_length) m_lap_length = l; } loadBoundingBoxNodes(); } // load