Track.cpp

Go to the documentation of this file.
00001 
00005 /* Copyright © 2009, 2010 James Legg.
00006     This program is free software: you can redistribute it and/or modify
00007     it under the terms of the GNU General Public License as published by
00008     the Free Software Foundation, either version 3 of the License, or
00009     (at your option) any later version.
00010 */
00011 #include "Track.h"
00012 
00013 #include "stream_loader.h"
00014 #include "FormErrors.h"
00015 
00016 #include <Debug.h>
00017 
00018 /* gcc >= 4.3 Depreciates hash_set & hash_map, causing warnings to be emited when
00019  * including boost graph library headers.
00020  * We don't need the hash-based storage selectors anyway, so turn them off.
00021  */
00022 #define BOOST_NO_HASH
00023 #include <boost/graph/adjacency_list.hpp>
00024 #include <boost/graph/dijkstra_shortest_paths.hpp>
00025 #undef BOOST_NO_HASH
00026 
00027 #include <cmath>
00028 
00029 const unsigned int track_newest_version = 3;
00030 
00031 namespace Track
00032 {
00033 
00034 Track::Track(std::istream & source, const Theme & theme, bool editor_in)
00035     :   file_version(int_from_stream(source))
00036     ,   theme(theme)
00037     ,   path(source, theme, this, editor_in)
00038     ,   editor(editor_in)
00039 {
00040     if (file_version == 0) throw CorruptedError();
00041     if (file_version == 3) m_lighting = Lighting(source);
00042     if (file_version > track_newest_version) throw NewVersionError();
00043     // version 1 contained some extra information, but we can ignore it
00044     // since it would have been at the end of the file.
00045     scan_theme();
00046 }
00047 
00048 Track::Track(const Theme & theme, bool editor_in)
00049     :   file_version(0)
00050     ,   theme(theme)
00051     ,   path(theme, this, editor_in)
00052     ,   editor(editor_in)
00053 {
00054     scan_theme();
00055 }
00056 
00057 
00058 Track::~Track()
00059 {
00060     
00061 }
00062 
00063 const Path & Track::get_path() const
00064 {
00065     return path;
00066 }
00067 
00068 Path & Track::get_path()
00069 {
00070     return path;
00071 }
00072 
00073 std::ostream & operator<<(std::ostream & destination, const Track & track)
00074 {
00075     destination << track_newest_version << ' ';
00076     destination << track.path << ' ' << track.get_lighting() << ' ';
00077     // the space at the end of the file prevents an error reading the last
00078     // number. end of file is not end of number, apparently.
00079     return destination;
00080 }
00081 
00082 const Theme & Track::get_theme() const
00083 {
00084     return theme;
00085 }
00086 
00090 template <class A>
00091 class CollisionDeallocator
00092 {
00093     public:
00097         CollisionDeallocator(A *mesh)
00098             :   m_mesh(mesh)
00099             {};
00104         template <class T>
00105         void operator ()(T shape)
00106         {
00107             delete shape;
00108             delete m_mesh;
00109         }
00110     private:
00112         A *m_mesh;
00113     
00114 };
00115 
00116 boost::shared_ptr<btCollisionShape> Track::get_collision_shape() const
00117 {
00118     btTriangleMesh *  shape = new btTriangleMesh;
00119     path.add_collision_faces(*shape);
00120     
00121     boost::shared_ptr<btCollisionShape> result(new btBvhTriangleMeshShape(shape, true),
00122                                                CollisionDeallocator<btTriangleMesh>(shape));
00123     return result;
00124 }
00125 
00126 boost::shared_ptr<btCollisionShape> Track::get_floor_shape() const
00127 {
00128     btTriangleMesh * shape = new btTriangleMesh;
00129     path.add_floor_faces(*shape);
00130     boost::shared_ptr<btCollisionShape> result(new btBvhTriangleMeshShape(shape, true),
00131                                                CollisionDeallocator<btTriangleMesh>(shape));
00132     return result;
00133 }
00134 
00135 const MeshFaces & Track::get_ai_mesh() const
00136 {
00137     return m_ai_mesh;
00138 }
00139 
00140 void Track::update_ai_mesh()
00141 {
00142     // reset incase this was used before a change.
00143     DEBUG_MESSAGE("Finding AI navigation mesh.");
00144     m_ai_mesh = MeshFaces();
00145     path.add_ai_faces(m_ai_mesh);
00146     // Merge vertices near each other, so the mesh can be navigated cleanly
00147     // by the AI cars.
00148     m_ai_mesh.merge_doubles();
00149     
00150     // Find the connectivity graph.
00151     scan_ai_graph();
00152     m_ai_graph = m_ai_mesh.get_connectivity();
00153 }
00154 
00155 struct EdgeProperties
00156 {
00157     float distance;
00158 };
00159 
00160 void Track::scan_ai_graph()
00161 {
00162     /* We want to set the u texture coordinate to a number indicating how far
00163      * into a lap a position is. This number is called lap_position.
00164      * 0.0 means the start of the lap. lap_position should vary linearly with
00165      * distance from the start.
00166      * We create a graph related to the navigation graph to run dijkstra's
00167      * algorithm on. We use the minimum distances assigned to each node as the
00168      * lap_position.
00169      * The new graph is a variation on the graph of connected vertices on the
00170      * ai mesh. The vertices along the starting plane are split so that faces
00171      * on either side of the starting plane are not connected across the
00172      * starting plane.
00173      * A source node is created which has 0 distance to all vertices along the
00174      * starting plane connecting with faces infront of it. Similarly, a sink
00175      * node is created with 0 distance to the nodes in the starting plane
00176      * connecting with faces behind it. This way position along the starting
00177      * plane doesn't matter, and it doesn't mess with ordering cars near the
00178      * starting plane.
00179      *
00180      * A possible improvement to this algorithm would take note of splits
00181      * where the paths are very different lengths. Currently this sort of
00182      * thing could happen:
00183      *  |  6  |
00184      *  |5 ^ 7| < goes backwards here. The long path should be scaled down so
00185      *  | | \8\                        that lap_position varies between
00186      *  |4|  \8\                       the values at the start and end of the
00187      *  | |   \7\                      split section.
00188      *  |3|    \6\
00189      *  | |    /5/
00190      *  |2|   /4/
00191      *  | |  /3/
00192      *  |1| /2/
00193      *  |  _ 1|
00194      *  |0   0|
00195      * 
00196      *  You could say this would be bad level design, but if the short path
00197      *  requires going much slower than the long path, or the long path
00198      *  provides an additional benifit (e.g. pit lane), the track could be
00199      *  well balanced and still suffer from this.
00200      * 
00201      * This could be done with a graph layout algorithm, though care must be
00202      * taken to insure the starting vertices stay at 0 and the ending vertices
00203      * have the largest value.
00204      */
00205     DEBUG_MESSAGE("scan_ai_graph: Getting vertex graph");
00206     MeshFaces::VertexGraph graph = m_ai_mesh.get_vertex_graph();
00207     // Add source and sink.
00208     MeshFaces::VertexGraph::vertex_descriptor source_descriptor;
00209     {
00210         source_descriptor = boost::add_vertex(graph);
00211         DEBUG_MESSAGE("Source node is " << source_descriptor);
00212     }
00213     MeshFaces::VertexGraph::vertex_descriptor sink_descriptor;
00214     {
00215         sink_descriptor = boost::add_vertex(graph);
00216         DEBUG_MESSAGE("Sink node is " << sink_descriptor);
00217     }
00218     
00219     // split start and end vertices, which lie along the start/finish plane.
00220     // First, what is the plane?
00221     btVector3 start_point = path.find_start_position();
00222     btVector3 start_plane_normal;
00223     btScalar start_plane_distance;
00224     path.find_start_plane(start_plane_normal, start_plane_distance);
00225     // Find the vertices we want.
00226     std::vector<MeshFaces::VertexGraph::vertex_descriptor> start_vertices;
00227     std::pair<MeshFaces::VertexGraph::vertex_iterator,
00228               MeshFaces::VertexGraph::vertex_iterator> its;
00229     for (its = boost::vertices(graph); its.first != its.second; its.first++)
00230     {
00231         MeshFaces::VertexGraph::vertex_descriptor vertex = *(its.first);
00232         btVector3 position = graph[vertex];
00233         // find distance from plane.
00234         btScalar to_plane = start_plane_normal.dot(position) + start_plane_distance;
00235         if (std::abs(to_plane) < 0.001)
00236         {
00237             // close enough to start plane to be in it.
00238             // Is it near enough to the start, or is it somewhere in the distance?
00244             if (start_point.distance2(position) < 289.0)
00245             {
00246                 // It will do.
00247                 start_vertices.push_back(vertex);
00248                 DEBUG_MESSAGE("Vertex " << vertex << " is on the start plane, distance^2=" << to_plane << ".");
00249             }
00250         }
00251     }
00252     // add copies of the vertices found to the graph.
00253     std::vector<MeshFaces::VertexGraph::vertex_descriptor> finish_vertices;
00254     for (std::vector<MeshFaces::VertexGraph::vertex_descriptor>::iterator it = start_vertices.begin();
00255          it != start_vertices.end();
00256          it++)
00257     {
00258         finish_vertices.push_back(boost::add_vertex(graph[*it], (graph)));
00259         DEBUG_MESSAGE("Vertex " << finish_vertices.back() << " is the end of track equivalent to vertex " << *it);
00260     }
00261     // now work out which edges should be moved from start to finish.
00262     std::vector<MeshFaces::VertexGraph::vertex_descriptor>::iterator start_it, finish_it;
00263     for (start_it = start_vertices.begin(), finish_it = finish_vertices.begin();
00264          start_it != start_vertices.end();
00265          start_it++, finish_it++)
00266     {
00267         typedef MeshFaces::VertexGraph::out_edge_iterator OutEdgeIterator;
00268         std::pair<OutEdgeIterator, OutEdgeIterator> its;
00269         for (its = boost::out_edges(*start_it, graph);
00270              its.first != its.second;
00271              its.first++)
00272         {
00273             // which side of the plane does this edge take it?
00274             MeshFaces::VertexGraph::vertex_descriptor target = boost::target(*(its.first), graph);
00275             // which side of the plane?
00276             btScalar plane_distance = start_plane_normal.dot(graph[target]) + start_plane_distance;
00277             if (plane_distance < -0.001)
00278             {
00279                 // behind the plane. Move the edge to the other side.
00280                 DEBUG_MESSAGE("Moving edge " << *(its.first) << " from start vertex " << *start_it << " to finish vertex " << *finish_it << ".");
00281                 boost::add_edge(*finish_it, target, graph[*(its.first)], graph);
00282                 boost::remove_edge(*(its.first), graph);
00283                 // edge iterators invalidated. Start again.
00287                 its = boost::out_edges(*start_it, graph);
00288             }
00289         }
00290     }
00291     // Link up start to source vertex.
00292     for (std::vector<MeshFaces::VertexGraph::vertex_descriptor>::iterator it = start_vertices.begin();
00293          it != start_vertices.end();
00294          it++)
00295     {
00296         // Use a large length to prevent the shortest path of nearby nodes
00297         // going backwards.
00298         boost::add_edge(source_descriptor, *it, MeshFaces::FaceEdgePointers(0, 0, 0, 100.0), graph);
00299     }
00300     // Link up finish to sink vertex.
00301     for (std::vector<MeshFaces::VertexGraph::vertex_descriptor>::iterator it = finish_vertices.begin();
00302          it != finish_vertices.end();
00303          it++)
00304     {
00305         boost::add_edge(sink_descriptor, *it, MeshFaces::FaceEdgePointers(0, 0, 0, 0.0), graph);
00306     }
00307     
00308     /* Now we have the graph we want. Lets find the minimal distance to each
00309      * vertex from the sink node.
00310      */
00311     std::vector<MeshFaces::VertexGraph::vertex_descriptor> predecessors(boost::num_vertices(graph));
00312     std::vector<btScalar> distances(boost::num_vertices(graph));
00313     boost::dijkstra_shortest_paths(graph, sink_descriptor,
00314         boost::weight_map(get(&MeshFaces::FaceEdgePointers::length, graph)).
00315         predecessor_map(&predecessors[0]).distance_map(&distances[0])
00316         );
00317     // The length of the lap is the shortest distance from end to finish,
00318     // without the 100m length of the connections to the source node.
00319     m_lap_length = distances[source_descriptor] - 100.0;
00320     /* Now we want to put the distances back into the mesh.
00321      * We use the u coordinate for face vertices.
00322      * The same vertex will appear on multiple faces. Normally each vertex has
00323      * only one value, but the vertices in start_vertices get 0 for the faces
00324      * infront of the start plane and distances[sink_descriptor] behind it.
00325      */
00326     // For each edge, set the right distances on each end.
00327     std::pair<MeshFaces::VertexGraph::edge_iterator,
00328               MeshFaces::VertexGraph::edge_iterator> edge_its;
00329     btScalar end_cut_off = m_lap_length / 2.0;
00330     for (edge_its = boost::edges(graph); edge_its.first != edge_its.second; edge_its.first++)
00331     {
00332         MeshFaces::VertexGraph::edge_descriptor edge = *(edge_its.first);
00333         MeshFaces::FaceEdgePointers & fep = graph[edge];
00334         // Each face vertex is set twice due to the edges both sides of it.
00335         if (fep.source)
00336         {
00337             if ((    boost::source(edge, graph) > sink_descriptor
00338                   || boost::target(edge, graph) > sink_descriptor)
00339                 && distances[fep.source->vertex_index] > end_cut_off)
00340             {
00341                 // Must be a copy of a starting vertex placed on the end.
00342                 // This edge connects to the end of the course.
00343                 fep.source->texture_coord_u = m_lap_length;
00344             }
00345             else
00346             {
00347                 fep.source->texture_coord_u = m_lap_length - distances[fep.source->vertex_index];
00348             }
00349         }
00350         if (fep.target)
00351         {
00352             if ((   boost::target(edge, graph) > sink_descriptor
00353                   || boost::source(edge, graph) > sink_descriptor)
00354                 && distances[fep.target->vertex_index] > end_cut_off)
00355             {
00356                 // Must be a copy of a starting vertex placed on the end.
00357                 // This edge connects to the end of the course.
00358                 fep.target->texture_coord_u = m_lap_length;
00359             }
00360             else
00361             {
00362                 fep.target->texture_coord_u = m_lap_length - distances[fep.target->vertex_index];
00363             }
00364         }
00365     }
00366     DEBUG_MESSAGE("Track is " << m_lap_length << "m long.");
00367 }
00368 
00369 const MeshFaces::Graph & Track::get_ai_graph() const
00370 {
00371     return m_ai_graph;
00372 }
00373 
00374 btScalar Track::get_lap_length() const
00375 {
00376     return m_lap_length;
00377 }
00378 
00379 void Track::set_filename(std::string filename_in)
00380 {
00381     filename = filename_in;
00382 }
00383 
00384 std::string Track::get_filename() const
00385 {
00386     return filename;
00387 }
00388 
00389 void Track::scan_theme()
00390 {
00391     if (!editor) return;
00392     std::size_t range = theme.get_number_of_segments();
00393     for (std::size_t index = 0; index < range; index++)
00394     {
00400         const_cast<DrawableMesh&>(theme.get_segment(index).get_graphics_mesh().get_faces()).set_render_mode(DrawableMesh::RM_WIREFRAME);
00401     }
00402 }
00403 
00404 const Lighting & Track::get_lighting() const
00405 {
00406     return m_lighting;
00407 }
00408 
00409 void Track::set_lighting(const Lighting & lighting)
00410 {
00411     m_lighting = lighting;
00412 }
00413 
00414 };

Get Racer at SourceForge.net. Fast, secure and Free Open Source software downloads

Generated at Mon Sep 6 00:41:12 2010 by Doxygen version 1.4.7 for Racer version svn335.