From 7e382cc3cad8c4f6945b0c9d89e2ca917b42b740 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Fri, 19 Feb 2010 15:44:09 +0000 Subject: [PATCH] Split mesh generation from Track3D to TrackType3D Add a Catalogue3D to hold the TrackType3D objects Better parametrization of track profile Make track profile loadable from the catalogue file Track colors and path rendering is non-funtional at the moment --- source/3d/catalogue.cpp | 33 ++++++ source/3d/catalogue.h | 34 ++++++ source/3d/layout.cpp | 7 +- source/3d/layout.h | 5 +- source/3d/track.cpp | 160 ++++--------------------- source/3d/track.h | 18 ++- source/3d/tracktype.cpp | 199 ++++++++++++++++++++++++++++++++ source/3d/tracktype.h | 43 +++++++ source/libmarklin/catalogue.cpp | 31 ++++- source/libmarklin/catalogue.h | 16 ++- source/libmarklin/profile.cpp | 58 ++++++++++ source/libmarklin/profile.h | 44 +++++++ tracks.dat | 17 +++ 13 files changed, 510 insertions(+), 155 deletions(-) create mode 100644 source/3d/catalogue.cpp create mode 100644 source/3d/catalogue.h create mode 100644 source/3d/tracktype.cpp create mode 100644 source/3d/tracktype.h create mode 100644 source/libmarklin/profile.cpp create mode 100644 source/libmarklin/profile.h diff --git a/source/3d/catalogue.cpp b/source/3d/catalogue.cpp new file mode 100644 index 0000000..994d5f4 --- /dev/null +++ b/source/3d/catalogue.cpp @@ -0,0 +1,33 @@ +/* $Id$ + +This file is part of the MSP Märklin suite +Copyright © 2010 Mikkosoft Productions, Mikko Rasa +Distributed under the GPL +*/ + +#include "catalogue.h" +#include "tracktype.h" + +using namespace std; +using namespace Msp; + +namespace Marklin { + +Catalogue3D::Catalogue3D(const Catalogue &c): + catalogue(c) +{ + const map &trks = catalogue.get_tracks(); + for(map::const_iterator i=trks.begin(); i!=trks.end(); ++i) + tracks[i->second] = new TrackType3D(*this, *i->second); +} + +const TrackType3D &Catalogue3D::get_track(const TrackType &tt) const +{ + map::const_iterator i = tracks.find(&tt); + if(i==tracks.end()) + throw KeyError("Unknown track type"); + + return *i->second; +} + +} // namespace Marklin diff --git a/source/3d/catalogue.h b/source/3d/catalogue.h new file mode 100644 index 0000000..689756d --- /dev/null +++ b/source/3d/catalogue.h @@ -0,0 +1,34 @@ +/* $Id$ + +This file is part of the MSP Märklin suite +Copyright © 2010 Mikkosoft Productions, Mikko Rasa +Distributed under the GPL +*/ + +#ifndef MARKLIN3D_CATALOGUE_H_ +#define MARKLIN3D_CATALOGUE_H_ + +#include +#include "libmarklin/catalogue.h" + +namespace Marklin { + +class TrackType3D; + +class Catalogue3D +{ +private: + const Catalogue &catalogue; + std::map tracks; + Msp::GL::Material *ballast_material; + +public: + Catalogue3D(const Catalogue &); + + const Catalogue &get_catalogue() const { return catalogue; } + const TrackType3D &get_track(const TrackType &) const; +}; + +} + +#endif diff --git a/source/3d/layout.cpp b/source/3d/layout.cpp index dcf5544..633baa1 100644 --- a/source/3d/layout.cpp +++ b/source/3d/layout.cpp @@ -1,7 +1,7 @@ /* $Id$ This file is part of the MSP Märklin suite -Copyright © 2006-2008 Mikkosoft Productions, Mikko Rasa +Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa Distributed under the GPL */ @@ -20,6 +20,7 @@ namespace Marklin { Layout3D::Layout3D(Layout &l): layout(l), + catalogue(layout.get_catalogue()), quality(4) { layout.signal_track_added.connect(sigc::mem_fun(this, &Layout3D::track_added)); @@ -35,8 +36,6 @@ Layout3D::~Layout3D() void Layout3D::set_quality(unsigned q) { quality = q; - for(list::iterator i=tracks.begin(); i!=tracks.end(); ++i) - (*i)->set_quality(quality); } void Layout3D::render(bool endpoints) const @@ -122,7 +121,7 @@ Track3D *Layout3D::pick_track(float x, float y, float size) const void Layout3D::track_added(Track &t) { - tracks.push_back(new Track3D(t, quality)); + tracks.push_back(new Track3D(*this, t)); } void Layout3D::track_removed(Track &t) diff --git a/source/3d/layout.h b/source/3d/layout.h index 208ddac..21686de 100644 --- a/source/3d/layout.h +++ b/source/3d/layout.h @@ -1,7 +1,7 @@ /* $Id$ This file is part of the MSP Märklin suite -Copyright © 2006-2008 Mikkosoft Productions, Mikko Rasa +Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa Distributed under the GPL */ @@ -9,6 +9,7 @@ Distributed under the GPL #define MARKLIN3D_LAYOUT_H_ #include "libmarklin/layout.h" +#include "catalogue.h" #include "track.h" namespace Marklin { @@ -17,6 +18,7 @@ class Layout3D { private: Layout &layout; + Catalogue3D catalogue; std::list tracks; unsigned quality; @@ -24,6 +26,7 @@ public: Layout3D(Layout &); ~Layout3D(); + const Catalogue3D &get_catalogue() const { return catalogue; } void set_quality(unsigned); const std::list &get_tracks() const { return tracks; } void render(bool =false) const; diff --git a/source/3d/track.cpp b/source/3d/track.cpp index 1df93a2..c3eb388 100644 --- a/source/3d/track.cpp +++ b/source/3d/track.cpp @@ -1,7 +1,7 @@ /* $Id$ This file is part of the MSP Märklin suite -Copyright © 2006-2009 Mikkosoft Productions, Mikko Rasa +Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa Distributed under the GPL */ @@ -9,60 +9,45 @@ Distributed under the GPL #include #include #include "libmarklin/tracktype.h" +#include "layout.h" #include "track.h" +#include "tracktype.h" using namespace std; using namespace Msp; namespace Marklin { -Track3D::Track3D(Track &t, unsigned q): +Track3D::Track3D(Layout3D &l, Track &t): + layout(l), track(t), - color(1, 1, 1), - varray((GL::NORMAL3, GL::VERTEX3)), - quality(q) -{ - build_object(); -} + type(layout.get_catalogue().get_track(track.get_type())), + color(1, 1, 1) +{ } void Track3D::set_color(const Msp::GL::Color &c) { color = c; } -void Track3D::set_quality(unsigned q) -{ - quality = q; - build_object(); -} - void Track3D::get_bounds(float angle, Point &minp, Point &maxp) const { - const Point &pos = track.get_position(); - float rot = track.get_rotation(); - float slope = track.get_slope(); + type.get_bounds(angle-track.get_rotation(), minp, maxp); float c = cos(-angle); float s = sin(-angle); - minp.x = maxp.x = c*pos.x-s*pos.y; - minp.y = maxp.y = s*pos.x+c*pos.y; - minp.z = pos.z+min(slope, 0.0f); - maxp.z = pos.z+0.01+max(slope, 0.0f); - - float c2 = cos(rot-angle); - float s2 = sin(rot-angle); - - for(vector::const_iterator i=border.begin(); i!=border.end(); ++i) - { - float x = c*pos.x-s*pos.y + c2*i->x-s2*i->y; - float y = s*pos.x+c*pos.y + s2*i->x+c2*i->y; + const Point &pos = track.get_position(); + minp.x += c*pos.x-s*pos.y; + maxp.x += c*pos.x-s*pos.y; + minp.y += s*pos.x+c*pos.y; + maxp.y += s*pos.x+c*pos.y; - minp.x = min(minp.x, x); - minp.y = min(minp.y, y); - maxp.x = max(maxp.x, x); - maxp.y = max(maxp.y, y); - } + float slope = track.get_slope(); + if(slope>0) + maxp.z += slope; + else + minp.z += slope; } void Track3D::render() const @@ -71,14 +56,7 @@ void Track3D::render() const glPushName(reinterpret_cast(this)); - varray.apply(); - glColor4f(0.25*color.r, 0.25*color.g, 0.25*color.b, 1); - glDrawElements(GL_QUADS, base_seq.size(), GL_UNSIGNED_INT, &base_seq[0]); - if(quality>1) - { - glColor4f(0.85*color.r, 0.85*color.g, 0.85*color.b, 1); - glDrawElements(GL_QUADS, rail_seq.size(), GL_UNSIGNED_INT, &rail_seq[0]); - } + type.render(); glPopName(); glPopMatrix(); @@ -139,14 +117,15 @@ void Track3D::render_path(int path) const { prepare_render(true); - varray.apply(); + (void)path; + /*varray.apply(); if(path>=0 && static_cast(path) &parts = track.get_type().get_parts(); - unsigned index = 0; - for(vector::const_iterator i=parts.begin(); i!=parts.end(); ++i) - build_part(*i, builder, index); -} - -void Track3D::build_part(const TrackPart &part, GL::VertexArrayBuilder &va_builder, unsigned &base_index) -{ - static vector profile; - if(profile.empty()) - { - profile.push_back(Point(0, -0.02, 0)); - profile.push_back(Point(0, -0.014, 0.008)); - profile.push_back(Point(0, -0.014, 0.008)); - profile.push_back(Point(0, 0.014, 0.008)); - profile.push_back(Point(0, 0.014, 0.008)); - profile.push_back(Point(0, 0.02, 0)); - for(unsigned i=0; i<2; ++i) - { - profile.push_back(Point(0, -0.009+i*0.017, 0.008)); - profile.push_back(Point(0, -0.009+i*0.017, 0.0103)); - profile.push_back(Point(0, -0.009+i*0.017, 0.0103)); - profile.push_back(Point(0, -0.008+i*0.017, 0.0103)); - profile.push_back(Point(0, -0.008+i*0.017, 0.0103)); - profile.push_back(Point(0, -0.008+i*0.017, 0.008)); - } - profile.push_back(Point(0, -0.002, 0.012)); - profile.push_back(Point(0, 0.002, 0.012)); - } - static unsigned psize = profile.size(); - - unsigned nsegs = (part.radius ? static_cast(part.length*(1< border; - Msp::GL::VertexArray varray; - std::vector base_seq; - std::vector rail_seq; - std::vector > path_seq; - unsigned quality; public: - Track3D(Track &, unsigned); + Track3D(Layout3D &, Track &); Track &get_track() const { return track; } void set_color(const Msp::GL::Color &); - void set_quality(unsigned); void get_bounds(float, Point &, Point &) const; void render() const; void render_endpoints() const; void render_path(int) const; private: void prepare_render(bool) const; - void build_object(); - void build_part(const TrackPart &, Msp::GL::VertexArrayBuilder &, unsigned &); }; } // namespace Marklin diff --git a/source/3d/tracktype.cpp b/source/3d/tracktype.cpp new file mode 100644 index 0000000..26be527 --- /dev/null +++ b/source/3d/tracktype.cpp @@ -0,0 +1,199 @@ +/* $Id$ + +This file is part of the MSP Märklin suite +Copyright © 2010 Mikkosoft Productions, Mikko Rasa +Distributed under the GPL +*/ + +#include +#include +#include +#include "catalogue.h" +#include "tracktype.h" + +using namespace std; +using namespace Msp; + +namespace { + +bool compare_z(const Marklin::Point &p1, const Marklin::Point &p2) +{ + return p1.z +Iter graham_scan(Iter begin, Iter end) +{ + // http://en.wikipedia.org/wiki/Graham_scan + + // Find point with lowest X coordinate + Marklin::Point lowest = *begin; + for(Iter i=begin; i!=end; ++i) + if(i->xx==lowest.x && i->y>lowest.y)) + lowest = *i; + + // Compute tangents and sort points + for(Iter i=begin; i!=end; ++i) + i->z = (i->x==lowest.x ? 1e5/(i->y-lowest.y-1) : (i->y-lowest.y)/(i->x-lowest.x)); + sort(begin, end, compare_z); + + for(Iter k=begin, i=k++, j=k++;; ) + { + // Compute winding by cross product + float turn = (j->x-i->x)*(k->y-j->y) - (k->x-j->x)*(j->y-i->y); + + if(turn<1e-5) + { + // Right turn - throw the middle point away + // Special case for collinear vertices in the beginning + if(i==begin) + j = k++; + else + j = i--; + } + else + { + // Left turn - store the middle point and advance + if(++i!=j) + *i = *j; + j = k++; + } + + // Cycle back to beginning and terminate after checking the last point + if(k==end) + k = begin; + else if(j==begin) + return ++i; + } +} + +} + +namespace Marklin { + +TrackType3D::TrackType3D(const Catalogue3D &cat3d, const TrackType &tt): + ballast_mesh((GL::NORMAL3, GL::COLOR4_UBYTE, GL::VERTEX3)), + rail_mesh((GL::NORMAL3, GL::COLOR4_UBYTE, GL::VERTEX3)) +{ + const Catalogue &cat = cat3d.get_catalogue(); + const vector &parts = tt.get_parts(); + + const Profile &ballast_profile = cat.get_ballast_profile(); + const Point &ballast_min = ballast_profile.get_min_coords(); + const Point &ballast_max = ballast_profile.get_max_coords(); + + const Profile &rail_profile = cat.get_rail_profile(); + const Point &rail_min = rail_profile.get_min_coords(); + const Point &rail_max = rail_profile.get_max_coords(); + + float gauge = cat.get_gauge(); + + { + unsigned index = 0; + GL::MeshBuilder bld(ballast_mesh); + bld.color(0.25f, 0.25f, 0.25f); + for(vector::const_iterator i=parts.begin(); i!=parts.end(); ++i) + build_part(*i, ballast_profile, Point(0, -ballast_min.y), bld, index); + } + + { + unsigned index = 0; + GL::MeshBuilder bld(rail_mesh); + bld.color(0.85f, 0.85f, 0.85f); + float y = ballast_max.y-ballast_min.y-rail_min.y; + for(vector::const_iterator i=parts.begin(); i!=parts.end(); ++i) + build_part(*i, rail_profile, Point(-gauge/2-rail_max.x, y), bld, index); + for(vector::const_iterator i=parts.begin(); i!=parts.end(); ++i) + build_part(*i, rail_profile, Point(gauge/2-rail_min.x, y), bld, index); + } + + min_z = max_z = border.front().z; + for(vector::iterator i=border.begin(); i!=border.end(); ++i) + { + min_z = min(min_z, i->z); + max_z = max(max_z, i->z); + } + border.erase(graham_scan(border.begin(), border.end()), border.end()); +} + +void TrackType3D::get_bounds(float angle, Point &minp, Point &maxp) const +{ + float c = cos(-angle); + float s = sin(-angle); + + minp = maxp = Point(); + minp.z = min_z; + maxp.z = max_z; + + for(vector::const_iterator i=border.begin(); i!=border.end(); ++i) + { + float x = c*i->x-s*i->y; + float y = s*i->x+c*i->y; + + minp.x = min(minp.x, x); + minp.y = min(minp.y, y); + maxp.x = max(maxp.x, x); + maxp.y = max(maxp.y, y); + } +} + +void TrackType3D::render() const +{ + ballast_mesh.draw(); + rail_mesh.draw(); +} + +void TrackType3D::build_part(const TrackPart &part, const Profile &profile, const Point &offset, GL::MeshBuilder &bld, unsigned &base_index) +{ + unsigned nsegs = (part.radius ? static_cast(part.length*16)+1 : 1); + float plen = part.length; + if(part.radius) + plen *= abs(part.radius); + + unsigned n_points = profile.get_n_points(); + for(unsigned i=0; i<=nsegs; ++i) + { + float a = part.dir+(part.radius ? i*plen/nsegs/part.radius : 0); + float c = cos(a); + float s = sin(a); + Point basep = part.get_point(i*plen/nsegs); + + Point p; + for(unsigned j=0; j0) + bld.vertex(p.x, p.y, p.z); + + if(j+1 +#include +#include "libmarklin/profile.h" +#include "libmarklin/tracktype.h" + +namespace Marklin { + +class Catalogue3D; + +class TrackType3D +{ +private: + Msp::GL::Mesh ballast_mesh; + Msp::GL::Mesh rail_mesh; + std::vector border; + float min_z; + float max_z; + +public: + TrackType3D(const Catalogue3D &, const TrackType &); + + void get_bounds(float, Point &, Point &) const; + + void render() const; + +private: + void build_part(const TrackPart &, const Profile &, const Point &, Msp::GL::MeshBuilder &, unsigned &); + //void optimize_border(); +}; + +} // namespace Marklin + +#endif diff --git a/source/libmarklin/catalogue.cpp b/source/libmarklin/catalogue.cpp index 55a5a44..3c9cf7d 100644 --- a/source/libmarklin/catalogue.cpp +++ b/source/libmarklin/catalogue.cpp @@ -1,7 +1,7 @@ /* $Id$ This file is part of the MSP Märklin suite -Copyright © 2006-2009 Mikkosoft Productions, Mikko Rasa +Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa Distributed under the GPL */ @@ -16,6 +16,11 @@ using namespace Msp; namespace Marklin { +Catalogue::Catalogue(): + scale(1), + gauge(1.524) +{ } + Catalogue::~Catalogue() { for(map::iterator i=tracks.begin(); i!=tracks.end(); ++i) @@ -46,10 +51,24 @@ LocoType &Catalogue::get_locomotive(unsigned art_nr) const Catalogue::Loader::Loader(Catalogue &c): DataFile::BasicLoader(c) { + add("ballast_profile", &Loader::ballast_profile); + add("gauge", &Loader::gauge); add("locomotive", &Loader::locomotive); + add("rail_profile", &Loader::rail_profile); + add("scale", &Loader::scale); add("track", &Loader::track); } +void Catalogue::Loader::ballast_profile() +{ + load_sub(obj.ballast_profile); +} + +void Catalogue::Loader::gauge(float g) +{ + obj.gauge = g/1000; +} + void Catalogue::Loader::locomotive(unsigned art_nr) { if(obj.locos.count(art_nr)) @@ -60,6 +79,16 @@ void Catalogue::Loader::locomotive(unsigned art_nr) obj.locos[art_nr] = loco.release(); } +void Catalogue::Loader::rail_profile() +{ + load_sub(obj.rail_profile); +} + +void Catalogue::Loader::scale(float n, float d) +{ + obj.scale = n/d; +} + void Catalogue::Loader::track(unsigned art_nr) { if(obj.tracks.count(art_nr)) diff --git a/source/libmarklin/catalogue.h b/source/libmarklin/catalogue.h index 4ce6fb1..5d88c28 100644 --- a/source/libmarklin/catalogue.h +++ b/source/libmarklin/catalogue.h @@ -1,7 +1,7 @@ /* $Id$ This file is part of the MSP Märklin suite -Copyright © 2006-2009 Mikkosoft Productions, Mikko Rasa +Copyright © 2006-2010 Mikkosoft Productions, Mikko Rasa Distributed under the GPL */ @@ -10,6 +10,7 @@ Distributed under the GPL #include #include +#include "profile.h" namespace Marklin { @@ -24,17 +25,30 @@ public: public: Loader(Catalogue &); private: + void ballast_profile(); + void gauge(float); void locomotive(unsigned); + void rail_profile(); + void scale(float, float); void track(unsigned); }; private: + float scale; + float gauge; + Profile rail_profile; + Profile ballast_profile; std::map tracks; std::map locos; public: + Catalogue(); ~Catalogue(); + float get_scale() const { return scale; } + float get_gauge() const { return gauge; } + const Profile &get_rail_profile() const { return rail_profile; } + const Profile &get_ballast_profile() const { return ballast_profile; } TrackType &get_track(unsigned) const; const std::map &get_tracks() const { return tracks; } LocoType &get_locomotive(unsigned) const; diff --git a/source/libmarklin/profile.cpp b/source/libmarklin/profile.cpp new file mode 100644 index 0000000..f4dcf18 --- /dev/null +++ b/source/libmarklin/profile.cpp @@ -0,0 +1,58 @@ +/* $Id$ + +This file is part of the MSP Märklin suite +Copyright © 2010 Mikkosoft Productions, Mikko Rasa +Distributed under the GPL +*/ + +#include +#include "profile.h" + +using namespace std; +using namespace Msp; + +namespace Marklin { + +const Point &Profile::get_point(unsigned i) const +{ + if(i>=points.size()) + throw InvalidParameterValue("Index out of range"); + return points[i]; +} + +Point Profile::get_edge_normal(unsigned i) const +{ + if(i+1>=points.size()) + throw InvalidParameterValue("Index out of range"); + float dx = points[i+1].x-points[i].x; + float dy = points[i+1].y-points[i].y; + float len = sqrt(dx*dx+dy*dy); + return Point(-dx/len, dy/len); +} + + +Profile::Loader::Loader(Profile &p): + DataFile::ObjectLoader(p) +{ + add("point", &Loader::point); +} + +void Profile::Loader::finish() +{ + obj.min_coords = obj.points[0]; + obj.max_coords = obj.points[0]; + for(unsigned i=1; i +#include +#include "geometry.h" + +namespace Marklin { + +class Profile +{ +public: + class Loader: public Msp::DataFile::ObjectLoader + { + public: + Loader(Profile &); + private: + virtual void finish(); + void point(float, float); + }; + +private: + std::vector points; + Point min_coords; + Point max_coords; + +public: + unsigned get_n_points() const { return points.size(); } + const Point &get_point(unsigned) const; + const Point &get_min_coords() const { return min_coords; } + const Point &get_max_coords() const { return max_coords; } + Point get_edge_normal(unsigned) const; +}; + +} // namespace Marklin + +#endif diff --git a/tracks.dat b/tracks.dat index 86089d0..7b7ba57 100644 --- a/tracks.dat +++ b/tracks.dat @@ -1,5 +1,22 @@ /* $Id$ */ +scale 1 87; +gauge 16.5; +rail_profile +{ + point 0.5 0.0; + point 0.5 2.3; + point -0.5 2.3; + point -0.5 0.0; +}; +ballast_profile +{ + point 20.0 0.0; + point 14.0 8.0; + point -14.0 8.0; + point -20.0 0.0; +}; + // Straight track 24064 -- 2.43.0