PathEdge.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 
00012 #include "PathEdge.h"
00013 #include "../stream_loader.h"
00014 #include "../FormErrors.h"
00015 #include "Path.h"
00016 
00017 namespace Track
00018 {
00019 
00020 const unsigned int path_edge_newest_version = 2;
00021 
00022 PathEdge::PathEdge(const Theme & theme)
00023     :   file_version(0)
00024     ,   theme(theme)
00025     ,   segment_index(0)
00026     ,   segment(0)
00027     ,   start(get_name(), EditAssist::EdgeStrengthHandle::EE_SOURCE)
00028     ,   finish(get_name(), EditAssist::EdgeStrengthHandle::EE_DESTINATION)
00029     ,   render_mode(DrawableMesh::RM_SOLID)
00030 {
00031     
00032 }
00033 
00034 PathEdge::PathEdge(std::istream & source, const Theme & theme)
00035     :   file_version(int_from_stream(source))
00036     ,   theme(theme)
00037     ,   segment_index(theme.get_segment_index(string_from_stream(source)))
00038     ,   segment(&theme.get_segment(segment_index))
00039     ,   start(source, get_name(), EditAssist::EdgeStrengthHandle::EE_SOURCE)
00040     ,   finish(source, get_name(), EditAssist::EdgeStrengthHandle::EE_DESTINATION)
00041     ,   render_mode(DrawableMesh::RM_SOLID)
00042 {
00043     if (file_version == 0)  throw CorruptedError();
00044     if (file_version > path_edge_newest_version) throw NewVersionError();
00045     if (file_version >= 2)
00046     {
00047         SaveableClassList::get_instance()->fill_vector(source, m_attachments);
00048         // Link all the attachments to this edge so they can be safely added and removed.
00049         for (std::vector<boost::shared_ptr<TrackAttachment> >::iterator it = m_attachments.begin();
00050               it != m_attachments.end();
00051               it++)
00052         {
00053             (**it).edge_name = get_name();
00054         }
00055     }
00056 }
00057 
00058 PathEdge::~PathEdge()
00059 {
00060     
00061 }
00062 
00063 // calculate good sizes of steps to take in approximation of edge length.
00064 template <int t>
00065 struct one_over_two_to_the_power_of
00066 {
00067     static const btScalar result = 0.5 * one_over_two_to_the_power_of<t - 1>::result;
00068 };
00069 
00070 template <>
00071 struct one_over_two_to_the_power_of<0>
00072 {
00073     static const btScalar result = 1.0;
00074 };
00075 
00076 
00077 void PathEdge::update(std::size_t start_vertex_index_in,
00078                       std::size_t finish_vertex_index_in,
00079                       const Path * path_in)
00080 {
00081     start_vertex_index = start_vertex_index_in;
00082     finish_vertex_index = finish_vertex_index_in;
00083     path = path_in;
00084     update();
00085 }
00086 
00087 void PathEdge::update()
00088 {
00089     const PathVertex & source_in = path->get_node(start_vertex_index);
00090     const PathVertex & target_in = path->get_node(finish_vertex_index);
00091     
00092     // reset the axis aligned bounding box we'll build it up later.
00093     bounds = AxisAlignedBoundingBox();
00094     graphics_bounds = bounds;
00095     
00096     // A cubic bezier spline isn't analytically intergratable.
00097     // Instead of hard numerical approximations, I'll do a piecewise linear
00098     // estimation.
00099     const std::size_t start_index = start.segment_connection_index;
00100     const std::size_t finish_index = finish.segment_connection_index;
00101     const btVector3 & p0 = source_in.get_position(start_index);
00102     const btVector3 & p3 = target_in.get_position(finish_index);
00103     
00104     btVector3 last_position = get_transform(0).getOrigin();
00105     length = 0;
00106     const btScalar step = one_over_two_to_the_power_of<5>::result;
00107     for (btScalar t = step; t < 1.0; t += step)
00108     {
00109         const btVector3 this_position = get_transform(t).getOrigin();
00110         length += (this_position - last_position).length();
00111         last_position = this_position;
00112     }
00113     
00114     // how many repetions of the segment do we need?
00115     if (segment)
00116     {
00117         // round to the nearest whole number of segments.
00118         number_of_repetions = int (length / segment->get_length() + 0.5);
00119     }
00120     if (number_of_repetions == 0)
00121     {
00122         // even short gaps between vertices must be filled.
00123         number_of_repetions = 1;
00124     }
00125     if (number_of_repetions > 64)
00126     {
00127         DEBUG_MESSAGE("Warning: large number of meshes (" << number_of_repetions << ") needed for edge " << get_name());
00128     }
00129     // make the meshes.
00130     meshes.clear();
00131     meshes.reserve(number_of_repetions);
00132     for (unsigned int i = 0; i < number_of_repetions; i++)
00133     {
00134         PieceDistortion transform(*this, i,
00135                                          segment->get_length(),
00136                                          segment->get_minimum_y());
00137         meshes.push_back(boost::shared_ptr<DrawableMesh>(
00138             new DrawableMesh(segment->get_graphics_mesh().get_distorted_faces(transform), render_mode)
00139         ));
00140         graphics_bounds |= meshes.back()->get_bounds();
00141     }
00142     
00143     // update control point handles.
00144     start.handle.update(p0, source_in.get_angle(start_index), start.gradient_strength);
00145     finish.handle.update(p3, target_in.get_angle(finish_index), finish.gradient_strength);
00146     recreate_attachment_handles();
00147     
00148     bounds |= graphics_bounds;
00149     bounds |= start.handle.get_position();
00150     bounds |= finish.handle.get_position();
00151     
00152     // Navigation mesh
00153     m_nav_mesh = MeshFaces();
00154     for (unsigned int i = 0; i < number_of_repetions; i++)
00155     {
00156         PieceDistortion transform(*this, i,
00157                                          segment->get_length(),
00158                                          segment->get_minimum_y());
00159         m_nav_mesh |= segment->get_ai_mesh().get_distorted_faces(transform);
00160     }
00161     m_nav_mesh.merge_doubles();
00162     m_nav_mesh.set_source(true, get_name());
00163     m_navigation_graph = m_nav_mesh.get_connectivity();
00164 }
00165 
00166 btScalar PathEdge::get_length() const
00167 {
00168     return length;
00169 }
00170 
00171 unsigned int PathEdge::get_number_of_repetions() const
00172 {
00173     return number_of_repetions;
00174 }
00175 
00176 btTransform PathEdge::get_transform(btScalar position) const
00177 {
00178     const std::size_t start_index = start.segment_connection_index;
00179     const std::size_t finish_index = finish.segment_connection_index;
00180     const btScalar rposition = 1.0 - position;
00181     const btScalar position_squared = position * position;
00182     const btScalar rposition_squared = rposition * rposition;
00183     const btScalar position_cubed = position_squared * position;
00184     const btScalar rposition_cubed = rposition_squared * rposition;
00185     const PathVertex & start_vertex = path->get_node(start_vertex_index);
00186     const btVector3 & p0 = start_vertex.get_position(start_index);
00187     const btVector3 p1 = p0 + start_vertex.get_gradient(start_index) * start.gradient_strength;
00188     const PathVertex & finish_vertex = path->get_node(finish_vertex_index);
00189     const btVector3 & p3 = finish_vertex.get_position(finish_index);
00190     const btVector3 p2 = p3 + finish_vertex.get_gradient(finish_index) * finish.gradient_strength;
00191     const btVector3 translation =   rposition_cubed * p0
00192                                   + 3.0 * rposition_squared * position * p1
00193                                   + 3.0 * rposition * position_squared * p2
00194                                   + position_cubed * p3;
00195     // find tangent for rotation.
00196     const btVector3 start_tangent = p1 - p0;
00197     const btVector3 end_tangent = p3 - p2;
00198     /* The tangent's control points should be tripled, but this cancels since we
00199      * immediately normalise.
00200      */
00201     const btVector3 tangent = ( rposition_squared * start_tangent
00202                                 + 2.0 * rposition * position * (p2 - p1)
00203                                 + position_squared * end_tangent)
00204                                .normalize();
00205     // roatation is such that the xz plane is perpendicular to the tangent.
00206     // rotation around the tangent is specified by an up vector.
00207     // We get the up vector from slerp of the tangenets at the end vertices.
00208     const btVector3 up = start_vertex.get_up(start_index).lerp(finish_vertex.get_up(finish_index), position)
00209                                          .normalize();
00211     // posible better up vector, but is sometimes still unintuitive.
00212     /*const btVector3 up = btTransform(source->get_angle().slerp(target->get_angle(), position))
00213                                     (btVector3(0.0, 0.0, 1.0));*/
00214     // Find the cross product of tangent and up to make the other axis.
00215     const btVector3 cross_axis = tangent.cross(up).normalize();
00216     // up might not be orthoganal to the other directions, improve it:
00217     const btVector3 better_up = cross_axis.cross(tangent);
00218     btMatrix3x3 rotation(cross_axis.x(), tangent.x(), better_up.x(),
00219                          cross_axis.y(), tangent.y(), better_up.y(),
00220                          cross_axis.z(), tangent.z(), better_up.z());
00221     
00222     return btTransform(rotation, translation);
00223 }
00224 
00225 const boost::shared_ptr<DrawableMesh> PathEdge::get_graphics_mesh(std::size_t index) const
00226 {
00227     assert(index < number_of_repetions);
00228     return meshes[index];
00229 }
00230 
00231 bool PathEdge::is_here(btVector3 start, btVector3 stop, btScalar radius) const
00232 {
00233     /* We should find the smallest distance between the line segment between
00234      * start and stop and the cubic bezier spline of the edge.
00235      * Instead, we'll find an approximation by using a piecewise linear
00236      * approximation of the cubic bezier.
00237      */
00238     btVector3 length = stop - start;
00239     btVector3 e2, e1;
00241     e2 = get_transform(0).getOrigin();
00242     const btScalar step = one_over_two_to_the_power_of<4>::result;
00243     for (btScalar t = step; t < 1.0; t += step)
00244     {
00245         e1 = e2;
00246         e2 = get_transform(t).getOrigin();
00247         // now e1->e2 is the line segment we will test.
00248         btVector3 ed = e2 - e1;
00249         btVector3 w = start - e1;
00250         btScalar a = length.dot(length);
00251         btScalar b = length.dot(ed);
00252         btScalar c = ed.dot(ed);
00253         btScalar d = length.dot(w);
00254         btScalar e = ed.dot(w);
00255         btScalar denominator = a * c - b * b;
00256         btScalar screen = b * e - c * d;
00257         btScalar section = a * e - b * d;
00258         // Now check if this is along the segments.
00259         if (screen < 0) screen = 0;
00260         if (screen > denominator) screen = denominator;
00261         if (section < 0) section = 0;
00262         if (section > denominator) section = denominator;
00263         // find distance
00264         btScalar distance = (w + (screen * length - section * ed) / denominator).length2();
00265         if (distance < radius * radius)
00266         {
00267             return true;
00268         }
00269     }
00270     return false;
00271 }
00272 
00273 float PathEdge::get_nearest_s(btVector3 start, btVector3 stop) const
00274 {
00275     /* We should find the smallest distance between the line segment between
00276      * start and stop and the cubic bezier spline of the edge.
00277      * Instead, we'll find an approximation by using a piecewise linear
00278      * approximation of the cubic bezier.
00279      */
00283     btVector3 length = stop - start;
00284     btVector3 e2, e1;
00286     e2 = get_transform(0).getOrigin();
00287     const btScalar step = one_over_two_to_the_power_of<4>::result;
00288     btScalar min_distance = std::numeric_limits<btScalar>::max();
00289     btScalar best_result;
00290     for (btScalar t = step; t < 1.0; t += step)
00291     {
00292         e1 = e2;
00293         e2 = get_transform(t).getOrigin();
00294         // now e1->e2 is the line segment we will test.
00295         btVector3 ed = e2 - e1;
00296         btVector3 w = start - e1;
00297         btScalar a = length.dot(length);
00298         btScalar b = length.dot(ed);
00299         btScalar c = ed.dot(ed);
00300         btScalar d = length.dot(w);
00301         btScalar e = ed.dot(w);
00302         btScalar denominator = a * c - b * b;
00303         btScalar screen = b * e - c * d;
00304         btScalar section = a * e - b * d;
00305         // Now check if this is along the segments.
00306         if (screen < 0) screen = 0;
00307         if (screen > denominator) screen = denominator;
00308         if (section < 0) section = 0;
00309         if (section > denominator) section = denominator;
00310         // find distance
00311         btScalar distance = (w + (screen * length - section * ed) / denominator).length2();
00312         if (distance < min_distance)
00313         {
00314             min_distance = distance;
00315             best_result = t + (section / denominator - 1) * step; 
00316         }
00317     }
00318     if (best_result < 0.0) return 0;
00319     else if (best_result > 1.0) return 1.0;
00320     else return best_result;
00321 }
00322 
00323 const EditAssist::ControlPoint * PathEdge::get_control_point_here(btVector3 start_pos, btVector3 stop_pos, btScalar radius) const
00324 {
00325     if (start.handle.is_here(start_pos, stop_pos, radius))
00326     {
00327         return &(start.handle);
00328     }
00329     else if (finish.handle.is_here(start_pos, stop_pos, radius))
00330     {
00331         return &(finish.handle);
00332     }
00333     else
00334     {
00335         for (std::vector<boost::shared_ptr<EditAssist::TrackAttachmentHandle> >::const_iterator
00336                     it = m_attachment_handles.begin();
00337              it != m_attachment_handles.end();
00338              it++)
00339         {
00340             if ((**it).is_here(start_pos, stop_pos, radius))
00341             {
00342                 return &(**(it));
00343             }
00344         }
00345     }
00346     // can't find a near enough control point.
00347     return 0;
00348 }
00349 
00350 void PathEdge::draw_control_points() const
00351 {
00352     start.handle.draw();
00353     finish.handle.draw();
00354 }
00355 
00356 std::size_t PathEdge::get_start_vertex_name()
00357 {
00358     return start_vertex_index;
00359 }
00360 
00361 std::size_t PathEdge::get_finish_vertex_name()
00362 {
00363     return finish_vertex_index;
00364 }
00365 
00366 void PathEdge::add_collision_faces(btTriangleMesh & shape) const
00367 {
00368     for (unsigned int i = 0; i < number_of_repetions; i++)
00369     {
00370         PieceDistortion transform(*this, i,
00371                                          segment->get_length(),
00372                                          segment->get_minimum_y());
00373         segment->get_floor_mesh().get_distorted_faces(transform).add_faces(shape);
00374         segment->get_wall_mesh().get_distorted_faces(transform).add_faces(shape);
00375     }
00376 }
00377 
00378 void PathEdge::add_floor_faces(btTriangleMesh & shape) const
00379 {
00380     for (unsigned int i = 0; i < number_of_repetions; i++)
00381     {
00382         PieceDistortion transform(*this, i,
00383                                          segment->get_length(),
00384                                          segment->get_minimum_y());
00385         segment->get_floor_mesh().get_distorted_faces(transform).add_faces(shape);
00386     }
00387 }
00388 
00389 void PathEdge::add_ai_faces(MeshFaces & mesh) const
00390 {
00391     mesh |= m_nav_mesh;    
00392 }
00393 
00394 AxisAlignedBoundingBox PathEdge::get_bounds() const
00395 {
00396     return graphics_bounds;
00397 }
00398 
00399 void PathEdge::draw() const
00400 {
00401     // draw everything
00402     assert(segment);
00403     segment->get_texture().bind();
00404     for (unsigned int i = 0; i < number_of_repetions; i++)
00405     {
00406         meshes[i]->draw();
00407     }
00408     draw_attachments();
00409 }
00410 
00411 void PathEdge::conditional_draw(const OcclusionTester & occlusion_tester) const
00412 {
00413     OcclusionTester::ViewState vs(occlusion_tester(graphics_bounds));
00414     switch (vs)
00415     {
00416         case OcclusionTester::VS_IN:
00417             // All on screen, draw everything.
00418             draw();
00419             break;
00420         case OcclusionTester::VS_PARTIAL:
00421             // Partially visible, check each mesh individually.
00422             assert(segment);
00423             segment->get_texture().bind();
00424             for (unsigned int i = 0; i < number_of_repetions; i++)
00425             {
00426                 meshes[i]->conditional_draw(occlusion_tester);
00427             }
00433             draw_attachments();
00434             break;
00435         case OcclusionTester::VS_OUT:
00436             // don't draw anything, off screen.
00437             break;
00438     }
00439 }
00440 
00441 void PathEdge::draw_attachments() const
00442 {
00443     // Draw attachments.
00444     if (render_mode == DrawableMesh::RM_WIREFRAME)
00445     {
00446         glEnable(GL_TEXTURE_2D);
00447     }
00448     for (std::vector<boost::shared_ptr<TrackAttachment> >::const_iterator it = m_attachments.begin();
00449          it != m_attachments.end();
00450          it++)
00451     {
00452         glPushMatrix();
00453             btScalar mat[16];
00454             mat[15] = 1.0;
00455             (**it).get_global_transform().getOpenGLMatrix(mat);
00456             glMultMatrixf(mat);
00457             (**it).draw();
00458         glPopMatrix();
00459     }
00460     if (render_mode == DrawableMesh::RM_WIREFRAME)
00461     {
00462         glDisable(GL_TEXTURE_2D);
00463     }
00464 }
00465 
00466 void PathEdge::make_cache() const
00467 {
00468     segment->get_texture().make_cache();
00469     for (unsigned int i = 0; i < number_of_repetions; i++)
00470     {
00471         meshes[i]->make_cache();
00472     }
00473 }
00474 
00475 void PathEdge::insert_attachment(boost::shared_ptr<TrackAttachment> attachment)
00476 {
00477     m_attachments.push_back(attachment);
00478     recreate_attachment_handles();
00479 }
00480 
00481 void PathEdge::remove_attachment(std::size_t attachment_name)
00482 {
00483     std::vector<boost::shared_ptr<TrackAttachment> >::iterator attach_it;
00484     for (attach_it = m_attachments.begin();
00485          (**attach_it).get_name() != attachment_name;
00486          attach_it++)
00487     {
00488         assert(attach_it != m_attachments.end());
00489     }
00490     assert(attach_it != m_attachments.end());
00491     m_attachments.erase(attach_it);
00492     // the attachment handles need to know their new index. Recreate them.
00493     recreate_attachment_handles();
00494 }
00495 
00496 const TrackAttachment & PathEdge::get_attachment(std::size_t attachment_name) const
00497 {
00498     std::vector<boost::shared_ptr<TrackAttachment> >::const_iterator attach_it;
00499     for (attach_it = m_attachments.begin();
00500          (**attach_it).get_name() != attachment_name;
00501          attach_it++)
00502     {
00503         assert(attach_it != m_attachments.end());
00504     }
00505     assert(attach_it != m_attachments.end());
00506     return **attach_it;
00507 }
00508 
00509 void PathEdge::set_attachment(std::size_t attachment_name,
00510                               const TrackAttachment & attachment)
00511 {
00512     std::vector<boost::shared_ptr<TrackAttachment> >::iterator attach_it;
00513     unsigned int index = 0;
00514     for (attach_it = m_attachments.begin();
00515          (**attach_it).get_name() != attachment_name;
00516          attach_it++, index++)
00517     {
00518         assert(attach_it != m_attachments.end());
00519     }
00520     assert(attach_it != m_attachments.end());
00521     (**attach_it) = attachment;
00522     // Set correct handle position.
00523     btTransform transform = get_transform(attachment.get_t_position());
00524     btVector3 pos = transform(btVector3(attachment.get_lateral_position(),
00525                               0.0,
00526                               attachment.get_vertical_position()));
00527     m_attachment_handles[index]->set_position(pos);
00528     // set attachment's global to local transformation.
00529     transform.setOrigin(pos);
00530     (**attach_it).set_global_transform(transform);
00531     
00532 }
00533 
00534 void PathEdge::recreate_attachment_handles()
00535 {
00536     m_attachment_handles.clear();
00537     for (std::size_t i = 0; i < m_attachments.size(); i++)
00538     {
00539         m_attachment_handles.push_back(
00540             boost::shared_ptr<EditAssist::TrackAttachmentHandle>(
00541                 new EditAssist::TrackAttachmentHandle(*this, i)));
00542         EditAssist::TrackAttachmentHandle & handle = *(m_attachment_handles.back());
00543         const TrackAttachment & attachment = *(m_attachments[i]);
00544         
00545         btTransform transform = get_transform(attachment.get_t_position());
00546         btVector3 pos = transform(btVector3(attachment.get_lateral_position(),
00547                                             0.0,
00548                                             attachment.get_vertical_position()));
00549         handle.set_position(pos);
00550         // also tell the attachment its global transform, since if the handle
00551         // is off the transform will be too.
00552         transform.setOrigin(pos);
00553         m_attachments[i]->set_global_transform(transform);
00554     }
00555 }
00556 
00557 const std::vector<boost::shared_ptr<TrackAttachment> > & PathEdge::get_attachments() const
00558 {
00559     return m_attachments;
00560 }
00561 
00562 std::ostream & operator<<(std::ostream & destination, const PathEdge & path_edge)
00563 {
00564     destination << path_edge_newest_version << ' ';
00565     string_to_stream(destination, path_edge.segment->get_name());
00566     destination << ' ' << path_edge.start
00567                 << ' ' << path_edge.finish
00568                 << ' ';
00569     SaveableClassList::get_instance()->write_vector(destination, path_edge.get_attachments());
00570     return destination;
00571 }
00572 
00573 AxisAlignedBoundingBox & operator|=(AxisAlignedBoundingBox & box,
00574                                     const PathEdge & path_edge)
00575 {
00576     box |= path_edge.bounds;
00577     return box;
00578 }
00579 
00580 }

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.