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 }
Generated at Mon Sep 6 00:41:12 2010 by Doxygen version 1.4.7 for Racer version svn335.