mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-11-05 05:14:27 +01:00
feat(roads): right of way and lane turns at junctions, road weights, kerbs, medians and forking roads, banks to the ground, roundabouts, pedestrian crossings, spawn sets by count; fix(editor): model skin export, placement on the ground under the orthophoto
This commit is contained in:
1020
world/Road.cpp
1020
world/Road.cpp
File diff suppressed because it is too large
Load Diff
121
world/Road.h
121
world/Road.h
@@ -25,6 +25,7 @@ namespace scene
|
||||
struct scratch_data;
|
||||
class basic_section;
|
||||
}
|
||||
class road_order;
|
||||
|
||||
// geometry a node puts in the scene as shapes of its own, apart from the shapes of the scenery, so it can take it back
|
||||
class owned_shapes
|
||||
@@ -54,6 +55,9 @@ class owned_shapes
|
||||
// deal with a road without knowing about it.
|
||||
// scenery entry:
|
||||
// node <max> <min> <name> road <axis, laid out like a path of a track> [<property> <values>]... endroad
|
||||
// properties, past the ones of the lanes and the look: median <gap|painted|island> <width at the start> <width at the end> <material>,
|
||||
// kerbs <left|right|both> <width> <material>, bank <left|right|both> <width at the start> <drop at the start> <width at the end> <drop at the end>,
|
||||
// bankmaterial <material>, weight <value>, roundabout
|
||||
class road_node : public scene::basic_node
|
||||
{
|
||||
|
||||
@@ -90,6 +94,26 @@ class road_node : public scene::basic_node
|
||||
float width{0.f};
|
||||
std::string material;
|
||||
};
|
||||
// what keeps the two directions of a road apart, where they're led away from each other
|
||||
enum class median_type
|
||||
{
|
||||
gap, // nothing: the ground shows between two roadways
|
||||
painted, // the surface carries on, marked as closed to the traffic
|
||||
island // raised on a kerb
|
||||
};
|
||||
struct median_data
|
||||
{
|
||||
median_type type{median_type::painted};
|
||||
std::array<float, 2> width{0.f, 0.f}; // at the start and at the end of the axis
|
||||
std::string material{"none"}; // what an island is covered with
|
||||
};
|
||||
// slope leading from the edge of the road down, or up, to the ground
|
||||
struct bank_data
|
||||
{
|
||||
bool set{false}; // false: a shoulder gets the bank the road gives to all its shoulders, anything else gets none
|
||||
std::array<float, 2> width{0.f, 0.f}; // at the start and at the end of the axis
|
||||
std::array<float, 2> drop{0.f, 0.f}; // how far below the edge of the road it ends; negative if it rises
|
||||
};
|
||||
// everything the scenery says about the road. the lanes go left to right when facing along the axis:
|
||||
// the ones going against it, outermost first, then the ones going along it
|
||||
struct state
|
||||
@@ -112,6 +136,14 @@ class road_node : public scene::basic_node
|
||||
float sounddistance{25.f};
|
||||
int quality{15};
|
||||
std::string environment{"flat"};
|
||||
float weight{100.f}; // how willing the drivers are to take the road when leaving a junction, next to the other roads they can take there
|
||||
median_data median;
|
||||
std::array<bool, 2> kerbs{false, false}; // left, right: a kerb runs along the edge of the surface, as high as the kerb of a sidewalk
|
||||
float kerbwidth{0.15f};
|
||||
std::string kerbmaterial{"none"};
|
||||
std::array<bank_data, 2> banks; // left, right
|
||||
std::string bankmaterial{"none"}; // what a bank is covered with, other than one of a shoulder, which goes with the shoulder
|
||||
bool roundabout{false}; // the piece is a part of a roundabout: traffic on it goes ahead of the traffic joining it
|
||||
|
||||
// brings the content to a usable form: values within their limits, an entry for each lane and for each pair of neighbours
|
||||
void normalize();
|
||||
@@ -127,6 +159,14 @@ class road_node : public scene::basic_node
|
||||
double width() const;
|
||||
// what the widths of the lanes are multiplied by at specified value of the curve parameter
|
||||
double scale(double const T) const;
|
||||
// true if the two directions of the road are kept apart anywhere along it
|
||||
bool divided() const;
|
||||
// how far apart the two directions are at specified value of the curve parameter
|
||||
double median_width(double const T) const;
|
||||
// distance from the axis of the line the two directions meet at, on a road which doesn't keep them apart
|
||||
double divide() const;
|
||||
// width of the whole roadway at specified value of the curve parameter: the lanes and what's between the directions
|
||||
double span(double const T) const;
|
||||
// shape of the middle of a lane, laid out in the direction of travel
|
||||
segment_data lane_path(std::size_t const Lane) const;
|
||||
// length of the axis
|
||||
@@ -211,13 +251,32 @@ class road_node : public scene::basic_node
|
||||
// scenery entry:
|
||||
// node <max> <min> <name> junction <centre> arm <position> <direction x z> <lanes in> <lanes out> <lane width> ... [<property> <values>]... endjunction
|
||||
// properties: surface <material>, texlength <m>, markings <white|orange|none>, side <none|shoulder|sidewalk> <width> <material>, kerb <height>,
|
||||
// slope <width> <drop>, stopline <number of an arm, counted from 1>, velocity <km/h>, friction <value>, environment <name>
|
||||
// the arms don't have to be level with each other: the surface is spanned between their ends and the centre
|
||||
// slope <width> <drop>, stopline <number of an arm, counted from 1>, velocity <km/h>, friction <value>, environment <name>,
|
||||
// priority <arm> <none|main|yield|stop>, turns <arm> <lane leading in, counted from the middle of the road> <letters out of l, s, r>,
|
||||
// armmedian <arm> <width of what keeps the two directions of the road apart there>, kerbs <width> <material>
|
||||
// the arms don't have to be level with each other: the surface is spanned between their ends and the centre.
|
||||
// the junction also tells the vehicles when to wait: the ones which have to give way wait for the ones with the right of way,
|
||||
// and where neither has it, for the ones coming from their right, or from the opposite side when turning left across their way
|
||||
class junction_node : public scene::basic_node
|
||||
{
|
||||
|
||||
public:
|
||||
// types
|
||||
// what the vehicles coming by a road are to do about the ones coming by the other roads
|
||||
enum class right_of_way
|
||||
{
|
||||
none, // the rule of the right hand
|
||||
priority, // the others give way
|
||||
yield, // gives way
|
||||
stop // stops, then gives way
|
||||
};
|
||||
// ways a lane can be left by
|
||||
enum turn_flags : int
|
||||
{
|
||||
turn_left = 1,
|
||||
turn_straight = 2,
|
||||
turn_right = 4
|
||||
};
|
||||
struct arm_data
|
||||
{
|
||||
glm::dvec3 position{0.0}; // middle of the end of the road
|
||||
@@ -226,6 +285,46 @@ class junction_node : public scene::basic_node
|
||||
int outgoing{1}; // lanes leading out of it
|
||||
float width{3.5f}; // width of a lane
|
||||
bool stopline{false}; // a line is painted across the lanes leading into the junction
|
||||
float median{0.f}; // how far apart the two directions of the road are there
|
||||
right_of_way priority{right_of_way::none};
|
||||
// ways each lane leading into the junction can be left by, a sum of turn_flags; the lanes are counted from the middle of the road.
|
||||
// a lane with no entry, or with 0, gets what the junction works out: the inner lane takes the left turns, the outer one the right turns
|
||||
std::vector<int> turns;
|
||||
};
|
||||
// a way through the junction from a lane leading into it
|
||||
struct way_data
|
||||
{
|
||||
std::size_t exit{0}; // arm it leaves by
|
||||
int kind{turn_straight}; // one of turn_flags
|
||||
int code{0}; // what a driver calls it, once they picked it
|
||||
TTrack *track{nullptr}; // lane it leads to
|
||||
segment_data path;
|
||||
std::vector<std::pair<std::size_t, std::size_t>> conflicts; // ways it can't be taken along with: lane leading in, and a way from that lane
|
||||
};
|
||||
// a lane leading into the junction, with what its traffic is told
|
||||
struct gate_data
|
||||
{
|
||||
std::size_t arm{0};
|
||||
int lane{1};
|
||||
TTrack *entry{nullptr}; // the lane
|
||||
TTrack *link{nullptr}; // path, or crossroads of paths, leading on from it
|
||||
std::vector<way_data> ways;
|
||||
std::vector<TTrack *> approach; // lanes the vehicles heading for the junction are looked for on, nearest first
|
||||
glm::dvec3 line{0.0}; // where the lane ends
|
||||
road_order *order{nullptr};
|
||||
// what's going on at the moment
|
||||
TDynamicObject *first{nullptr}; // vehicle nearest to the junction
|
||||
double distance{0.0}; // of that vehicle from the end of the lane
|
||||
double speed{0.0};
|
||||
double halflength{0.0};
|
||||
unsigned taken{0}; // ways that vehicle may take, a bit for each
|
||||
unsigned inside{0}; // ways with a vehicle on them
|
||||
bool active{false}; // the vehicle is near enough to matter
|
||||
bool blocked{false}; // it's told to wait
|
||||
bool committed{false}; // it was let through, the others wait for it
|
||||
bool stopped{false}; // it made its stop at the stop sign
|
||||
double waited{0.0}; // how long it's been standing there
|
||||
double idle{0.0}; // how long it's been standing there since it was let through
|
||||
};
|
||||
struct state
|
||||
{
|
||||
@@ -235,6 +334,9 @@ class junction_node : public scene::basic_node
|
||||
float texturelength{4.f};
|
||||
road_node::marking_colour markings{road_node::marking_colour::white};
|
||||
road_node::side_data side; // what the corners between the roads are lined with, the way the sides of a road are
|
||||
bool kerbs{false}; // a kerb runs around the corners, the way it runs along the edges of a road
|
||||
float kerbwidth{0.15f};
|
||||
std::string kerbmaterial{"none"};
|
||||
float kerbheight{0.12f};
|
||||
glm::vec2 slope{1.f, 0.4f}; // width and drop of the bank which closes a shoulder
|
||||
float velocity{30.f}; // speed limit on the way through
|
||||
@@ -280,6 +382,13 @@ class junction_node : public scene::basic_node
|
||||
}
|
||||
// true if there's a vehicle on any path of the junction
|
||||
bool occupied() const;
|
||||
// tells the vehicles heading for the junction whether they can drive on. to be called with each step of the simulation
|
||||
void update(double const Deltatime);
|
||||
// lanes leading into the junction, with what their traffic is told at the moment
|
||||
std::vector<gate_data> const &gates() const
|
||||
{
|
||||
return m_gates;
|
||||
}
|
||||
// generates geometry of the surface
|
||||
std::vector<scene::shape_node> create_shapes() const;
|
||||
// puts geometry of the junction in the scene as shapes of its own, or takes it back
|
||||
@@ -304,10 +413,16 @@ class junction_node : public scene::basic_node
|
||||
void export_as_text_(std::ostream &Output) const override;
|
||||
// creates a path through the junction from provided definition and registers it with the simulation
|
||||
TTrack *create_track(std::string const &Definition, std::size_t const Index);
|
||||
// sets up what the traffic of each lane leading in is told, once the ways through are made
|
||||
void create_gates();
|
||||
// members
|
||||
state m_state;
|
||||
std::vector<TTrack *> m_links;
|
||||
std::vector<segment_data> m_movements;
|
||||
std::vector<gate_data> m_gates;
|
||||
std::vector<road_order *> m_orders; // orders for the drivers, used again each time the gates are made. never freed, as a driver can hold on to one
|
||||
double m_scan{0.0}; // time left to the next look at the traffic
|
||||
bool m_quiet{false}; // there was no traffic the last time
|
||||
bool m_merged{false};
|
||||
owned_shapes m_shapes;
|
||||
};
|
||||
@@ -330,6 +445,8 @@ class junction_table : public basic_table<junction_node>
|
||||
public:
|
||||
// legacy style initialization, to be performed when the tracks are joined, ahead of the roads
|
||||
void InitJunctions();
|
||||
// to be called with each step of the simulation
|
||||
void update(double const Deltatime);
|
||||
// generates geometry of the junctions and puts it in the scene
|
||||
void create_geometry(scene::scratch_data &Scratchpad);
|
||||
};
|
||||
|
||||
60
world/RoadOrder.h
Normal file
60
world/RoadOrder.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "world/Event.h"
|
||||
|
||||
// an order for the drivers of road vehicles heading for a place they may have to wait at: a closed level crossing,
|
||||
// a junction they have to give way at. to them it's what a signal is to the driver of a train. it doesn't go to
|
||||
// the event manager and never runs: the drivers find it on the lane they scan, and ask it what speed it allows
|
||||
class road_order : public basic_event
|
||||
{
|
||||
|
||||
public:
|
||||
road_order()
|
||||
{
|
||||
m_passive = true;
|
||||
}
|
||||
void init() override {}
|
||||
std::string input_text() const override
|
||||
{
|
||||
return "SetVelocity";
|
||||
}
|
||||
TCommandType input_command() const override
|
||||
{
|
||||
return TCommandType::cm_SetVelocity;
|
||||
}
|
||||
double input_value(int const Index) const override
|
||||
{
|
||||
return m_velocity;
|
||||
}
|
||||
glm::dvec3 input_location() const override
|
||||
{
|
||||
return m_location;
|
||||
}
|
||||
// members
|
||||
double m_velocity{-1.0}; // 0: stop, -1: no limit
|
||||
glm::dvec3 m_location{0.0};
|
||||
|
||||
private:
|
||||
std::string type() const override
|
||||
{
|
||||
return "roadorder";
|
||||
}
|
||||
void deserialize_(cParser &Input, scene::scratch_data &Scratchpad) override {}
|
||||
void run_() override {}
|
||||
void export_as_text_(std::ostream &Output) const override {}
|
||||
};
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
@@ -12,6 +12,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#include "world/Track.h"
|
||||
#include "world/Event.h"
|
||||
#include "world/RoadOrder.h"
|
||||
#include "vehicle/DynObj.h"
|
||||
#include "vehicle/Driver.h"
|
||||
#include "vehicle/Train.h"
|
||||
@@ -37,6 +38,11 @@ constexpr double near_zone{30.0}; // a rail vehicle this close to the stop lines
|
||||
constexpr double clear_zone{20.0}; // a vehicle isn't put on the road with another one this close
|
||||
constexpr double retry_period{1.0}; // seconds to the next try, when a vehicle can't be put on the road
|
||||
constexpr double front_room{2.5}; // half the length a vehicle is presumed to have before it's made
|
||||
constexpr double queue_reach{250.0}; // how far back from a closed crossing the vehicles standing on the lanes are taken to wait for it
|
||||
constexpr double stuck_time{15.0}; // seconds a vehicle of a spawn point can stand still before it's taken off the road
|
||||
constexpr double stuck_speed{0.1}; // m/s, slower than this is standing still
|
||||
constexpr double reverse_speed{0.5}; // m/s, a vehicle going backwards faster than this is taken off the road
|
||||
constexpr double stripe_width{0.5}; // of a pedestrian crossing; the gaps are as wide
|
||||
|
||||
// a path as a curve: points along it, and distances along it
|
||||
class path_curve
|
||||
@@ -154,46 +160,34 @@ bool out_of_reach(segment_data const &Path, glm::dvec3 const &Point, double cons
|
||||
return glm::distance(glm::dvec2{start.x, start.z}, glm::dvec2{Point.x, Point.z}) > span + Reach;
|
||||
}
|
||||
|
||||
// an order for the drivers heading for a closed crossing; to them it's what a signal showing stop is to the driver of a train.
|
||||
// it doesn't go to the event manager and never runs: the drivers find it on the lane they scan, and ask it what speed it allows
|
||||
class stop_event : public basic_event
|
||||
// an order for the drivers heading for a closed crossing
|
||||
using stop_event = road_order;
|
||||
|
||||
// the road passing under specified point: the one whose axis is nearest. T: receives the value of the curve parameter of its axis there
|
||||
road_node const *road_under(glm::dvec3 const &Point, double &T)
|
||||
{
|
||||
|
||||
public:
|
||||
stop_event()
|
||||
road_node const *result{nullptr};
|
||||
auto best{std::numeric_limits<double>::max()};
|
||||
for (auto const *road : simulation::Roads.sequence())
|
||||
{
|
||||
m_passive = true;
|
||||
if (road == nullptr || road->m_editorremoved || out_of_reach(road->definition().axis, Point, 50.0))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
auto const &layout{road->definition()};
|
||||
path_curve const axis{layout.axis};
|
||||
double distance{0.0};
|
||||
auto const t{axis.nearest(Point, distance)};
|
||||
if (distance > 0.5 * layout.span(t) + 1.0 || std::abs(axis.point(t).y - Point.y) > 3.0 || distance >= best)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
best = distance;
|
||||
result = road;
|
||||
T = t;
|
||||
}
|
||||
void init() override {}
|
||||
std::string input_text() const override
|
||||
{
|
||||
return "SetVelocity";
|
||||
}
|
||||
TCommandType input_command() const override
|
||||
{
|
||||
return TCommandType::cm_SetVelocity;
|
||||
}
|
||||
double input_value(int const Index) const override
|
||||
{
|
||||
return m_velocity;
|
||||
}
|
||||
glm::dvec3 input_location() const override
|
||||
{
|
||||
return m_location;
|
||||
}
|
||||
// members
|
||||
double m_velocity{-1.0}; // 0: stop, -1: no limit
|
||||
glm::dvec3 m_location{0.0};
|
||||
|
||||
private:
|
||||
std::string type() const override
|
||||
{
|
||||
return "roadstop";
|
||||
}
|
||||
void deserialize_(cParser &Input, scene::scratch_data &Scratchpad) override {}
|
||||
void run_() override {}
|
||||
void export_as_text_(std::ostream &Output) const override {}
|
||||
};
|
||||
return result;
|
||||
}
|
||||
|
||||
// lane of a road with the point nearest to specified one, within specified distance from it. Station: receives the distance of that point from the start of the lane
|
||||
TTrack *nearest_lane(glm::dvec3 const &Point, double const Reach, double &Station)
|
||||
@@ -242,24 +236,25 @@ void roadpoint_node::state::normalize()
|
||||
interval = std::clamp(interval, 1.f, 3600.f);
|
||||
variation = std::clamp(variation, 0.f, 1.f);
|
||||
velocity = std::clamp(velocity, 5.f, 200.f);
|
||||
copies = std::clamp(copies, 1, 16);
|
||||
count = std::clamp(count, 1, 64);
|
||||
radius = std::clamp(radius, 0.5f, 50.f);
|
||||
length = std::clamp(length, 1.f, 20.f);
|
||||
}
|
||||
|
||||
roadpoint_node::roadpoint_node(scene::node_data const &Nodedata) : basic_node(Nodedata)
|
||||
{
|
||||
m_state.kind = (Nodedata.type == "spawn" ? kind_type::spawn : Nodedata.type == "despawn" ? kind_type::despawn : kind_type::crossing);
|
||||
m_state.kind = (Nodedata.type == "spawn" ? kind_type::spawn : Nodedata.type == "despawn" ? kind_type::despawn : Nodedata.type == "crosswalk" ? kind_type::crosswalk : kind_type::crossing);
|
||||
}
|
||||
|
||||
// scenery keyword for specified kind of point
|
||||
std::string roadpoint_node::keyword(kind_type const Kind)
|
||||
{
|
||||
return Kind == kind_type::spawn ? "spawn" : Kind == kind_type::despawn ? "despawn" : "crossing";
|
||||
return Kind == kind_type::spawn ? "spawn" : Kind == kind_type::despawn ? "despawn" : Kind == kind_type::crosswalk ? "crosswalk" : "crossing";
|
||||
}
|
||||
|
||||
bool roadpoint_node::is_keyword(std::string const &Type)
|
||||
{
|
||||
return Type == "crossing" || Type == "spawn" || Type == "despawn";
|
||||
return Type == "crossing" || Type == "spawn" || Type == "despawn" || Type == "crosswalk";
|
||||
}
|
||||
|
||||
// reads vehicles out of a text
|
||||
@@ -341,6 +336,8 @@ void roadpoint_node::import(cParser &Input, glm::dvec3 const &Offset)
|
||||
Input.getTokens(3);
|
||||
Input >> m_state.position.x >> m_state.position.y >> m_state.position.z;
|
||||
m_state.position += Offset;
|
||||
int copies{0}; // of each vehicle, the way the first sceneries with spawn points gave the size of the set
|
||||
auto counted{false};
|
||||
|
||||
auto token{Input.getToken<std::string>()};
|
||||
while (false == token.empty() && token != "end" + type)
|
||||
@@ -374,10 +371,21 @@ void roadpoint_node::import(cParser &Input, glm::dvec3 const &Offset)
|
||||
Input.getTokens();
|
||||
Input >> m_state.velocity;
|
||||
}
|
||||
else if (token == "count")
|
||||
{
|
||||
Input.getTokens();
|
||||
Input >> m_state.count;
|
||||
counted = true;
|
||||
}
|
||||
else if (token == "copies")
|
||||
{
|
||||
Input.getTokens();
|
||||
Input >> m_state.copies;
|
||||
Input >> copies;
|
||||
}
|
||||
else if (token == "length")
|
||||
{
|
||||
Input.getTokens();
|
||||
Input >> m_state.length;
|
||||
}
|
||||
else if (token == "radius")
|
||||
{
|
||||
@@ -441,6 +449,10 @@ void roadpoint_node::import(cParser &Input, glm::dvec3 const &Offset)
|
||||
}
|
||||
token = Input.getToken<std::string>();
|
||||
}
|
||||
if (false == counted && copies > 0)
|
||||
{
|
||||
m_state.count = copies * static_cast<int>(std::max<std::size_t>(1, m_state.vehicles.size()));
|
||||
}
|
||||
m_state.normalize();
|
||||
location(m_state.position);
|
||||
}
|
||||
@@ -449,10 +461,10 @@ void roadpoint_node::import(cParser &Input, glm::dvec3 const &Offset)
|
||||
void roadpoint_node::define(state const &State)
|
||||
{
|
||||
auto const vehicles{m_state.vehicles};
|
||||
auto const copies{m_state.copies};
|
||||
auto const count{m_state.count};
|
||||
m_state = State;
|
||||
m_state.normalize();
|
||||
if (m_prepared && (m_state.vehicles != vehicles || m_state.copies != copies))
|
||||
if (m_prepared && (m_state.vehicles != vehicles || m_state.count != count))
|
||||
{
|
||||
// vehicles can't be unmade, there's too much holding on to them. the old ones are left alone:
|
||||
// those which wait stay where they are, those on the road drive on until something takes them away
|
||||
@@ -492,6 +504,24 @@ void roadpoint_node::bind()
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kind_type::crosswalk:
|
||||
{
|
||||
// the stripes go across the road the point is on
|
||||
double t{0.0};
|
||||
auto const *road{road_under(m_state.position, t)};
|
||||
if (road != nullptr)
|
||||
{
|
||||
auto const &layout{road->definition()};
|
||||
path_curve const axis{layout.axis};
|
||||
m_onroad = true;
|
||||
m_along = axis.direction(t);
|
||||
m_halfwidth = 0.5 * layout.span(t);
|
||||
// the point itself may be off the axis, the stripes aren't
|
||||
auto const centre{axis.point(t)};
|
||||
m_state.position = centre;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case kind_type::despawn:
|
||||
{
|
||||
// the vehicles are taken from every lane which passes close enough
|
||||
@@ -525,25 +555,8 @@ void roadpoint_node::bind()
|
||||
void roadpoint_node::bind_crossing()
|
||||
{
|
||||
// the road the crossing is on: the one whose axis passes nearest
|
||||
road_node const *crossed{nullptr};
|
||||
auto best{std::numeric_limits<double>::max()};
|
||||
for (auto const *road : simulation::Roads.sequence())
|
||||
{
|
||||
if (road == nullptr || road->m_editorremoved || out_of_reach(road->definition().axis, m_state.position, 50.0))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
auto const &layout{road->definition()};
|
||||
path_curve const axis{layout.axis};
|
||||
double distance{0.0};
|
||||
auto const t{axis.nearest(m_state.position, distance)};
|
||||
if (distance > 0.5 * layout.width() * layout.scale(t) + 1.0 || std::abs(axis.point(t).y - m_state.position.y) > 3.0 || distance >= best)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
best = distance;
|
||||
crossed = road;
|
||||
}
|
||||
double where{0.0};
|
||||
auto const *crossed{road_under(m_state.position, where)};
|
||||
if (crossed != nullptr)
|
||||
{
|
||||
auto const &lanes{crossed->tracks()};
|
||||
@@ -659,6 +672,24 @@ void roadpoint_node::bind_crossing()
|
||||
{
|
||||
m_rails.emplace_back(entry.first);
|
||||
}
|
||||
// lanes the vehicles wait on while the crossing is closed: the ones with the stops, and what leads to them
|
||||
for (auto const &stop : m_stops)
|
||||
{
|
||||
auto *lane{stop.track};
|
||||
double reach{0.0};
|
||||
for (int guard = 0; lane != nullptr && guard < 64 && reach < queue_reach; ++guard)
|
||||
{
|
||||
if (std::find(m_queue.begin(), m_queue.end(), lane) == m_queue.end())
|
||||
{
|
||||
m_queue.emplace_back(lane);
|
||||
}
|
||||
if (false == lane->m_paths.empty())
|
||||
{
|
||||
reach += glm::distance(lane->m_paths.front().points[segment_data::point::start], lane->m_paths.front().points[segment_data::point::end]);
|
||||
}
|
||||
lane = lane->trPrev;
|
||||
}
|
||||
}
|
||||
close(m_closed);
|
||||
}
|
||||
|
||||
@@ -683,8 +714,10 @@ void roadpoint_node::unbind()
|
||||
}
|
||||
m_stops.clear();
|
||||
m_rails.clear();
|
||||
m_queue.clear();
|
||||
m_lanes.clear();
|
||||
m_seen.clear();
|
||||
m_onroad = false;
|
||||
}
|
||||
|
||||
// makes the vehicles of a spawn point
|
||||
@@ -709,47 +742,48 @@ void roadpoint_node::prepare()
|
||||
// vehicles are known by their names, so a point without one makes something up for them
|
||||
static int nameless{0};
|
||||
auto const basename{m_name.empty() ? "roadspawn" + std::to_string(++nameless) : m_name};
|
||||
for (auto const &definition : m_state.vehicles)
|
||||
// the listed vehicles are gone through as many times as it takes, from a random one on, so the set gets each kind in turn
|
||||
auto const kinds{m_state.vehicles.size()};
|
||||
auto const start{kinds > 0 ? std::min(kinds - 1, static_cast<std::size_t>(LocalRandom(0.0, static_cast<double>(kinds)))) : 0};
|
||||
for (int index = 0; index < m_state.count && kinds > 0; ++index)
|
||||
{
|
||||
for (int copy = 0; copy < m_state.copies; ++copy)
|
||||
auto const &definition{m_state.vehicles[(start + static_cast<std::size_t>(index)) % kinds]};
|
||||
// a vehicle like any other of the scenery, made where the point is and driven at the speed of the point...
|
||||
auto *vehicle{new TDynamicObject()};
|
||||
auto const length{vehicle->Init(basename + ":" + std::to_string(++m_made), definition.folder, definition.skin, definition.type, m_lanes.front(), m_offset + front_room, "headdriver",
|
||||
m_state.velocity, "", static_cast<float>(definition.load), definition.loadtype, false, "")};
|
||||
if (length == 0.0)
|
||||
{
|
||||
// a vehicle like any other of the scenery, made where the point is and driven at the speed of the point...
|
||||
auto *vehicle{new TDynamicObject()};
|
||||
auto const length{vehicle->Init(basename + ":" + std::to_string(++m_made), definition.folder, definition.skin, definition.type, m_lanes.front(), m_offset + front_room, "headdriver",
|
||||
m_state.velocity, "", static_cast<float>(definition.load), definition.loadtype, false, "")};
|
||||
if (length == 0.0)
|
||||
{
|
||||
ErrorLog("Bad road point: vehicle \"" + definition.folder + "/" + definition.type + "\" of spawn \"" + m_name + "\" couldn't be made");
|
||||
if (vehicle->MyTrack != nullptr)
|
||||
{
|
||||
vehicle->MyTrack->RemoveDynamicObject(vehicle);
|
||||
}
|
||||
delete vehicle;
|
||||
continue;
|
||||
}
|
||||
if (vehicle->mdModel != nullptr)
|
||||
{
|
||||
for (auto const &smokesource : vehicle->mdModel->smoke_sources())
|
||||
{
|
||||
simulation::Particles.insert(smokesource.first, vehicle, smokesource.second);
|
||||
}
|
||||
}
|
||||
if (false == simulation::Vehicles.insert(vehicle))
|
||||
{
|
||||
ErrorLog("Bad road point: vehicle name \"" + vehicle->name() + "\" made for spawn \"" + m_name + "\" is taken already");
|
||||
}
|
||||
pooled_vehicle entry;
|
||||
entry.vehicle = vehicle;
|
||||
entry.velocity = vehicle->MoverParameters->V;
|
||||
m_pool.emplace_back(entry);
|
||||
// ...which is taken off the road right away, the way the vehicles leaving the scenery are, to wait for its turn
|
||||
ErrorLog("Bad road point: vehicle \"" + definition.folder + "/" + definition.type + "\" of spawn \"" + m_name + "\" couldn't be made");
|
||||
if (vehicle->MyTrack != nullptr)
|
||||
{
|
||||
vehicle->MyTrack->RemoveDynamicObject(vehicle);
|
||||
vehicle->MyTrack = nullptr;
|
||||
}
|
||||
vehicle->bEnabled = false;
|
||||
delete vehicle;
|
||||
continue;
|
||||
}
|
||||
if (vehicle->mdModel != nullptr)
|
||||
{
|
||||
for (auto const &smokesource : vehicle->mdModel->smoke_sources())
|
||||
{
|
||||
simulation::Particles.insert(smokesource.first, vehicle, smokesource.second);
|
||||
}
|
||||
}
|
||||
if (false == simulation::Vehicles.insert(vehicle))
|
||||
{
|
||||
ErrorLog("Bad road point: vehicle name \"" + vehicle->name() + "\" made for spawn \"" + m_name + "\" is taken already");
|
||||
}
|
||||
pooled_vehicle entry;
|
||||
entry.vehicle = vehicle;
|
||||
entry.velocity = vehicle->MoverParameters->V;
|
||||
m_pool.emplace_back(entry);
|
||||
// ...which is taken off the road right away, the way the vehicles leaving the scenery are, to wait for its turn
|
||||
if (vehicle->MyTrack != nullptr)
|
||||
{
|
||||
vehicle->MyTrack->RemoveDynamicObject(vehicle);
|
||||
vehicle->MyTrack = nullptr;
|
||||
}
|
||||
vehicle->bEnabled = false;
|
||||
}
|
||||
if (m_pool.empty())
|
||||
{
|
||||
@@ -757,6 +791,12 @@ void roadpoint_node::prepare()
|
||||
}
|
||||
}
|
||||
|
||||
// true if specified vehicle stands in the line of vehicles held by the crossing
|
||||
bool roadpoint_node::holds(TDynamicObject const &Vehicle) const
|
||||
{
|
||||
return m_state.kind == kind_type::crossing && m_closed && false == m_editorremoved && std::find(m_queue.begin(), m_queue.end(), Vehicle.MyTrack) != m_queue.end();
|
||||
}
|
||||
|
||||
std::size_t roadpoint_node::waiting() const
|
||||
{
|
||||
return static_cast<std::size_t>(std::count_if(m_pool.begin(), m_pool.end(), [](pooled_vehicle const &Entry) { return false == Entry.vehicle->bEnabled && Entry.vehicle->MyTrack == nullptr; }));
|
||||
@@ -790,6 +830,11 @@ void roadpoint_node::update(double const Deltatime)
|
||||
update_despawn();
|
||||
break;
|
||||
}
|
||||
case kind_type::crosswalk:
|
||||
{
|
||||
// paint only
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -846,6 +891,36 @@ void roadpoint_node::update_spawn(double const Deltatime)
|
||||
// the point was made in the editor, or the scenery was loaded for editing
|
||||
prepare();
|
||||
}
|
||||
// vehicles of the point which got stuck are taken off the road, to wait for their next turn: the ones standing still
|
||||
// for too long, unless it's a closed level crossing they wait at, and the ones which started to go backwards
|
||||
for (auto &entry : m_pool)
|
||||
{
|
||||
auto *vehicle{entry.vehicle};
|
||||
if (false == vehicle->bEnabled || vehicle->MyTrack == nullptr || (simulation::Train != nullptr && simulation::Train->Dynamic() == vehicle))
|
||||
{
|
||||
// not on the road, or taken over by the user
|
||||
entry.standing = 0.0;
|
||||
continue;
|
||||
}
|
||||
auto const speed{vehicle->MoverParameters->V};
|
||||
// forward is the way it went when it was put on the road
|
||||
auto const backwards{speed * entry.velocity < 0.0 && std::abs(speed) > reverse_speed};
|
||||
if (std::abs(speed) > stuck_speed || simulation::Roadpoints.holds(*vehicle))
|
||||
{
|
||||
entry.standing = 0.0;
|
||||
}
|
||||
else
|
||||
{
|
||||
entry.standing += Deltatime;
|
||||
}
|
||||
if (backwards || entry.standing > stuck_time)
|
||||
{
|
||||
WriteLog("Road traffic: vehicle \"" + vehicle->name() + "\" taken off the road, " + (backwards ? "it was going backwards" : "it stood still for too long"));
|
||||
entry.standing = 0.0;
|
||||
vehicle->bEnabled = false;
|
||||
TDynamicObject::bDynamicRemove = true;
|
||||
}
|
||||
}
|
||||
if (m_pool.empty() || m_lanes.empty())
|
||||
{
|
||||
return;
|
||||
@@ -933,15 +1008,41 @@ void roadpoint_node::update_despawn()
|
||||
std::vector<scene::shape_node> roadpoint_node::create_shapes() const
|
||||
{
|
||||
std::vector<scene::shape_node> shapes;
|
||||
if (m_state.kind != kind_type::crossing || false == m_state.stoplines || m_stops.empty())
|
||||
{
|
||||
return shapes;
|
||||
}
|
||||
std::vector<world_vertex> vertices;
|
||||
glm::dvec3 const lift{0.0, line_lift, 0.0};
|
||||
glm::vec3 const upwards{0.f, 1.f, 0.f};
|
||||
if (m_state.kind == kind_type::crosswalk && m_onroad)
|
||||
{
|
||||
// stripes laid along the road, side by side from one edge of it to the other
|
||||
glm::dvec3 across{m_along.z, 0.0, -m_along.x};
|
||||
if (glm::length2(across) > 1e-12)
|
||||
{
|
||||
across = glm::normalize(across);
|
||||
auto const reach{m_halfwidth - stopline_inset};
|
||||
auto const stripes{std::max(1, static_cast<int>(std::floor((2.0 * reach + stripe_width) / (2.0 * stripe_width))))};
|
||||
// the stripes are spread evenly over the width, with one at each edge
|
||||
auto const first{-0.5 * (stripes * 2 - 1) * stripe_width};
|
||||
auto const back{-m_along * static_cast<double>(m_state.length)};
|
||||
for (int stripe = 0; stripe < stripes; ++stripe)
|
||||
{
|
||||
auto const offset{first + stripe * 2.0 * stripe_width};
|
||||
auto const right{m_state.position + across * offset + m_along * (0.5 * m_state.length) + lift};
|
||||
auto const left{right + across * stripe_width};
|
||||
vertices.push_back({right + back, upwards, {0.f, 0.f}});
|
||||
vertices.push_back({right, upwards, {1.f, 0.f}});
|
||||
vertices.push_back({left + back, upwards, {0.f, 1.f}});
|
||||
vertices.push_back({right, upwards, {1.f, 0.f}});
|
||||
vertices.push_back({left, upwards, {1.f, 1.f}});
|
||||
vertices.push_back({left + back, upwards, {0.f, 1.f}});
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto const &stop : m_stops)
|
||||
{
|
||||
if (m_state.kind != kind_type::crossing || false == m_state.stoplines)
|
||||
{
|
||||
break;
|
||||
}
|
||||
// a line across the lane, ending where the vehicles are to stop
|
||||
glm::dvec3 across{stop.direction.z, 0.0, -stop.direction.x};
|
||||
if (glm::length2(across) < 1e-12)
|
||||
@@ -993,7 +1094,7 @@ void roadpoint_node::hide()
|
||||
// radius() subclass details, calculates node's bounding radius
|
||||
float roadpoint_node::radius_()
|
||||
{
|
||||
return m_state.kind == kind_type::crossing ? m_state.clearance + 5.f : m_state.radius;
|
||||
return m_state.kind == kind_type::crossing ? m_state.clearance + 5.f : m_state.kind == kind_type::crosswalk ? static_cast<float>(m_halfwidth) + m_state.length : m_state.radius;
|
||||
}
|
||||
|
||||
// serialize() subclass details, sends content of the subclass to provided stream
|
||||
@@ -1025,7 +1126,7 @@ void roadpoint_node::export_as_text_(std::ostream &Output) const
|
||||
}
|
||||
case kind_type::spawn:
|
||||
{
|
||||
Output << "interval " << m_state.interval << ' ' << "variation " << m_state.variation << ' ' << "velocity " << m_state.velocity << ' ' << "copies " << m_state.copies << ' ';
|
||||
Output << "interval " << m_state.interval << ' ' << "variation " << m_state.variation << ' ' << "velocity " << m_state.velocity << ' ' << "count " << m_state.count << ' ';
|
||||
for (auto const &vehicle : m_state.vehicles)
|
||||
{
|
||||
// the way vehicles are put in a scenery. the path and the offset are there to keep that form, the point doesn't use them
|
||||
@@ -1043,6 +1144,11 @@ void roadpoint_node::export_as_text_(std::ostream &Output) const
|
||||
Output << "radius " << m_state.radius << ' ';
|
||||
break;
|
||||
}
|
||||
case kind_type::crosswalk:
|
||||
{
|
||||
Output << "length " << m_state.length << ' ';
|
||||
break;
|
||||
}
|
||||
}
|
||||
Output << "end" << type << "\n";
|
||||
}
|
||||
@@ -1110,6 +1216,12 @@ void roadpoint_table::create_geometry()
|
||||
}
|
||||
}
|
||||
|
||||
// true if specified vehicle stands in the line of vehicles held by a closed level crossing
|
||||
bool roadpoint_table::holds(TDynamicObject const &Vehicle) const
|
||||
{
|
||||
return std::any_of(m_items.begin(), m_items.end(), [&Vehicle](roadpoint_node const *Point) { return Point != nullptr && Point->holds(Vehicle); });
|
||||
}
|
||||
|
||||
// to be called with each step of the simulation
|
||||
void roadpoint_table::update(double const Deltatime)
|
||||
{
|
||||
|
||||
@@ -24,17 +24,20 @@ http://mozilla.org/MPL/2.0/.
|
||||
class TDynamicObject;
|
||||
class basic_event;
|
||||
|
||||
// a place on a road where something happens to the traffic. three kinds of it:
|
||||
// a place on a road where something happens to the traffic, or is painted for it:
|
||||
// - a level crossing: vehicles are stopped ahead of the rails for as long as a train is near,
|
||||
// - a point where vehicles appear, drawn from a set made ready while the scenery is loaded,
|
||||
// - a point where vehicles are taken away. the ones which came from a point of the previous kind go back to its set.
|
||||
// - a point where vehicles are taken away. the ones which came from a point of the previous kind go back to its set,
|
||||
// - a pedestrian crossing: stripes painted across the road.
|
||||
// scenery entries:
|
||||
// node <max> <min> <name> crossing <position> [clearance <m>] [warning <m>] [stoplines <yes|no>] endcrossing
|
||||
// node <max> <min> <name> spawn <position> [interval <s>] [variation <0..1>] [velocity <km/h>] [copies <n>]
|
||||
// node <max> <min> <name> spawn <position> [interval <s>] [variation <0..1>] [velocity <km/h>] [count <n>]
|
||||
// [node <max> <min> <name> dynamic <folder> <skin> <type> <path> <offset> <driver> <velocity> <load> [<load type>] enddynamic]... endspawn
|
||||
// node <max> <min> <name> despawn <position> [radius <m>] enddespawn
|
||||
// node <max> <min> <name> crosswalk <position> [length <m>] endcrosswalk
|
||||
// the vehicles of a spawn point are given the way vehicles are put in a scenery; where they're put and how they're driven
|
||||
// is up to the point, so the path, the offset, the driver and the velocity of these entries don't matter
|
||||
// is up to the point, so the path, the offset, the driver and the velocity of these entries don't matter. the listed vehicles
|
||||
// are what the point draws from to have <count> vehicles of its own on the roads; one which gets stuck is taken off the road
|
||||
class roadpoint_node : public scene::basic_node
|
||||
{
|
||||
|
||||
@@ -44,7 +47,8 @@ class roadpoint_node : public scene::basic_node
|
||||
{
|
||||
crossing,
|
||||
spawn,
|
||||
despawn
|
||||
despawn,
|
||||
crosswalk
|
||||
};
|
||||
struct vehicle_data
|
||||
{
|
||||
@@ -71,10 +75,12 @@ class roadpoint_node : public scene::basic_node
|
||||
float interval{20.f}; // seconds from a vehicle to the next
|
||||
float variation{0.5f}; // part of the interval which is drawn at random
|
||||
float velocity{50.f}; // speed the vehicles appear with, and are told to keep, km/h
|
||||
int copies{2}; // number of each vehicle made ready
|
||||
int count{6}; // how many vehicles of the point can be on the roads at a time. that many are made ready, drawn from the listed ones
|
||||
std::vector<vehicle_data> vehicles;
|
||||
// point where vehicles are taken away
|
||||
float radius{2.f}; // vehicles on lanes this close to the point are taken
|
||||
// pedestrian crossing
|
||||
float length{4.f}; // how much of the road the stripes take along it
|
||||
|
||||
// brings the content to a usable form
|
||||
void normalize();
|
||||
@@ -135,6 +141,8 @@ class roadpoint_node : public scene::basic_node
|
||||
return m_pool.size();
|
||||
}
|
||||
std::size_t waiting() const;
|
||||
// true if specified vehicle stands in the line of vehicles held by the crossing, which is closed
|
||||
bool holds(TDynamicObject const &Vehicle) const;
|
||||
// generates geometry of the stop lines
|
||||
std::vector<scene::shape_node> create_shapes() const;
|
||||
// puts geometry of the point in the scene, or takes it back
|
||||
@@ -153,6 +161,7 @@ class roadpoint_node : public scene::basic_node
|
||||
{
|
||||
TDynamicObject *vehicle{nullptr};
|
||||
double velocity{0.0}; // what its speed was when it was made, to be given back each time it's put on the road
|
||||
double standing{0.0}; // how long it's been standing still on the road
|
||||
};
|
||||
// methods
|
||||
float radius_() override;
|
||||
@@ -171,6 +180,7 @@ class roadpoint_node : public scene::basic_node
|
||||
std::vector<stop_data> m_stops;
|
||||
std::vector<basic_event *> m_events; // orders for the drivers, one for each stop. these are never freed, as a driver can hold on to one
|
||||
std::vector<TTrack *> m_rails; // rails a train closes the crossing from
|
||||
std::vector<TTrack *> m_queue; // lanes the vehicles held by the crossing wait on
|
||||
std::map<TDynamicObject const *, double> m_seen; // rail vehicles near, with how far each was the last time
|
||||
bool m_closed{false};
|
||||
double m_hold{0.0}; // time left for the crossing to stay closed after the last train was seen
|
||||
@@ -182,6 +192,10 @@ class roadpoint_node : public scene::basic_node
|
||||
bool m_prepared{false};
|
||||
int m_made{0}; // number of vehicles made so far, for their names
|
||||
double m_timer{0.0}; // time left to the next vehicle
|
||||
// pedestrian crossing: the road at the point
|
||||
bool m_onroad{false};
|
||||
glm::dvec3 m_along{0.0, 0.0, 1.0}; // the way the road goes
|
||||
double m_halfwidth{0.0}; // of the road
|
||||
owned_shapes m_shapes;
|
||||
};
|
||||
|
||||
@@ -199,6 +213,8 @@ class roadpoint_table : public basic_table<roadpoint_node>
|
||||
void create_geometry();
|
||||
// to be called with each step of the simulation
|
||||
void update(double const Deltatime);
|
||||
// true if specified vehicle stands in the line of vehicles held by a closed level crossing
|
||||
bool holds(TDynamicObject const &Vehicle) const;
|
||||
|
||||
private:
|
||||
bool m_geometry{false}; // the scene is ready to take geometry of the points
|
||||
|
||||
@@ -1942,6 +1942,29 @@ bool TTrack::SwitchForced(int i, TDynamicObject *o)
|
||||
return true;
|
||||
};
|
||||
|
||||
// picks one of the routes through a crossroads for a driver heading for it
|
||||
int TTrack::RouteDraw() const {
|
||||
|
||||
auto const count { std::min( RouteCount(), static_cast<int>( m_routeweights.size() ) ) };
|
||||
auto total { 0.0 };
|
||||
for( int route = 0; route < count; ++route ) {
|
||||
total += std::max( 0.f, m_routeweights[ route ] );
|
||||
}
|
||||
if( total <= 0.0 ) {
|
||||
// even odds, the way it's always been for the crossroads of the scenery
|
||||
return 1 + static_cast<int>( std::floor( Random( static_cast<double>( RouteCount() ) - 0.001 ) ) );
|
||||
}
|
||||
auto draw { Random( total ) };
|
||||
auto last { 1 };
|
||||
for( int route = 0; route < count; ++route ) {
|
||||
if( m_routeweights[ route ] <= 0.f ) { continue; }
|
||||
last = route + 1;
|
||||
draw -= m_routeweights[ route ];
|
||||
if( draw < 0.0 ) { break; }
|
||||
}
|
||||
return last;
|
||||
}
|
||||
|
||||
int TTrack::CrossSegment(int from, int into)
|
||||
{ // ustawienie wskaźnika na segement w pożądanym kierunku (into) od strony (from)
|
||||
// zwraca kod segmentu, z kierunkiem jazdy jako znakiem ±
|
||||
|
||||
@@ -12,6 +12,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <array>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/mat4x4.hpp>
|
||||
@@ -187,6 +188,7 @@ public:
|
||||
std::vector<segment_data> m_paths; // source data for owned paths
|
||||
bool m_editorremoved { false };
|
||||
scene::basic_node *m_road { nullptr }; // road or road junction which generated this path. such path is one-way, and isn't saved nor edited on its own
|
||||
std::array<float, 3> m_routeweights { 0.f, 0.f, 0.f }; // odds of each route through a crossroads being picked by a driver, by the number of the route. all zero: even odds
|
||||
int iterate_stamp = 0;
|
||||
|
||||
// sleepermodel optional parameter -------------------------------------------------
|
||||
@@ -280,6 +282,8 @@ public:
|
||||
int
|
||||
RouteCount() const {
|
||||
return SwitchExtension != nullptr ? SwitchExtension->iRoads - 1 : 1; }
|
||||
// picks one of the routes through a crossroads for a driver heading for it. the routes are numbered from 1
|
||||
int RouteDraw() const;
|
||||
void Load(cParser *parser, glm::dvec3 const &pOrigin);
|
||||
void init_segments( bool const Initial );
|
||||
void update_location();
|
||||
|
||||
Reference in New Issue
Block a user