From b3ee5f28eb7da7b23b35bb7e932ced54dd1db3f4 Mon Sep 17 00:00:00 2001
From: Nick Gasson <nick@nickg.me.uk>
Date: Sun, 30 May 2010 20:12:27 +0100
Subject: [PATCH] Start making track part of the static scenery

---
 include/ITrackSegment.hpp |  6 +++-
 include/TrackCommon.hpp   | 24 +++++++++++++++-
 src/CrossoverTrack.cpp    | 49 ++++++++++++++++++++++++++++----
 src/CurvedTrack.cpp       |  3 +-
 src/Map.cpp               | 14 ++++++---
 src/Points.cpp            |  3 +-
 src/SBend.cpp             |  1 +
 src/SlopeTrack.cpp        |  1 +
 src/StraightTrack.cpp     | 31 ++++++++++++++++++--
 src/TrackCommon.cpp       | 60 ++++++++++++++++++++++++++++++++++-----
 10 files changed, 170 insertions(+), 22 deletions(-)

diff --git a/include/ITrackSegment.hpp b/include/ITrackSegment.hpp
index 1946f69..5e57844 100644
--- a/include/ITrackSegment.hpp
+++ b/include/ITrackSegment.hpp
@@ -1,5 +1,5 @@
 //
-//  Copyright (C) 2009  Nick Gasson
+//  Copyright (C) 2009-2010  Nick Gasson
 //
 //  This program is free software: you can redistribute it and/or modify
 //  it under the terms of the GNU General Public License as published by
@@ -21,6 +21,7 @@
 #include "Platform.hpp"
 #include "Maths.hpp"
 #include "IXMLSerialisable.hpp"
+#include "IMesh.hpp"
 
 #include <vector>
 #include <set>
@@ -98,6 +99,9 @@ struct ITrackSegment : IXMLSerialisable {
    // Render the track with the origin in the centre
    virtual void render() const = 0;
 
+   // Merge this track segment's mesh into a scenery mesh buffer
+   virtual void merge(IMeshBufferPtr buf) const = 0;
+
    // Set the absolute position of the track in the world
    virtual void setOrigin(int x, int y, float height) = 0;
 
diff --git a/include/TrackCommon.hpp b/include/TrackCommon.hpp
index 499e524..8afd796 100644
--- a/include/TrackCommon.hpp
+++ b/include/TrackCommon.hpp
@@ -1,5 +1,5 @@
 //
-//  Copyright (C) 2009  Nick Gasson
+//  Copyright (C) 2009-2010  Nick Gasson
 //
 //  This program is free software: you can redistribute it and/or modify
 //  it under the terms of the GNU General Public License as published by
@@ -22,9 +22,31 @@
 #include "IMesh.hpp"
 #include "BezierCurve.hpp"
 
+class StraightTrackHelper {
+public:
+   void mergeStraightRail(IMeshBufferPtr buf,
+      Vector<float> off, float yAngle) const;
+   
+private:
+   void mergeOneRail(IMeshBufferPtr buf,
+      Vector<float> off, float yAngle) const;
+      
+   static IMeshBufferPtr railBuf;
+};
+
+class SleeperHelper {
+public:
+   void mergeSleeper(IMeshBufferPtr buf,
+      Vector<float> off, float yAngle) const;
+   
+private:
+   static IMeshBufferPtr sleeperBuf;
+};
+
 // Common track rendering functions
 void renderSleeper();
 void renderStraightRail();
+
 void renderCurvedTrack(int baseRadius, track::Angle startAngle,
                        track::Angle endAngle);
 void renderRailMesh(IMeshPtr aMesh);
diff --git a/src/CrossoverTrack.cpp b/src/CrossoverTrack.cpp
index e8459e4..0f45b8f 100644
--- a/src/CrossoverTrack.cpp
+++ b/src/CrossoverTrack.cpp
@@ -1,5 +1,5 @@
 //
-//  Copyright (C) 2009  Nick Gasson
+//  Copyright (C) 2009-2010  Nick Gasson
 //
 //  This program is free software: you can redistribute it and/or modify
 //  it under the terms of the GNU General Public License as published by
@@ -27,14 +27,14 @@
 #include <GL/glu.h>
 #include <boost/lexical_cast.hpp>
 
-using namespace std;
-using namespace std::tr1;
-using namespace std::tr1::placeholders;
+using namespace placeholders;
 using namespace boost;
 
 // A section of track that allows travelling along both axis
 class CrossoverTrack : public ITrackSegment,
-                       public enable_shared_from_this<CrossoverTrack> {
+                       public enable_shared_from_this<CrossoverTrack>,
+                       private StraightTrackHelper,
+                       private SleeperHelper {
 public:
    CrossoverTrack() : myX(0), myY(0), height(0.0f) {}
    ~CrossoverTrack() {}
@@ -42,6 +42,8 @@ public:
    void setOrigin(int x, int y, float h);
    
    void render() const;
+   void merge(IMeshBufferPtr buf) const;
+   
    float segmentLength(const track::TravelToken& aToken) const;
    bool isValidDirection(const track::Direction& aDirection) const;
    track::Connection nextPosition(const track::TravelToken& aToken) const;
@@ -65,6 +67,43 @@ private:
    float height;
 };
 
+void CrossoverTrack::merge(IMeshBufferPtr buf) const
+{
+   // Render the y-going rails and sleepers
+   {    
+      Vector<float> off = makeVector(
+         static_cast<float>(myX),
+         height,
+         static_cast<float>(myY));
+      
+      mergeStraightRail(buf, off, 0.0f);
+
+      off += makeVector(0.0f, 0.0f, -0.4f);
+            
+      for (int i = 0; i < 4; i++) {
+         mergeSleeper(buf, off, 90.0f);
+         off += makeVector(0.0f, 0.0f, 0.25f);
+      }
+   }
+
+   // Render the x-going rails and sleepers
+   {
+      Vector<float> off = makeVector(
+         static_cast<float>(myX),
+         height,
+         static_cast<float>(myY));
+      
+      mergeStraightRail(buf, off, 90.0f);
+      
+      off += makeVector(-0.4f, 0.0f, 0.0f);
+            
+      for (int i = 0; i < 4; i++) {
+         mergeSleeper(buf, off, 0.0f);
+         off += makeVector(0.25f, 0.0f, 0.0f);
+      }
+   }
+}
+
 void CrossoverTrack::render() const
 {
    glPushMatrix();
diff --git a/src/CurvedTrack.cpp b/src/CurvedTrack.cpp
index 713662c..1f24abd 100644
--- a/src/CurvedTrack.cpp
+++ b/src/CurvedTrack.cpp
@@ -1,5 +1,5 @@
 //
-//  Copyright (C) 2009  Nick Gasson
+//  Copyright (C) 2009-2010  Nick Gasson
 //
 //  This program is free software: you can redistribute it and/or modify
 //  it under the terms of the GNU General Public License as published by
@@ -41,6 +41,7 @@ public:
    ~CurvedTrack();
 
    void render() const;
+   void merge(IMeshBufferPtr buf) const {}
 
    void setOrigin(int x, int y, float h);
    float segmentLength(const track::TravelToken& aToken) const;
diff --git a/src/Map.cpp b/src/Map.cpp
index c61db22..b510bdf 100644
--- a/src/Map.cpp
+++ b/src/Map.cpp
@@ -620,6 +620,15 @@ void Map::buildMesh(int id, Point<int> botLeft, Point<int> topRight)
          
          if (tile.scenery)
             tile.scenery->merge(buf);
+
+         // Draw the track, if any
+         if (tile.track && tile.track->needsRendering(frameNum)) {
+            // TODO: how will this work with track that spans
+            // multiple sectors?
+            tile.track->get()->merge(buf);
+            
+            tile.track->renderedOn(frameNum);
+         }
       }
    }
 
@@ -769,12 +778,9 @@ void Map::renderSector(IGraphicsPtr aContext, int id,
             glEnd();
          }
 
-         // Draw the track, if any
          Tile& tile = tileAt(x, y);
+         
          if (tile.track && tile.track->needsRendering(frameNum)) {
-            tile.track->get()->render();
-            tile.track->renderedOn(frameNum);
-            
 #if 0
             // Draw the endpoints for debugging
             vector<Point<int> > tiles;
diff --git a/src/Points.cpp b/src/Points.cpp
index 22e4f52..4452996 100644
--- a/src/Points.cpp
+++ b/src/Points.cpp
@@ -1,5 +1,5 @@
 //
-//  Copyright (C) 2009  Nick Gasson
+//  Copyright (C) 2009-2010  Nick Gasson
 //
 //  This program is free software: you can redistribute it and/or modify
 //  it under the terms of the GNU General Public License as published by
@@ -33,6 +33,7 @@ public:
 
    // ITrackSegment interface
    void render() const;
+   void merge(IMeshBufferPtr buf) const {}
    void setOrigin(int x, int y, float h) { myX = x; myY = y; height = h; }
    float segmentLength(const track::TravelToken& aToken) const;
    bool isValidDirection(const track::Direction& aDirection) const;
diff --git a/src/SBend.cpp b/src/SBend.cpp
index 6b1695c..44a235e 100644
--- a/src/SBend.cpp
+++ b/src/SBend.cpp
@@ -34,6 +34,7 @@ public:
 
    // ITrackSegment interface
    void render() const;
+   void merge(IMeshBufferPtr buf) const {}
    void setOrigin(int x, int y, float h);
    float segmentLength(const track::TravelToken& token) const;
    bool isValidDirection(const track::Direction& dir) const;
diff --git a/src/SlopeTrack.cpp b/src/SlopeTrack.cpp
index c6f83f7..a5164c1 100644
--- a/src/SlopeTrack.cpp
+++ b/src/SlopeTrack.cpp
@@ -37,6 +37,7 @@ public:
 
    // ITrackSegment interface
    void render() const;
+   void merge(IMeshBufferPtr buf) const {}
    void setOrigin(int x, int y, float h);
    float segmentLength(const track::TravelToken& token) const;
    track::TravelToken getTravelToken(track::Position pos,
diff --git a/src/StraightTrack.cpp b/src/StraightTrack.cpp
index d7b67d7..a12ea00 100644
--- a/src/StraightTrack.cpp
+++ b/src/StraightTrack.cpp
@@ -1,5 +1,5 @@
 //
-//  Copyright (C) 2009  Nick Gasson
+//  Copyright (C) 2009-2010  Nick Gasson
 //
 //  This program is free software: you can redistribute it and/or modify
 //  it under the terms of the GNU General Public License as published by
@@ -19,6 +19,7 @@
 #include "TrackCommon.hpp"
 #include "ILogger.hpp"
 #include "XMLBuilder.hpp"
+#include "Matrix.hpp"
 
 #include <cassert>
 #include <stdexcept>
@@ -33,12 +34,15 @@ using namespace track;
 
 // Concrete implementation of straight-line pieces of track
 class StraightTrack : public ITrackSegment,
-                      public enable_shared_from_this<StraightTrack> {
+                      public enable_shared_from_this<StraightTrack>,
+                      private StraightTrackHelper,
+                      private SleeperHelper {
 public:
    StraightTrack(const Direction& aDirection);
    ~StraightTrack();
    
    void render() const;
+   void merge(IMeshBufferPtr buf) const;
    
    void setOrigin(int x, int y, float h);
    float segmentLength(const track::TravelToken& token) const { return 1.0f; }
@@ -232,6 +236,29 @@ void StraightTrack::render() const
    glPopMatrix();
 }
 
+void StraightTrack::merge(IMeshBufferPtr buf) const
+{
+   Vector<float> off = makeVector(
+      static_cast<float>(origin.x),
+      height,
+      static_cast<float>(origin.y));
+
+   float yAngle = direction == axis::X ? 90.0f : 0.0f;
+
+   mergeStraightRail(buf, off, yAngle);
+
+   yAngle += 90.0f;
+
+   Matrix<float, 4> r = Matrix<float, 4>::rotation(yAngle, 0.0f, 1.0f, 0.0f);
+   off += r.transform(makeVector(-0.4f, 0.0f, 0.0f));
+
+   for (int i = 0; i < 4; i++) {
+      mergeSleeper(buf, off, yAngle);
+
+      off += r.transform(makeVector(0.25f, 0.0f, 0.0f));
+   }  
+}
+
 xml::element StraightTrack::toXml() const
 {
    return xml::element("straightTrack")
diff --git a/src/TrackCommon.cpp b/src/TrackCommon.cpp
index 78b4867..51888fd 100644
--- a/src/TrackCommon.cpp
+++ b/src/TrackCommon.cpp
@@ -1,5 +1,5 @@
 //
-//  Copyright (C) 2009  Nick Gasson
+//  Copyright (C) 2009-2010  Nick Gasson
 //
 //  This program is free software: you can redistribute it and/or modify
 //  it under the terms of the GNU General Public License as published by
@@ -19,6 +19,7 @@
 #include "ILogger.hpp"
 #include "Maths.hpp"
 #include "IMesh.hpp"
+#include "Matrix.hpp"
 
 #include <cmath>
 #include <map>
@@ -40,7 +41,7 @@ namespace {
    
    const Colour METAL = makeColour(0.5f, 0.5f, 0.5f);
    
-   void generateSleeperMesh()
+   IMeshBufferPtr generateSleeperMeshBuffer()
    {
       IMeshBufferPtr buf = makeMeshBuffer();
 
@@ -87,10 +88,15 @@ namespace {
          makeVector(-sleeperOff, 0.0f, r),
          brown);
 
-      theSleeperMesh = makeMesh(buf);
+      return buf;
    }
 
-   void generateRailMesh()
+   void generateSleeperMesh()
+   {
+      theSleeperMesh = makeMesh(generateSleeperMeshBuffer());
+   }
+   
+   IMeshBufferPtr generateRailMeshBuffer()
    {
       IMeshBufferPtr buf = makeMeshBuffer();
 
@@ -113,9 +119,14 @@ namespace {
          makeVector(RAIL_WIDTH/2.0f, 0.0f, 1.0f),
          makeVector(RAIL_WIDTH/2.0f, 0.0f, 0.0f),
          makeVector(RAIL_WIDTH/2.0f, track::RAIL_HEIGHT, 0.0f),
-         METAL);   
-      
-      theRailMesh = makeMesh(buf);
+         METAL);
+
+      return buf;
+   }         
+
+   void generateRailMesh()
+   {
+      theRailMesh = makeMesh(generateRailMeshBuffer());
    }
 
    void buildOneBezierRail(const BezierCurve<float>& func,
@@ -261,6 +272,7 @@ namespace {
       
       theRailMesh->render();
    }
+
 }
 
 IMeshPtr makeBezierRailMesh(const BezierCurve<float>& aFunc)
@@ -268,6 +280,17 @@ IMeshPtr makeBezierRailMesh(const BezierCurve<float>& aFunc)
    return generateBezierRailMesh(aFunc);
 }
 
+IMeshBufferPtr SleeperHelper::sleeperBuf;
+
+void SleeperHelper::mergeSleeper(IMeshBufferPtr buf,
+   Vector<float> off, float yAngle) const
+{
+   if (!sleeperBuf)
+      sleeperBuf = generateSleeperMeshBuffer();
+
+   buf->merge(sleeperBuf, off, yAngle);
+}
+
 // Draw a sleeper in the current matrix location
 void renderSleeper()
 {   
@@ -304,6 +327,29 @@ void renderStraightRail()
    glPopMatrix();
 }
 
+IMeshBufferPtr StraightTrackHelper::railBuf;
+
+void StraightTrackHelper::mergeOneRail(IMeshBufferPtr buf,
+   Vector<float> off, float yAngle) const
+{
+   if (!railBuf)
+      railBuf= generateRailMeshBuffer();
+   
+   buf->merge(railBuf, off, yAngle);
+}
+   
+void StraightTrackHelper::mergeStraightRail(IMeshBufferPtr buf,
+   Vector<float> off, float yAngle) const
+{
+   Matrix<float, 4> r = Matrix<float, 4>::rotation(yAngle, 0.0f, 1.0f, 0.0f);
+   
+   off += r.transform(makeVector(-GAUGE/2.0f, 0.0f, -0.5f));
+   mergeOneRail(buf, off, yAngle);
+
+   off += r.transform(makeVector(GAUGE, 0.0f, 0.0f));
+   mergeOneRail(buf, off, yAngle);
+}
+
 // Move to the origin of a curved section of track
 void transformToOrigin(int baseRadius, track::Angle startAngle)
 {
-- 
2.39.5