mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-17 10:59:20 +02:00
2153 lines
89 KiB
C++
2153 lines
89 KiB
C++
/*
|
|
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 "application/applicationmode.h"
|
|
#include "application/editoruipanels.h"
|
|
#include "input/editormouseinput.h"
|
|
#include "input/editorkeyboardinput.h"
|
|
#include "vehicle/Camera.h"
|
|
#include "scene/sceneeditor.h"
|
|
#include "scene/scenenode.h"
|
|
#include "editor/editorTerrain.hpp"
|
|
#include "editor/editorTerrainStreamer.hpp"
|
|
#include "editor/editorOrthophoto.hpp"
|
|
#include "editor/editorTrack.hpp"
|
|
#include "editor/editorAlignment.hpp"
|
|
#include "editor/editorAlignmentStore.hpp"
|
|
#include "editor/editorSwitchStore.hpp"
|
|
#include "editor/editorSpeed.hpp"
|
|
#include "editor/editorRoad.hpp"
|
|
#include "editor/editorProfile.hpp"
|
|
#include "editor/editorInfra.hpp"
|
|
#include "editor/editorGauge.hpp"
|
|
#include "world/Sweep.h"
|
|
|
|
#include <array>
|
|
#include <map>
|
|
#include <chrono>
|
|
#include <functional>
|
|
#include <memory>
|
|
#include <optional>
|
|
#include <set>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
class TAnimModel;
|
|
// a tool of a field of work of the editor, a button in the toolbar and over the 3d view
|
|
struct editor_tool_button;
|
|
class TTrack;
|
|
namespace ui
|
|
{
|
|
class vehicles_bank;
|
|
struct vehicle_desc;
|
|
struct skin_set;
|
|
}
|
|
|
|
class editor_mode : public application_mode, private editor_track::observer
|
|
{
|
|
|
|
public:
|
|
// model entering the structure gauge, at its deepest place
|
|
struct gauge_hit
|
|
{
|
|
std::string model;
|
|
glm::dvec3 point{0.0};
|
|
double depth{0.0}; // m
|
|
TTrack const *track{nullptr}; // nearest one, may be gone by now
|
|
int path{0};
|
|
glm::dvec2 direction{0.0, 1.0}; // of the track there, in the plan
|
|
};
|
|
// joint of the paths found wrong by the joints check
|
|
struct joint_issue
|
|
{
|
|
enum class kind { gap, step, kink, cant, grade, open };
|
|
kind type{kind::gap};
|
|
TTrack *track{nullptr};
|
|
editor_track::point_ref point;
|
|
TTrack *other{nullptr};
|
|
editor_track::point_ref theirs;
|
|
glm::dvec3 position{0.0};
|
|
double value{0.0}; // m, deg, mm, per mille
|
|
bool fixed{false};
|
|
};
|
|
// constructors
|
|
editor_mode();
|
|
~editor_mode() override;
|
|
// methods
|
|
// initializes internal data structures of the mode. returns: true on success, false otherwise
|
|
bool init() override;
|
|
// mode-specific update of simulation data. returns: false on error, true otherwise
|
|
bool update() override;
|
|
// maintenance method, called when the mode is activated
|
|
void enter() override;
|
|
// maintenance method, called when the mode is deactivated
|
|
void exit() override;
|
|
// input handlers
|
|
void on_key(int Key, int Scancode, int Action, int Mods) override;
|
|
void on_cursor_pos(double Horizontal, double Vertical) override;
|
|
void on_mouse_button(int Button, int Action, int Mods) override;
|
|
void on_scroll(double const Xoffset, double const Yoffset) override;
|
|
void on_window_resize(int w, int h) override
|
|
{
|
|
;
|
|
}
|
|
void on_event_poll() override;
|
|
bool is_command_processor() const override;
|
|
// takes the last change back; changes made together (e.g. to the models of a selection) are taken back together
|
|
void undo_last();
|
|
static bool focus_active();
|
|
static void set_focus_active(bool isActive);
|
|
static TCamera& get_camera() { return Camera; }
|
|
static glm::mat4 projection_matrix(float const Aspect);
|
|
void toggle_ortho();
|
|
static bool change_history() { return m_change_history; }
|
|
static void set_change_history(bool enabled) { m_change_history = enabled; }
|
|
static bool settings_open() { return m_settings_open; }
|
|
static void set_settings_open(bool enabled) { m_settings_open = enabled; }
|
|
private:
|
|
// types
|
|
struct editormode_input
|
|
{
|
|
|
|
editormouse_input mouse;
|
|
editorkeyboard_input keyboard;
|
|
|
|
bool init();
|
|
void poll();
|
|
};
|
|
|
|
struct state_backup
|
|
{
|
|
|
|
TCamera camera;
|
|
bool freefly;
|
|
bool picking;
|
|
};
|
|
|
|
struct stored_profile;
|
|
struct EditorSnapshot
|
|
{
|
|
enum class Action { Move, Rotate, Scale, Add, Delete, TrackEdit, Other, RoadEdit, Array, Bend, Terrain };
|
|
// copy of a model an array made
|
|
struct array_copy
|
|
{
|
|
TAnimModel *model{nullptr}; // nullptr while the copy is out of the scene
|
|
UID uuid;
|
|
// what it takes to make the copy again, filled in when it's taken out of the scene
|
|
std::string serialized;
|
|
std::string name;
|
|
glm::dvec3 position{0.0};
|
|
glm::vec3 rotation{0.0f};
|
|
glm::vec3 scale{1.0f};
|
|
};
|
|
|
|
Action action{Action::Other};
|
|
std::string node_name; // node identifier (basic_node::name())
|
|
// direct pointer to node when available; used for in-memory undo/redo lookup
|
|
scene::basic_node *node_ptr{nullptr};
|
|
std::string serialized; // full text for recreate (used for Add/Delete)
|
|
glm::dvec3 position{0.0, 0.0, 0.0};
|
|
glm::vec3 rotation{0.0f, 0.0f, 0.0f};
|
|
glm::vec3 scale{1.0f, 1.0f, 1.0f};
|
|
UID uuid; // node UUID for reference, used as fallback lookup for deleted/recreated nodes
|
|
scene::layer_handle layer{null_handle}; // scenery layer of the node, so a recreated node returns to it
|
|
scene::instance_handle instance{0}; // include of a scenery template which was placed or removed, instead of a node
|
|
std::vector<scene::instance_handle> instances; // includes placed or removed together
|
|
std::vector<std::pair<scene::instance_handle, std::string>> directives; // includes changed together, with the directives to go back to
|
|
std::vector<std::pair<sweep_node *, sweep_node::state>> sweeps; // models along curves changed, with the definitions to go back to
|
|
std::vector<sweep_node *> sweeps_toggled; // models along curves made or removed: undo flips whether they're there
|
|
std::vector<std::pair<TTrack *, editor_track::state>> tracks;
|
|
std::vector<TTrack *> created;
|
|
std::vector<TTrack *> removed;
|
|
editor_road::record roads; // what a change of the roads comes to
|
|
std::vector<infra::object_state> infra; // objects which followed the paths
|
|
bool profiles{false}; // the stored grade line designs changed too, these were the ones before
|
|
std::vector<stored_profile> profile_library;
|
|
std::optional<std::vector<alignment_store::record>> alignments; // the kept horizontal alignments changed too, these were the ones before
|
|
std::optional<std::vector<switch_store::record>> switch_records; // the kept switches changed too, these were the ones before
|
|
std::vector<array_copy> copies; // models an array added to the one it was made of
|
|
bool joined{false}; // undone and redone together with the change before it
|
|
std::shared_ptr<terrain_streamer::change const> terrain; // the heightmap terrain from before the change (Action::Terrain)
|
|
};
|
|
void push_snapshot(scene::basic_node *node, EditorSnapshot::Action Action = EditorSnapshot::Action::Move, std::string const &Serialized = std::string());
|
|
|
|
std::vector<EditorSnapshot> m_history; // history of changes to nodes, used for undo functionality
|
|
std::vector<EditorSnapshot> g_redo;
|
|
// methods
|
|
void update_camera(double const Deltatime);
|
|
|
|
editor_ui *ui() const;
|
|
void redo_last();
|
|
// a single step of the history, see undo_last()
|
|
void undo_one();
|
|
void redo_one();
|
|
// the changes of the heightmap terrain since the last step become a step of the history: a stroke of the brush,
|
|
// or what a frame did; the next change is recorded from now on
|
|
void terrain_history_step();
|
|
// puts the terrain back as the step recorded it, the step left with the state it replaced
|
|
void restore_terrain(EditorSnapshot &Snapshot);
|
|
bool m_history_busy{false}; // undo or redo under way
|
|
void handle_brush_mouse_hold(int Action, int Button);
|
|
void apply_rotation_for_new_node(scene::basic_node *node, int rotation_mode, float fixed_rotation_value);
|
|
// members
|
|
state_backup m_statebackup; // helper, cached variables to be restored on mode exit
|
|
editormode_input m_input;
|
|
static TCamera Camera;
|
|
|
|
// focus (smooth camera fly-to) state
|
|
static bool m_focus_active;
|
|
glm::dvec3 m_focus_start_pos{0.0,0.0,0.0};
|
|
glm::dvec3 m_focus_target_pos{0.0,0.0,0.0};
|
|
glm::vec3 m_focus_start_angle{0.0f}; // camera pitch/yaw/roll at focus start
|
|
glm::vec3 m_focus_target_angle{0.0f}; // camera pitch/yaw/roll facing the focused object
|
|
double m_focus_time{0.0};
|
|
double m_focus_duration{0.6};
|
|
|
|
double fTime50Hz{0.0}; // bufor czasu dla komunikacji z PoKeys
|
|
scene::basic_editor m_editor;
|
|
scene::basic_node *m_node{nullptr}; // currently selected scene node
|
|
scene::instance_handle m_instance{0}; // currently selected include of a scenery template; these are handled as a whole
|
|
include_selection m_include; // parameters of the selected include, changed with the gizmo and in the node properties
|
|
bool m_include_gesture{false}; // a change of the selected include is under way, and has its undo snapshot already
|
|
std::vector<std::pair<TAnimModel *, int>> m_retired; // models replaced after changes of includes, with the frames left until they're destroyed
|
|
bool m_takesnapshot{true}; // helper, hints whether snapshot of selected node(s) should be taken before modification
|
|
bool m_dragging = false;
|
|
glm::dvec3 oldPos; // world position of the last brush placement
|
|
bool m_brush_has_last{false}; // false at the start of a brush stroke, so the first placement ignores spacing
|
|
bool mouseHold{false};
|
|
float kMaxPlacementDistance = 200.0f;
|
|
static bool m_change_history;
|
|
static bool m_settings_open;
|
|
|
|
double m_ortho_pitch{0.0};
|
|
bool m_ortho_pan{false};
|
|
glm::dvec2 m_ortho_pan_cursor{0.0};
|
|
void ortho_pan_to(double const Horizontal, double const Vertical);
|
|
void ortho_pan_stop();
|
|
// camera fly-mode (right mouse button held); used to flush motion when it's released
|
|
command_relay m_camera_relay;
|
|
bool m_camera_flying{false};
|
|
|
|
// UI/history settings
|
|
int m_max_history_size{200};
|
|
int m_selected_history_idx{-1};
|
|
glm::dvec3 clamp_mouse_offset_to_max(const glm::dvec3 &offset);
|
|
// the ground under a point the cursor landed on, for things which are to stand on the ground
|
|
glm::dvec3 placement_on_ground(glm::dvec3 Location);
|
|
// puts every instance of the model the selected node shows on the ground under it
|
|
void drop_model_instances();
|
|
void render_object_menu();
|
|
// undo, redo and what can be done with the selection, with their keys
|
|
void render_edit_menu();
|
|
// the list of the keyboard shortcuts, opened from the help menu or by F1
|
|
void render_help_menu();
|
|
void render_shortcuts_window();
|
|
bool m_shortcuts_open{false};
|
|
// deletes what's selected: a road piece or point, a signal, an include of a scenery template, a model, tracks (Del)
|
|
void delete_selected();
|
|
void delete_model(TAnimModel *Model, bool const Joined);
|
|
|
|
// more models selected at once, in the surroundings: m_node is the one the gizmo is on, the others are in m_selection.
|
|
// Shift+LMB adds a model or takes it out; a model of a group stands for the whole group
|
|
std::vector<scene::basic_node *> m_selection;
|
|
std::vector<scene::basic_node *> selected_nodes() const; // m_node first
|
|
std::vector<scene::basic_node *> selection_with_groups() const; // and the other models of their groups
|
|
void select_node(scene::basic_node *Node, bool const Additive);
|
|
void draw_selection_overlay();
|
|
// the other models of the selection follow m_node moved or turned by the gizmo
|
|
void selection_snapshots(EditorSnapshot::Action const Action);
|
|
void selection_follow(glm::dvec3 const &Moved, glm::vec3 const &Turned, glm::dvec3 const &Pivot);
|
|
// clipboard (Ctrl+C, Ctrl+V): definitions of the models, placed around the cursor as they were around the selected one
|
|
struct clipboard_entry
|
|
{
|
|
std::string definition;
|
|
glm::dvec3 offset{0.0};
|
|
glm::vec3 angles{0.0f};
|
|
glm::vec3 scale{1.0f};
|
|
int group{-1}; // models copied with their group are grouped again, each group anew
|
|
};
|
|
std::vector<clipboard_entry> m_clipboard;
|
|
void copy_selection();
|
|
void paste_clipboard(glm::dvec3 const &Location);
|
|
glm::dvec3 cursor_ground(); // the point of the ground under the cursor
|
|
// groups (Ctrl+G, Ctrl+Shift+G), written to the scenery as group ... endgroup
|
|
void group_selection();
|
|
void ungroup_selection();
|
|
void drop_selection_to_ground();
|
|
// menu at the cursor, Ctrl+RMB
|
|
bool m_contextopen{false};
|
|
glm::dvec3 m_contextpoint{0.0};
|
|
void render_context_menu();
|
|
|
|
// focus camera smoothly on specified node
|
|
void start_focus(scene::basic_node *node, double duration = 0.6);
|
|
|
|
// drops the node straight down onto the nearest surface below (terrain or another object). its group goes along with it,
|
|
// unless Alone: then the node goes by itself. Joined: undone together with the change before
|
|
void snap_to_ground(scene::basic_node *node, bool const Alone = false, bool const Joined = false);
|
|
|
|
// the heightmap terrain of the scenery (terrain/<name>/): chunks, sculpting, painting, water
|
|
void render_terrain_ui();
|
|
// the terrain as a field of work of its own: the terrain tools and the orthophoto in the tool options window
|
|
void render_terrain_tool_options();
|
|
// opens or leaves the terrain; leaving it puts the terrain tools away, so the mouse doesn't stay with them
|
|
void terrain_workspace(bool const Open);
|
|
// opens the heightmap terrain of the scenery for editing, named after the scenery, if the scenery has none yet. returns: true if it's open
|
|
bool ensure_terrain();
|
|
// adds chunks around the camera, Count along a side, at the height of the ground under each grid point where there's any
|
|
void add_chunks_around(int const Count);
|
|
// adds a chunk of the terrain shaped as the ground under it; where there's no ground, carried on level with the nearest point
|
|
// of it, or at the base height. returns: false if there's a chunk already
|
|
bool add_chunk_from_ground(heightmap::chunk_key const &Key, float const Spacing, std::uint16_t const Layer = 0);
|
|
// the tool of the terrain the left mouse button works
|
|
enum class terrain_tool
|
|
{
|
|
none, // picks the models, as in the surroundings
|
|
chunks, // adds a chunk, Shift: removes it
|
|
sculpt, // raises the ground, Shift: lowers it
|
|
smooth,
|
|
level, // leads the ground to the target height, Ctrl: picks the target height under the cursor
|
|
paint, // paints the chosen material, Shift: the first material of the palette
|
|
spacing, // selects chunks (Ctrl: adds or takes away one), whose point spacing is then converted
|
|
water, // outlines a body of water
|
|
restore, // takes the touch-ups over the modifiers back
|
|
fill // outlines an area filled with a material
|
|
};
|
|
terrain_tool m_terrain_tool{terrain_tool::none};
|
|
bool terrain_brush() const
|
|
{
|
|
return m_terrain_tool == terrain_tool::sculpt || m_terrain_tool == terrain_tool::smooth || m_terrain_tool == terrain_tool::level || m_terrain_tool == terrain_tool::paint ||
|
|
m_terrain_tool == terrain_tool::restore;
|
|
}
|
|
// converts the point spacing of the selected chunks to m_terrain_convert_spacing
|
|
void convert_selected_chunks();
|
|
// takes the height of whatever is under the cursor (terrain, track, model) for the target of the level brush. returns: false if there's nothing
|
|
bool pick_terrain_target();
|
|
bool terrain_clicking() const { return m_terrain_tool == terrain_tool::chunks || m_terrain_tool == terrain_tool::spacing || m_terrain_tool == terrain_tool::water || m_terrain_tool == terrain_tool::fill; }
|
|
// fills the outlined area with the material of the chosen palette entry, turned by m_paint_rotation
|
|
void fill_terrain_area();
|
|
// the brush, the hovered chunk and the outlines of water drawn over the viewport
|
|
void draw_terrain_overlay();
|
|
// forgets what's known about the ground, after the terrain changed
|
|
void forget_ground();
|
|
// conversion of the ground of the terrain files of the scenery (terrain directive, .txtf/.btf) to heightmap terrain
|
|
struct ground_conversion
|
|
{
|
|
struct source
|
|
{
|
|
std::string file; // as the terrain directive names it
|
|
std::uint16_t reference{0}; // see scene::terrain_file::reference_of()
|
|
bool text{false}; // read from the text file, rather than the binary one
|
|
bool good{false};
|
|
bool chosen{true};
|
|
std::string message;
|
|
std::size_t first{0}, count{0}; // its triangles
|
|
};
|
|
bool open{false};
|
|
std::vector<source> files;
|
|
std::vector<std::array<glm::dvec3, 3>> triangles;
|
|
std::vector<std::uint16_t> materials; // of the triangles: index of the name
|
|
std::vector<std::string> names; // of the materials
|
|
std::set<std::size_t> chosen; // materials to convert
|
|
std::string status;
|
|
} m_ground_conversion;
|
|
// reads the triangles of the terrain files of the scenery for the conversion
|
|
void load_ground_files();
|
|
// the folder of the textures as a browser, in the window of the node bank while the terrain is edited: the textures and the
|
|
// materials can be dragged onto the fields which take a material
|
|
struct texture_browser
|
|
{
|
|
struct item
|
|
{
|
|
std::string name; // as the materials are named: relative to the folder of the textures, without the extension
|
|
std::string label; // the file, without the folder
|
|
bool material{false}; // there's a .mat file of the name
|
|
};
|
|
std::string folder; // relative to the folder of the textures, empty or with a trailing slash
|
|
std::vector<std::string> folders; // under the current one
|
|
std::vector<item> items;
|
|
bool listed{false};
|
|
char filter[64]{};
|
|
float size{96.f}; // of the images, pixels
|
|
std::string selected;
|
|
std::map<std::string, int> thumbnails; // texture shown for the item of the name, 0: none
|
|
int loads{0}; // textures read in the frame, a few at most so the browser doesn't stall the editor
|
|
} m_textures;
|
|
void render_texture_browser();
|
|
// texture of the image of the item of the browser, 0 while there's none
|
|
std::uint64_t texture_thumbnail(texture_browser::item const &Item);
|
|
// takes a material dragged from the texture browser onto the item drawn last. returns: true if one was dropped, its name in Name
|
|
static bool material_drop(std::string &Name);
|
|
// the same, for a text field
|
|
static bool material_drop(char *Buffer, std::size_t Size);
|
|
// replaces the chosen triangles of the chosen terrain files with chunks of heightmap terrain of their shape, painted with
|
|
// their materials; the files lose them on save. returns: summary for the user
|
|
std::string convert_ground();
|
|
void render_ground_conversion();
|
|
// handles a click of the chunk, spacing and water tools (Shift: the other way round)
|
|
void handle_terrain_click(bool const Shift);
|
|
// writes the changes to the files of scenery opened for editing (Ctrl+S)
|
|
bool save();
|
|
// places include of specified scenery template at the cursor, in the active layer
|
|
void place_include(std::string const &File, int RotationMode, float FixedRotation);
|
|
// undo and redo of placing or removing an include: brings removed include back, or removes it
|
|
void toggle_include(scene::instance_handle Instance);
|
|
// makes specified include the selected one, 0 clears the selection
|
|
void select_include(scene::instance_handle Instance);
|
|
// writes parameter values of the selected include to its directive, and shows the outcome in the scene
|
|
void apply_include();
|
|
// replaces directive of specified include, and the models shown for it
|
|
void set_include_directive(scene::instance_handle Instance, std::string const &Directive);
|
|
// undo and redo of an include snapshot
|
|
void restore_include(EditorSnapshot &Snapshot);
|
|
// undo and redo of the includes placed, removed or changed together
|
|
void restore_includes(EditorSnapshot &Snapshot);
|
|
// gizmo for the selected include, limited to what the parameters of its template can express
|
|
void render_include_gizmo(glm::mat4 const &View, glm::mat4 const &Projection, glm::dvec3 const &Camerapos);
|
|
// exports the scenery in legacy (text) format, with layers hidden in the editor left out of the picture
|
|
void export_scenery();
|
|
// works the brush of the terrain under the cursor while the left mouse button is held
|
|
void handle_terrain_brush(double Deltatime);
|
|
// the chunk of the terrain (if any) covering the given world point
|
|
editor_terrain *terrain_at(double X, double Z);
|
|
// the chunks of the terrain held in memory
|
|
std::vector<editor_terrain *> active_terrains() const;
|
|
// samples the selected model instance's geometry into the chunks of the terrain it covers, then removes it
|
|
void capture_terrain();
|
|
bool m_terrain_open{false}; // the terrain is the field of work, chosen in the toolbar
|
|
float m_terrain_spacing{2.0f}; // point spacing of the chunks made and of the spacing tool
|
|
float m_terrain_baseheight{0.0f}; // height of chunks made where there's no ground
|
|
int m_terrain_add_count{4}; // chunks along a side added around the camera
|
|
float m_terrain_brush_radius{12.0f};
|
|
float m_terrain_brush_strength{4.0f}; // metres per second while held; for the paint, how fast it covers
|
|
// the level brush
|
|
float m_terrain_target{0.0f}; // height the ground is led to
|
|
int m_terrain_level_mode{0}; // 0: up and down, 1: only raises, 2: only lowers
|
|
bool m_terrain_target_from_stroke{false}; // the target is the height where a stroke starts
|
|
bool m_terrain_stroke{false}; // a stroke of the brush is under way
|
|
bool m_terrain_picking{false}; // the next click picks the target height
|
|
int m_terrain_layer{0}; // entry of the palette painted
|
|
char m_terrain_material[128]{"grass"}; // material added to the palette
|
|
float m_terrain_material_size{0.0f}; // metres its textures repeat at, 0: given by the material
|
|
float m_terrain_layer_size{0.0f}; // repeat of the chosen palette entry, while it's dragged
|
|
bool m_terrain_layer_size_dragged{false};
|
|
float m_terrain_layer_rotation{0.0f}; // angle of the chosen palette entry, while it's dragged
|
|
bool m_terrain_layer_rotation_dragged{false};
|
|
std::vector<glm::dvec3> m_water_points; // outline of a body of water being drawn
|
|
float m_water_level{0.0f};
|
|
bool m_water_level_edited{false}; // the level was given by hand, the points clicked don't change it
|
|
char m_water_material[128]{""};
|
|
int m_water_selected{-1};
|
|
char m_water_selected_material[128]{""}; // material of the chosen water, while it's edited
|
|
int m_water_material_of{-1}; // the water whose material is in the field above
|
|
float m_water_selected_size{0.0f}; // metres its texture repeats at, 0: given by the material
|
|
char m_water_default_material[128]{""}; // of the waters which don't name their own, while it's edited
|
|
bool m_water_default_edited{false};
|
|
std::set<heightmap::chunk_key> m_terrain_selection; // chunks chosen with the spacing tool
|
|
float m_terrain_convert_spacing{1.0f}; // point spacing the selected chunks are converted to
|
|
bool m_terrain_convert_paint{true}; // chunks converted to 0.5 m get paint samples every 0.5 m too
|
|
std::vector<glm::dvec3> m_paint_outline; // of the area filled with a material
|
|
float m_paint_feather{0.0f}; // metres the filled material blends in over across the outline, 0: sharp
|
|
float m_paint_rotation{0.0f}; // degrees the textures of the filled material are turned by
|
|
float m_water_selected_level{0.0f}; // level of the chosen water, while it's edited
|
|
bool m_water_level_dragged{false};
|
|
std::string m_terrain_status;
|
|
|
|
// the heightmap terrain, shared with the scenery loader so it shows in every mode
|
|
terrain_streamer &m_streamer{EditorTerrain};
|
|
|
|
// geoportal orthophoto layer drawn under the other viewport overlays
|
|
void render_orthophoto_ui();
|
|
void render_map_menu();
|
|
bool m_newscenery_asked{false};
|
|
void render_new_scenery_popup();
|
|
bool restart_for_new_scenery();
|
|
bool restart_editor(std::string const &Scenery);
|
|
bool m_openscenery_asked{false};
|
|
bool m_opened_at_track{false};
|
|
bool m_turntable_included{false};
|
|
bool turntable_controls(std::vector<std::string> &Rootstatements);
|
|
void starter_trainset(std::vector<std::string> &Trailing);
|
|
std::optional<std::pair<glm::dvec3, glm::vec3>> m_saved_camera;
|
|
char m_openscenery_search[64]{};
|
|
std::vector<std::string> m_openscenery_list;
|
|
std::string m_openscenery_choice;
|
|
void render_open_scenery_popup();
|
|
bool m_orthophoto_window{false};
|
|
void render_orthophoto_window();
|
|
// view menu: the top view, the lanes of the roads, the structure gauge, the environment
|
|
void render_view_menu();
|
|
// time of day, day of the year, visibility, clouds and air temperature, as the scenery is seen in the editor
|
|
void render_environment_window();
|
|
bool m_environment_open{false};
|
|
// over the 3d view: the tools of the field of work along its left edge, the modes of the gizmo at its top, the axes of the
|
|
// world and the top view in its top right corner
|
|
void render_viewport_overlays();
|
|
void render_viewport_tools();
|
|
// the tools of the field of work, in the order of the toolbar
|
|
void work_area_tools(std::vector<editor_tool_button> &Buttons);
|
|
// the tools of the field of work in the toolbar, as icons with their names
|
|
void render_toolbar_tools();
|
|
// what the row of the toolbar holds, written at its left end: the fields of work, the tools
|
|
static void render_toolbar_caption(char const *Text);
|
|
void render_viewport_gizmo();
|
|
void render_view_axes();
|
|
// the camera looking along an axis of the world (0: x, 1: y, 2: z) from its positive or negative side
|
|
void look_along_axis(int const Axis, bool const Positive);
|
|
// origin of the scenery in PUWG 1992 the scenery file gives in its //$g line, if it does
|
|
bool m_georeference_read{false};
|
|
bool m_georeference{false};
|
|
glm::dvec2 m_georeference_origin{0.0}; // northing, easting
|
|
std::string m_georeference_line;
|
|
void read_georeference();
|
|
void draw_orthophoto();
|
|
void draw_ortho_compass() const;
|
|
// picks up the stored settings and the origin of the current scenery
|
|
void load_orthophoto_settings();
|
|
void save_orthophoto_settings();
|
|
editor_orthophoto m_orthophoto;
|
|
std::string m_orthophoto_scenery; // scenery whose origin is loaded
|
|
glm::dvec2 m_orthophoto_origin_edit{0.0}; // origin being typed in (northing, easting); applied when editing ends
|
|
|
|
// hierarchy management
|
|
void add_to_hierarchy(scene::basic_node *node);
|
|
void remove_from_hierarchy(scene::basic_node *node);
|
|
scene::basic_node* find_in_hierarchy(const std::string &uuid_str);
|
|
scene::basic_node* find_node_by_any(scene::basic_node *node_ptr, const std::string &uuid_str, const std::string &name);
|
|
|
|
// clear history/redo pointers that reference the given node (prevent dangling pointers)
|
|
void nullify_history_pointers(scene::basic_node *node);
|
|
void render_change_history();
|
|
void render_settings();
|
|
|
|
// area fill: scatters models from a package over a polygon outlined in the viewport
|
|
void render_area_fill();
|
|
void draw_area_fill_outline() const;
|
|
void add_area_fill_point();
|
|
void run_area_fill();
|
|
void undo_last_area_fill();
|
|
std::vector<glm::dvec3> m_fill_points; // outline vertices (world space) in click order
|
|
std::vector<TAnimModel *> m_fill_last; // instances created by the most recent fill, for "Undo last fill"
|
|
std::vector<std::string> m_fill_custom; // user-assembled package of node templates
|
|
int m_fill_custom_idx{-1};
|
|
model_set_ref m_fill_source; // manual = m_fill_custom, otherwise a user set or node bank group
|
|
float m_fill_density{200.0f}; // objects per hectare
|
|
float m_fill_min_spacing{2.0f}; // minimal distance between placed objects (m)
|
|
bool m_fill_random_rotation{true}; // random yaw; otherwise the Functions panel rotation settings apply
|
|
float m_fill_scale_min{1.0f};
|
|
float m_fill_scale_max{1.0f};
|
|
bool m_fill_models_as_ground{true}; // large model instances (terrain tiles) count as ground
|
|
std::string m_fill_status; // result of the last fill, shown in the panel
|
|
|
|
// array: copies of the selected model set out in a row, after the array modifier of Blender
|
|
struct array_settings
|
|
{
|
|
int fit{0}; // 0: fixed count, 1: as many as the length takes
|
|
int count{2}; // the model itself included
|
|
float length{10.0f}; // m
|
|
bool relative{true};
|
|
glm::vec3 relative_offset{1.0f, 0.0f, 0.0f}; // of the size of the model, along its axes
|
|
bool constant{false};
|
|
glm::vec3 constant_offset{1.0f, 0.0f, 0.0f}; // m, along the axes of the model
|
|
float turn{0.0f}; // degrees around the vertical axis, each copy against the one before it
|
|
bool ground{false}; // the copies are put as high over the ground as the model is
|
|
bool operator==(array_settings const &) const = default;
|
|
};
|
|
struct array_tool
|
|
{
|
|
static constexpr int limit = 1000; // models in an array, so a slip of the finger doesn't freeze the editor
|
|
static constexpr double minimal_step = 0.001; // m, copies any closer would sit in one another
|
|
array_settings settings;
|
|
// the array made last, which follows the settings until something else is selected or changed
|
|
TAnimModel *base{nullptr}; // model it was made of, nullptr if there's none
|
|
std::size_t step{0}; // size of the undo history with the array at its top
|
|
array_settings applied; // what the copies were set out with
|
|
glm::dvec3 location{0.0}; // of the model then
|
|
glm::vec3 angles{0.0f};
|
|
glm::vec3 scale{1.0f};
|
|
// size of the model asked about last
|
|
TModel3d const *sized{nullptr};
|
|
glm::vec3 size{0.0f};
|
|
std::string status; // outcome of the last operation, shown in the panel
|
|
};
|
|
array_tool m_array;
|
|
void render_array();
|
|
// size of the model of specified instance along its axes, the scale of the instance left out
|
|
glm::vec3 array_size(TAnimModel const &Base);
|
|
// offset of each copy from the one before it, along the axes of the model
|
|
glm::dvec3 array_offset(TAnimModel const &Base);
|
|
// number of the models in the array, the one it's made of included. Step: length of the offset
|
|
int array_count(double const Step) const;
|
|
// location and angles of each copy in turn
|
|
std::vector<std::pair<glm::dvec3, glm::vec3>> array_placements(TAnimModel &Base);
|
|
// true while the array made last follows the settings
|
|
bool array_live() const;
|
|
void make_array();
|
|
// brings the copies of the array made last to what the settings ask for
|
|
void shape_array();
|
|
void update_array();
|
|
void restore_array_snapshot(EditorSnapshot Snapshot, std::vector<EditorSnapshot> &Opposite, bool const Undo);
|
|
|
|
// ImGuizmo-based transform gizmo for the selected node
|
|
enum class gizmo_operation { translate, rotate, scale };
|
|
void render_gizmo();
|
|
// gizmo settings, drawn in the toolset window
|
|
void render_gizmo_options();
|
|
// the tools of the field of work in the toolbar under the menu
|
|
void render_toolbar();
|
|
// an edit mode of the node bank as the field of work, from the toolbar or its key (1-5)
|
|
void choose_edit_mode(nodebank_panel::edit_mode const Mode);
|
|
// fields of work of the editor, in the row above the toolbar: the models around, the tracks, the roads, the terrain
|
|
enum class work_area
|
|
{
|
|
surroundings,
|
|
tracks,
|
|
roads,
|
|
terrain
|
|
};
|
|
work_area current_work_area() const;
|
|
void choose_work_area(work_area const Area);
|
|
void render_workspaces();
|
|
bool m_gizmo_enabled{true}; // master switch for the in-viewport gizmo
|
|
bool m_gizmo_using{false}; // tracks an ongoing drag, so a single undo snapshot is taken per drag
|
|
bool m_gizmo_local{false}; // manipulate in the object's local space instead of world space
|
|
gizmo_operation m_gizmo_op{gizmo_operation::translate}; // current transform mode (translate/rotate/scale)
|
|
float m_gizmo_snap{1.0f}; // translation snap step (metres) applied while Ctrl is held
|
|
|
|
TTrack *selected_track() const;
|
|
void render_path_ui();
|
|
void draw_track_overlay() const;
|
|
editor_track::point_ref track_handle_hit() const;
|
|
void select_track(scene::basic_node *Node);
|
|
void render_track_gizmo();
|
|
void commit_track_drag(bool const Force);
|
|
void cancel_track_tools();
|
|
void push_track_snapshot(std::vector<std::pair<TTrack *, editor_track::state>> States, std::vector<TTrack *> Created = {}, std::vector<TTrack *> Removed = {});
|
|
// false, with the reason, when one of the paths can't be changed, its file not taking the changes back
|
|
static bool tracks_editable(std::vector<TTrack *> const &Tracks, std::string &Reason);
|
|
void trim_history();
|
|
void restore_track_snapshot(EditorSnapshot const &Snapshot, std::vector<EditorSnapshot> &Opposite, bool const Undo);
|
|
// changes of the kept alignments and switches go into the last step of the history, made just before them
|
|
void records_to_history();
|
|
// the paths a drag of the gizmo takes, to tell after it what became of the kept alignments and switches
|
|
struct record_watch
|
|
{
|
|
bool active{false};
|
|
std::vector<TTrack *> moving;
|
|
std::vector<std::pair<glm::dvec3, glm::dvec3>> before; // ends of the first path of each moving track
|
|
std::map<int, std::vector<TTrack *>> on; // the kept alignments and the moving paths on them
|
|
std::set<int> whole; // the kept alignments all of whose paths move
|
|
} m_record_watch;
|
|
void records_watch_begin();
|
|
void records_watch_end();
|
|
editor_track::point_ref m_track_point;
|
|
std::vector<TTrack *> m_track_drag;
|
|
std::vector<std::pair<TTrack *, editor_track::point_ref>> m_track_drag_points;
|
|
std::vector<editor_track::joint> m_track_joints; // of the neighbours, following the whole track dragged
|
|
editor_track::snap_target m_track_snap;
|
|
glm::dvec3 m_track_pivot{0.0};
|
|
glm::mat4 m_track_gizmo{1.0f};
|
|
bool m_track_gizmo_using{false};
|
|
struct track_gizmo_drag
|
|
{
|
|
gizmo_operation operation{gizmo_operation::translate};
|
|
bool control{false};
|
|
double angle{0.0};
|
|
int path{-1};
|
|
bool atend{true};
|
|
std::size_t pieces{1};
|
|
std::vector<segment_data> paths;
|
|
editor_track::point_ref point;
|
|
glm::dvec3 origin{0.0};
|
|
glm::dvec3 axis{0.0};
|
|
double grabbed{0.0};
|
|
double length{0.0};
|
|
double stretch{0.0};
|
|
std::vector<std::pair<TTrack *, editor_track::point_ref>> partners;
|
|
glm::dvec3 fixed{0.0};
|
|
std::vector<std::pair<TTrack *, std::vector<int>>> held;
|
|
std::vector<TTrack *> extra;
|
|
int ring{-1};
|
|
glm::dmat3 basis{1.0};
|
|
glm::dvec3 pivot{0.0};
|
|
double grade{0.0};
|
|
double roll{0.0};
|
|
double roll0{0.0};
|
|
double pivot_chainage{0.0};
|
|
std::vector<double> chainages;
|
|
std::vector<double> lengths;
|
|
struct switch_joint
|
|
{
|
|
TTrack *track{nullptr};
|
|
editor_track::point_ref own;
|
|
editor_track::point_ref theirs;
|
|
glm::dvec3 point{0.0};
|
|
glm::dvec3 tangent{0.0};
|
|
double roll{0.0};
|
|
bool moved{false};
|
|
std::vector<std::pair<TTrack *, std::vector<segment_data>>> cluster;
|
|
std::vector<std::pair<TTrack *, std::vector<editor_track::joint>>> joints;
|
|
};
|
|
std::vector<switch_joint> switches;
|
|
};
|
|
track_gizmo_drag m_track_gizmo_drag;
|
|
gizmo_operation track_gizmo_operation(TTrack const &Track, bool const Pointmode) const;
|
|
glm::mat4 track_gizmo_frame(TTrack const &Track, bool const Pointmode, gizmo_operation const Operation) const;
|
|
void track_gizmo_hold();
|
|
void track_gizmo_settle();
|
|
void track_gizmo_stretch(TTrack &Track, double const Length);
|
|
void track_gizmo_control(TTrack &Track, glm::dvec3 Control);
|
|
void track_gizmo_point(TTrack &Track, glm::dvec3 const &Position);
|
|
void track_gizmo_switches(TTrack &Track);
|
|
void track_gizmo_begin(TTrack &Track, bool const Pointmode, gizmo_operation const Operation);
|
|
void track_gizmo_turn(TTrack &Track, int const Axis, double const Angle);
|
|
void track_gizmo_length(TTrack &Track, double Length);
|
|
void track_gizmo_end(TTrack &Track);
|
|
bool m_track_dirty{false};
|
|
std::chrono::steady_clock::time_point m_track_last_commit;
|
|
editor_track::state m_track_field_before;
|
|
std::array<std::array<char, 256>, 3> m_track_materials{};
|
|
std::array<TTrack const *, 3> m_track_material_edited{}; // path whose texture name is being typed
|
|
// typed names of the track circuits, the sleeper model and its skin, and the event to add, for the path
|
|
TTrack const *m_track_names_for{nullptr};
|
|
std::array<char, 256> m_track_isolated{};
|
|
std::array<char, 256> m_track_sleeper_model{};
|
|
std::array<char, 256> m_track_sleeper_skin{};
|
|
std::array<char, 128> m_track_event{};
|
|
int m_track_event_list{0};
|
|
std::string m_track_events_missing;
|
|
void render_path_circuits(TTrack &Track);
|
|
std::string m_track_mode_notice;
|
|
// structure gauge along the line of the selected path, shown in the 3d view
|
|
struct gauge_state
|
|
{
|
|
bool open{false}; // window
|
|
bool enabled{true}; // tunnel along the selected path
|
|
std::vector<gauge::profile> profiles;
|
|
int profile{0}; // railway
|
|
int road{-1};
|
|
TTrack const *track{nullptr}; // scanned
|
|
std::size_t history{0};
|
|
bool pending{false}; // rescan once the edit ends
|
|
struct
|
|
{
|
|
std::vector<glm::dvec3> surface; // triangles of the tunnel
|
|
std::vector<glm::dvec3> edges; // lines
|
|
std::vector<gauge_hit> hits;
|
|
} line; // along the selected path
|
|
struct
|
|
{
|
|
bool scanned{false};
|
|
std::size_t history{0};
|
|
std::string profile;
|
|
std::vector<gauge_hit> hits;
|
|
} map; // whole scenery
|
|
struct
|
|
{
|
|
std::vector<glm::dvec3> surface;
|
|
std::vector<glm::dvec3> edges;
|
|
} spot; // tunnel around the hit the camera went to
|
|
int current{-1}; // hit the camera went to
|
|
bool published{false}; // overlay up to date
|
|
std::shared_ptr<struct gauge_scan> scan; // of the whole scenery, done in slices over the frames
|
|
} m_gauge;
|
|
void gauge_load();
|
|
// the chosen railway and road outlines
|
|
gauge::choice gauge_choice() const;
|
|
std::vector<gauge_hit> const &gauge_hits() const;
|
|
void update_gauge();
|
|
void scan_gauge(TTrack &Track);
|
|
void scan_gauge_map();
|
|
void step_gauge_map();
|
|
void finish_gauge_map();
|
|
void gauge_publish();
|
|
void gauge_focus(int Index);
|
|
void gauge_spot(gauge_hit const &Hit);
|
|
void render_gauge_window();
|
|
void render_gauge_hits();
|
|
bool render_gauge_choice(char const *Label, int &Index, bool Road);
|
|
bool render_gauge_outline(int Index);
|
|
struct route_design
|
|
{
|
|
TTrack *from{nullptr};
|
|
TTrack *to{nullptr};
|
|
editor_track::chain chain;
|
|
std::string error;
|
|
std::string status;
|
|
alignment::design design;
|
|
alignment::result result;
|
|
int vertex{-1};
|
|
int grip{-1};
|
|
bool single{true}; // one curve between its straights, instead of the whole line
|
|
double origin{0.0}; // km of the start, m
|
|
int designed{0}; // vertices whose parameters came from the alignment kept in the scenery
|
|
int departed{-1}; // alignment kept in the scenery near the fragment, which its paths no longer follow
|
|
double departure{0.0};
|
|
};
|
|
void render_route_ui();
|
|
bool render_route_parameters();
|
|
bool render_route_vertices();
|
|
bool render_route_vertex(int const Index);
|
|
void render_route_result();
|
|
void route_reset();
|
|
void route_bind_ends();
|
|
bool route_from_curve(TTrack &Track);
|
|
bool route_from_line(TTrack &Track);
|
|
alignment::vertex route_vertex_of(editor_track::curve const &Curve) const;
|
|
bool route_load(TTrack &Track);
|
|
void route_read_stored();
|
|
void route_take_stored(int const Index);
|
|
void record_switch(TTrack &Track, editor_track::switch_template const &Shape);
|
|
void record_slip(std::vector<TTrack *> const &Parts, std::string const &Kind, std::string const &Type, double const Radius, double const Angle, std::string const &Base);
|
|
void render_switch_record(TTrack &Track);
|
|
void route_store();
|
|
bool digitize_drawn(std::vector<TTrack *> const &Created, bool const Alone = true);
|
|
bool digitize_chain(editor_track::chain const &Chain);
|
|
void render_route_stored();
|
|
void route_update() { m_route.result = alignment::compute(m_route.design); }
|
|
void route_apply();
|
|
bool route_relay_through_switches(editor_track::chain const &Chain, std::vector<std::pair<TTrack *, editor_track::state>> &States, std::vector<TTrack *> &Created, TTrack *&Last);
|
|
void route_recommend(alignment::vertex &Vertex) const;
|
|
void draw_route_overlay() const;
|
|
void render_route_gizmo();
|
|
glm::dvec3 route_vertex_position(int const Vertex) const;
|
|
bool route_active() const;
|
|
route_design m_route;
|
|
|
|
struct straights_state
|
|
{
|
|
editor_track::straight_tolerance tolerance;
|
|
double minimum_length{50.0};
|
|
std::vector<editor_track::straight> found;
|
|
int listed{-1};
|
|
TTrack *current_for{nullptr};
|
|
editor_track::straight current;
|
|
std::vector<editor_track::straight> neighbours;
|
|
std::vector<double> spacings{3.5, 4.0, 4.5, 4.75, 5.0, 5.5, 6.0};
|
|
char filter[32]{};
|
|
int handle{-1};
|
|
bool dragging{false};
|
|
editor_track::straight drag_line;
|
|
std::vector<editor_track::straight> set;
|
|
std::vector<editor_track::straight> drag_lines;
|
|
bool fitting{false};
|
|
editor_track::curve fit_before;
|
|
editor_track::curve fit_after;
|
|
bool fit_has_before{false};
|
|
bool fit_has_after{false};
|
|
std::vector<std::pair<TTrack *, editor_track::state>> drag_states;
|
|
glm::dvec3 preview_start{0.0};
|
|
glm::dvec3 preview_end{0.0};
|
|
glm::mat4 gizmo{1.0f};
|
|
glm::dvec3 edit_start{0.0};
|
|
glm::dvec3 edit_end{0.0};
|
|
double edit_length{0.0};
|
|
double edit_azimuth{0.0};
|
|
double distance{4.0};
|
|
double curve_radius{0.0};
|
|
bool auto_transitions{true};
|
|
double transition{0.0};
|
|
int tool{0};
|
|
bool tool_mouse{false};
|
|
std::vector<glm::dvec3> detour;
|
|
editor_track::straight detour_line;
|
|
glm::dvec3 detour_mouse{0.0};
|
|
bool tool_placed{false};
|
|
bool tool_dragging{false};
|
|
double tool_at{0.0};
|
|
glm::dvec3 tool_point{0.0};
|
|
glm::dvec3 tool_handle{0.0};
|
|
editor_track::straight tool_line;
|
|
glm::mat4 tool_gizmo{1.0f};
|
|
std::string status;
|
|
};
|
|
void render_straights_ui();
|
|
void draw_straights_overlay() const;
|
|
editor_track::straight const ¤t_straight();
|
|
bool straights_active();
|
|
void render_straight_gizmo();
|
|
void straight_apply(editor_track::straight const &Line, glm::dvec3 const &Start, glm::dvec3 const &End);
|
|
void straights_apply(std::vector<editor_track::straight> const &Lines, std::vector<std::pair<glm::dvec3, glm::dvec3>> const &Ends);
|
|
bool in_straight_set(editor_track::straight const &Line) const;
|
|
bool place_straight_tool();
|
|
void render_straight_tool_gizmo();
|
|
void apply_straight_tool();
|
|
double straight_tool_radius(editor_track::straight const &Line) const;
|
|
void straight_reshape(editor_track::straight const &Line, double const From, double const To, std::function<glm::dvec3(glm::dvec3 const &)> const &Tail, double const Radius);
|
|
void straight_refresh();
|
|
bool start_curve_fit(editor_track::straight const &Line, int const Handle);
|
|
void update_curve_fit(glm::dvec3 const &Start, glm::dvec3 const &End);
|
|
void add_detour_point();
|
|
void apply_detour();
|
|
std::vector<glm::dvec3> detour_outline() const;
|
|
void find_neighbour_straights();
|
|
// listed spacing of a neighbouring straight the line can snap to, if it is close enough
|
|
struct parallel_offer
|
|
{
|
|
bool nearby{false};
|
|
bool snaps{false};
|
|
double spacing{0.0};
|
|
double distance{0.0};
|
|
double angle{0.0}; // deg between the two straights
|
|
double correction{0.0}; // m along the line normal to reach the spacing
|
|
editor_track::straight const *neighbour{nullptr};
|
|
};
|
|
parallel_offer straight_parallel_offer(editor_track::straight const &Line, glm::dvec3 const &Offset) const;
|
|
glm::dvec3 snap_straight_offset(editor_track::straight const &Line, glm::dvec3 const &Offset) const;
|
|
glm::dvec3 snap_straight_direction(glm::dvec3 const &Pivot, glm::dvec3 const &Moved) const;
|
|
straights_state m_straights;
|
|
|
|
struct curve_sample
|
|
{
|
|
double station{0.0};
|
|
glm::dvec3 position{0.0};
|
|
glm::dvec2 tangent{0.0, 1.0};
|
|
float roll{0.f};
|
|
float radius{0.f};
|
|
};
|
|
struct switch_tool
|
|
{
|
|
bool curved{false};
|
|
std::vector<curve_sample> frame;
|
|
std::vector<TTrack *> frame_tracks;
|
|
std::vector<editor_track::switch_template> templates;
|
|
bool collected{false};
|
|
int armed{-1};
|
|
int last{0}; // template armed the last time
|
|
bool placing{false};
|
|
double along{0.0};
|
|
glm::dvec3 point{0.0};
|
|
glm::dvec3 mouse{0.0};
|
|
editor_track::straight line;
|
|
std::string status;
|
|
char name[64]{};
|
|
struct drive_family
|
|
{
|
|
std::string name;
|
|
std::array<std::string, 4> files;
|
|
std::array<double, 4> heights{};
|
|
bool preferred{false};
|
|
};
|
|
std::vector<drive_family> drives;
|
|
bool drives_scanned{false};
|
|
int drive{-1};
|
|
int drive_side{0};
|
|
struct slide_state
|
|
{
|
|
bool active{false};
|
|
TTrack *track{nullptr};
|
|
glm::vec2 pressed{0.f};
|
|
double grip{0.0};
|
|
double low{0.0};
|
|
double high{0.0};
|
|
double distance{0.0};
|
|
std::vector<double> joints;
|
|
};
|
|
slide_state slide;
|
|
double slide_typed{0.0};
|
|
};
|
|
switch_tool m_switch;
|
|
bool switch_room(TTrack &Track, double &Low, double &High, std::string &Reason);
|
|
bool move_switch_paths(TTrack &Track, std::vector<segment_data> const &Paths, std::string &Status);
|
|
bool slide_switch(TTrack &Track, double const Distance);
|
|
bool switch_slides(TTrack const &Track) const;
|
|
void switch_slide_start(TTrack &Track);
|
|
void switch_slide_update();
|
|
void switch_slide_finish();
|
|
void draw_switch_slide() const;
|
|
void scan_switch_drives();
|
|
std::string switch_name_for_new() const;
|
|
bool place_switch_drive(TTrack &Switch);
|
|
bool flip_switch_drive(TTrack &Switch);
|
|
void advance_switch_name();
|
|
void join_last_steps();
|
|
bool render_switch_drive_choice();
|
|
int m_turnout_template{0};
|
|
struct turntable_template
|
|
{
|
|
std::string file;
|
|
std::string name;
|
|
double length{0.0};
|
|
int angles{0};
|
|
int positions{0};
|
|
int track{0};
|
|
int yaw{0};
|
|
std::vector<double> fixed;
|
|
};
|
|
struct turntable_fit
|
|
{
|
|
editor_track::snap_target end;
|
|
double heading{0.0};
|
|
double radius{0.0};
|
|
double length{0.0};
|
|
std::vector<segment_data> pieces;
|
|
std::string issue;
|
|
bool chosen{true};
|
|
TTrack *replaces{nullptr};
|
|
};
|
|
struct turntable_tool
|
|
{
|
|
std::vector<turntable_template> templates;
|
|
bool scanned{false};
|
|
int chosen{-1};
|
|
char name[64]{};
|
|
bool placing{false};
|
|
glm::dvec3 centre{0.0};
|
|
double yaw{0.0};
|
|
glm::vec2 pressed{0.f};
|
|
editor_track::snap_target snap;
|
|
TTrack *table{nullptr};
|
|
scene::instance_handle include{0};
|
|
int kind{-1};
|
|
double length{0.0};
|
|
double table_yaw{0.0};
|
|
std::vector<double> exits;
|
|
float reach{80.f};
|
|
float radius{150.f};
|
|
float stub{25.f};
|
|
float step{0.f};
|
|
std::vector<turntable_fit> fits;
|
|
int hovered{-1};
|
|
double heading{0.0};
|
|
double exit_length{0.0};
|
|
std::string status;
|
|
std::string error;
|
|
};
|
|
turntable_tool m_turntable;
|
|
void scan_turntables();
|
|
std::string turntable_name_for_new() const;
|
|
bool turntable_read(TTrack *Table);
|
|
std::vector<scene::instance_handle> turntable_includes(TTrack const &Table);
|
|
glm::dvec3 turntable_centre() const;
|
|
std::vector<double> turntable_lines() const;
|
|
double turntable_snap(double const Heading) const;
|
|
void turntable_finish_placement();
|
|
void turntable_place(glm::dvec3 const &Centre, double const Yaw);
|
|
bool turntable_fit_end(editor_track::snap_target const &End, turntable_fit &Fit) const;
|
|
void turntable_find_fits();
|
|
void turntable_build(std::vector<turntable_fit const *> const &Fits);
|
|
bool turntable_exit_at(glm::dvec3 const &Ground, double &Heading, double &Length) const;
|
|
void turntable_store_exits();
|
|
void render_turntable_ui();
|
|
void draw_turntable_overlay() const;
|
|
struct extend_tool
|
|
{
|
|
bool active{false};
|
|
TTrack *track{nullptr};
|
|
glm::dvec3 point{0.0};
|
|
glm::dvec2 direction{0.0, 1.0};
|
|
double grade{0.0};
|
|
glm::dvec3 mouse{0.0};
|
|
int path{0};
|
|
bool atend{true};
|
|
editor_track::snap_target origin;
|
|
editor_track::snap_target snap;
|
|
glm::vec2 pressed{0.f};
|
|
};
|
|
extend_tool m_extend;
|
|
// lays new track through the clicked points, with no path needed to start from
|
|
struct lay_tool
|
|
{
|
|
bool active{false};
|
|
std::vector<glm::dvec3> points;
|
|
editor_track::snap_target start; // free end the track starts from, if any
|
|
glm::dvec3 mouse{0.0};
|
|
editor_track::snap_target mouse_snap;
|
|
std::vector<segment_data> preview;
|
|
std::string preview_error;
|
|
double preview_length{0.0};
|
|
double radius{500.0};
|
|
bool transitions{true};
|
|
double piece_length{100.0};
|
|
bool copy_selected{false};
|
|
editor_track::path_style style;
|
|
std::string status;
|
|
};
|
|
lay_tool m_lay;
|
|
void render_lay_ui();
|
|
void render_lay_status();
|
|
void lay_click();
|
|
void lay_finish(editor_track::snap_target const &End);
|
|
void lay_cancel();
|
|
void lay_enter();
|
|
std::vector<segment_data> lay_pieces(std::vector<glm::dvec3> const &Points, editor_track::snap_target const &End, double &Length, std::string &Error) const;
|
|
alignment::result lay_shape(std::vector<glm::dvec3> const &Points, glm::dvec2 const &Startdirection, glm::dvec2 const &Enddirection, alignment::design &Design) const;
|
|
void render_switch_ui();
|
|
bool start_switch_placement();
|
|
void finish_switch_placement();
|
|
std::vector<segment_data> switch_preview() const;
|
|
void insert_switch(editor_track::straight const &Line, double const Along, int const Direction, int const Side);
|
|
void switch_placed(std::vector<TTrack *> const &Created);
|
|
static std::vector<curve_sample> sample_chain(editor_track::chain const &Chain);
|
|
static curve_sample sample_at(std::vector<curve_sample> const &Frame, double const Station);
|
|
static double nearest_station(std::vector<curve_sample> const &Frame, glm::dvec3 const &Point);
|
|
static std::vector<segment_data> curved_switch_paths(editor_track::switch_template const &Shape, std::vector<curve_sample> const &Frame, double const Station, int const Direction, int const Side);
|
|
void insert_curved_switch(int const Direction, int const Side);
|
|
bool place_switch_on_straight(editor_track::straight const &Line, editor_track::switch_template const &Shape, TTrack const *Style, double const Along, int const Direction, int const Side, bool const Snap, std::vector<std::pair<TTrack *, editor_track::state>> &States, std::vector<TTrack *> &Created, std::vector<TTrack *> &Removed, std::string const &Name = {});
|
|
struct crossover_plan
|
|
{
|
|
editor_track::straight target;
|
|
editor_track::switch_template shape;
|
|
double along{0.0};
|
|
int direction{1};
|
|
int side{1};
|
|
segment_data insert;
|
|
std::vector<segment_data> paths;
|
|
};
|
|
bool plan_crossover(crossover_plan &Plan) const;
|
|
bool cut_straight(editor_track::straight const &Line, double const From, double const To, std::vector<std::pair<TTrack *, editor_track::state>> &States, std::vector<TTrack *> &Created, std::vector<TTrack *> &Removed, TTrack **Style);
|
|
bool insert_double_slip(editor_track::straight const &Line, glm::dvec3 const &Point, editor_track::switch_template const &Shape);
|
|
bool replace_double_slip(TTrack &Part, editor_track::switch_template const &Shape);
|
|
std::vector<TTrack *> build_double_slip(glm::dvec3 const &PointA, glm::dvec3 const &PointB, glm::dvec3 const &PointC, glm::dvec3 const &PointD, glm::dvec2 const &Crossing, double const Height, glm::dvec2 const &First, glm::dvec2 const &Second, double const Angle, double const Radius, TTrack &Style, std::string const &Type = "Rkpd");
|
|
bool start_extend(editor_track::snap_target const &End);
|
|
void finish_extend();
|
|
std::vector<segment_data> extend_pieces() const;
|
|
// the free end of the path as the start of a new track; false if it isn't free or has no direction
|
|
static bool free_end(TTrack &Track, int const Path, bool const Atend, extend_tool &Tool);
|
|
// the free end new pieces are added at: the selected point if it's one, else the end, else the start
|
|
bool chosen_free_end(extend_tool &Tool);
|
|
// adds a straight (Side 0) or a curve to the left (1) or the right (-1) at the free end of the selected track
|
|
void extend_add(int const Side);
|
|
void render_extend_ui();
|
|
enum class track_layout
|
|
{
|
|
arc_straight,
|
|
straight_arc,
|
|
arc,
|
|
straight
|
|
};
|
|
track_layout m_track_layout{track_layout::arc_straight};
|
|
double m_join_radius{0.0};
|
|
int m_join_variant{0};
|
|
TTrack const *m_join_track{nullptr};
|
|
editor_track::point_ref m_join_point;
|
|
mutable alignment::join_result m_join_last;
|
|
bool track_wheel(double const Offset);
|
|
std::string track_shaping(bool const Joined) const;
|
|
void render_layout_combo();
|
|
double design_speed(TTrack const *From, TTrack const *To) const;
|
|
std::vector<segment_data> layout_pieces(glm::dvec3 const &Point, glm::dvec2 const &Direction, double const Grade, glm::dvec3 const &Mouse, double const Radius) const;
|
|
std::vector<segment_data> join_pieces(TTrack const *From, editor_track::point_ref const &Frompoint, glm::dvec3 const &Start, glm::dvec2 const &Direction, editor_track::snap_target const &End, double &Length,
|
|
std::string &Error) const;
|
|
double m_extend_length{25.0};
|
|
double m_extend_radius{300.0};
|
|
bool extend_snap(editor_track::snap_target &Target) const;
|
|
void update_build_tools();
|
|
void draw_build_overlay() const;
|
|
// modes of the track editor, one at a time: path is the plain selection
|
|
enum class track_tab { straights, route, path, turnout, profile, speed, joints, infra, lay, lineside, turntable, signals };
|
|
struct track_mode
|
|
{
|
|
char const *label;
|
|
char const *key;
|
|
track_tab tab;
|
|
char const *tooltip;
|
|
};
|
|
static std::array<track_mode, 12> const &track_modes();
|
|
// the objects along the track: a tab for each tool
|
|
int m_lineside_tab{0};
|
|
void render_lineside_ui();
|
|
void arm_switch();
|
|
bool m_track_window_open{false};
|
|
// the inspector is pinned to the right edge of the screen, the profile strip to the bottom one, both resized from the inner edge
|
|
void render_track_inspector();
|
|
// the track tools in the tool options window, and the selected path in the inspector
|
|
void render_track_tool_options();
|
|
void render_track_selection();
|
|
void show_track_tab(track_tab const Tab);
|
|
bool track_analysis_tab() const { return m_track_tab == track_tab::profile || m_track_tab == track_tab::speed || m_track_tab == track_tab::joints || m_track_tab == track_tab::infra; }
|
|
bool track_shortcut(int const Key);
|
|
bool track_busy() const; // something going on in the mode, which Esc drops before it leaves the mode
|
|
std::string track_mode_name() const;
|
|
void render_turnout_ui();
|
|
struct track_name_edit
|
|
{
|
|
TTrack const *track{nullptr};
|
|
std::string synced;
|
|
char text[128]{};
|
|
editor_track::name_uses uses;
|
|
std::string status;
|
|
};
|
|
track_name_edit m_track_name;
|
|
void render_track_name(TTrack &Track);
|
|
void render_path_parameters(TTrack &Track);
|
|
void render_straight_ui();
|
|
track_tab m_track_tab{track_tab::path};
|
|
void delete_selected_track();
|
|
void start_straight_gesture(int const Tool);
|
|
void finish_straight_gesture();
|
|
void toggle_straight_set(TTrack &Track);
|
|
struct key_hint
|
|
{
|
|
char const *key;
|
|
std::string action;
|
|
};
|
|
std::vector<key_hint> track_key_hints(bool const All) const;
|
|
void render_track_guide();
|
|
std::string track_readout() const;
|
|
void draw_track_hints();
|
|
// path under the cursor, outlined before a click so it's known which one the click takes
|
|
struct track_hover
|
|
{
|
|
TTrack *track{nullptr};
|
|
int path{0};
|
|
glm::dvec3 point{0.0};
|
|
double since{0.0};
|
|
};
|
|
track_hover m_hover;
|
|
track_hover track_under_cursor() const;
|
|
void update_track_hover();
|
|
void draw_track_hover() const;
|
|
struct track_intent
|
|
{
|
|
enum class kind { none, gizmo, select, point, box, straight_set, straight_break, detour, straight_tool, straight_handle, route_vertex, route_grip, switch_at, lay, lay_end, sweep, turntable_place, turntable_exit, turntable_fit, spread, extend_end, signal_place, signal_move, switch_slide };
|
|
kind what{kind::none};
|
|
TTrack *track{nullptr};
|
|
int path{0};
|
|
editor_track::point_ref point;
|
|
int index{-1};
|
|
glm::dvec3 position{0.0};
|
|
editor_track::snap_target snap;
|
|
std::string action;
|
|
};
|
|
track_intent m_intent;
|
|
track_intent track_intent_at(int const Mods);
|
|
bool track_gesture_on() const;
|
|
void update_track_intent();
|
|
void track_press(track_intent const &Intent);
|
|
void turntable_press(track_intent const &Intent);
|
|
track_intent turntable_intent(glm::dvec3 const &Ground, bool const Shift);
|
|
void draw_track_intent() const;
|
|
editor_track::snap_target snap_free_end(glm::dvec3 const &Near, glm::vec2 const &Screen, TTrack const *Self, int const Category, std::vector<TTrack const *> const &Exclude) const;
|
|
void draw_free_ends(TTrack const *Self, int const Category, std::vector<TTrack const *> const &Exclude) const;
|
|
struct point_drag
|
|
{
|
|
bool active{false};
|
|
bool moved{false};
|
|
glm::vec2 from{0.f};
|
|
double height{0.0};
|
|
};
|
|
point_drag m_point_drag;
|
|
struct handle_drag
|
|
{
|
|
bool active{false};
|
|
bool moved{false};
|
|
glm::vec2 from{0.f};
|
|
double height{0.0};
|
|
glm::dvec3 offset{0.0};
|
|
glm::dvec3 position{0.0};
|
|
};
|
|
handle_drag m_handle_drag;
|
|
void handle_drag_start(glm::dvec3 const &Anchor);
|
|
bool handle_drag_step(glm::dvec3 &Position);
|
|
void point_drag_start(editor_track::point_ref const &Point);
|
|
void point_drag_update();
|
|
void point_drag_finish();
|
|
bool route_hit(int &Vertex, int &Grip) const;
|
|
int straight_handle_hit();
|
|
bool switch_reach(glm::dvec3 const &Ground, glm::dvec3 &Point);
|
|
glm::dvec3 cursor_ground() const;
|
|
glm::dvec3 cursor_level(double const Height) const;
|
|
glm::dvec3 cursor_terrain(double const Height);
|
|
double bed_height(glm::dvec3 const &Point, glm::dvec2 const &Direction, TTrack const *Style) const;
|
|
double nearby_ground(glm::dvec3 const &Point) const;
|
|
static std::pair<double, double> trackbed(TTrack const *Style);
|
|
std::vector<double> bed_heights(std::vector<glm::dvec3> const &Points, std::vector<glm::dvec2> const &Directions, TTrack const *Style) const;
|
|
void follow_ground(std::vector<segment_data> &Pieces, double const Height0, double const Grade0, double const Height1, double const Grade1, TTrack const *Style) const;
|
|
glm::dvec3 lay_bed(glm::dvec3 const &Point);
|
|
std::optional<glm::dvec3> m_cursor_override;
|
|
bool sweep_grabs(glm::dvec3 const &Point);
|
|
// menu of the path a click of the right mouse button lands on
|
|
struct track_context
|
|
{
|
|
track_hover target;
|
|
std::chrono::steady_clock::time_point pressed;
|
|
glm::vec3 angle{0.f};
|
|
glm::vec2 mouse{0.f};
|
|
bool armed{false};
|
|
bool pending{false};
|
|
};
|
|
track_context m_track_context;
|
|
void track_context_press();
|
|
void track_context_release();
|
|
void render_track_context();
|
|
void split_track_at(TTrack &Track, glm::dvec3 const &Point);
|
|
void focus_track(TTrack &Track, int const Path, glm::dvec3 const &Point);
|
|
// paths found by their names
|
|
struct track_search
|
|
{
|
|
char text[64]{};
|
|
std::string searched;
|
|
std::vector<TTrack *> found;
|
|
std::size_t total{0};
|
|
bool focus{false};
|
|
};
|
|
track_search m_track_search;
|
|
void render_track_search();
|
|
// paths selected together: Shift+LMB adds or takes one out, Shift+drag adds those in the box
|
|
std::vector<TTrack *> m_track_set;
|
|
struct track_box
|
|
{
|
|
bool active{false};
|
|
glm::vec2 from{0.f};
|
|
glm::vec2 to{0.f};
|
|
TTrack *hovered{nullptr};
|
|
};
|
|
track_box m_track_box;
|
|
struct track_set_fields
|
|
{
|
|
double velocity{100.0};
|
|
char rail[128]{};
|
|
char trackbed[128]{};
|
|
char circuits[256]{};
|
|
int environment{0};
|
|
};
|
|
track_set_fields m_track_set_fields;
|
|
glm::mat4 m_track_set_gizmo{1.0f};
|
|
bool m_track_set_using{false};
|
|
glm::dvec3 m_track_set_pivot{0.0};
|
|
std::vector<std::pair<TTrack *, std::vector<editor_track::joint>>> m_track_set_joints;
|
|
bool in_track_set(TTrack const *Track) const;
|
|
bool track_set_begin(bool const Rotate);
|
|
void track_set_turn(double const Angle);
|
|
void track_set_move(glm::dvec3 const &Offset);
|
|
void track_set_end();
|
|
glm::dvec3 track_set_centre() const;
|
|
void track_set_toggle(TTrack &Track);
|
|
void track_set_prune();
|
|
void start_track_box();
|
|
void finish_track_box();
|
|
void draw_track_set() const;
|
|
void render_track_set_ui();
|
|
void render_track_set_gizmo();
|
|
void track_set_delete();
|
|
struct track_spread
|
|
{
|
|
int stop{0};
|
|
bool same_rails{false};
|
|
bool same_bed{false};
|
|
bool same_speed{false};
|
|
float reach{0.f};
|
|
TTrack const *from{nullptr};
|
|
int signature{-1};
|
|
std::vector<TTrack *> preview;
|
|
double length{0.0};
|
|
};
|
|
mutable track_spread m_spread;
|
|
std::vector<TTrack *> track_spread_from(TTrack &Start) const;
|
|
std::vector<TTrack *> const &track_spread_preview(TTrack &Start) const;
|
|
void track_spread_apply(TTrack &Start);
|
|
void track_set_grow();
|
|
void render_track_spread_ui();
|
|
void draw_track_spread() const;
|
|
// changes each path of the set which can be changed; false from Change: the path stays as it was
|
|
void track_set_apply(std::function<bool(TTrack &)> const &Change, bool const Rebuild, char const *What);
|
|
// second track alongside the selected path or the line through it, at a spacing, curves included
|
|
struct parallel_tool
|
|
{
|
|
bool open{false};
|
|
bool expand{false};
|
|
double spacing{4.0};
|
|
int side{1}; // 1: to the right, -1: to the left, of the direction of the line
|
|
int scope{1}; // 0: the selected path, 1: the line through it, up to the switches
|
|
TTrack *built_for{nullptr};
|
|
double built_spacing{0.0};
|
|
int built_side{0};
|
|
int built_scope{-1};
|
|
std::size_t built_history{0};
|
|
TTrack *pair_src{nullptr};
|
|
TTrack *pair_dst{nullptr};
|
|
double measured{0.0};
|
|
std::vector<segment_data> pieces;
|
|
std::vector<TTrack *> styles;
|
|
double length{0.0};
|
|
std::string error;
|
|
std::string status;
|
|
};
|
|
parallel_tool m_parallel;
|
|
TTrack *parallel_source() const;
|
|
TTrack *parallel_target() const;
|
|
void parallel_update();
|
|
void render_parallel_controls(bool const Scope = true);
|
|
void render_parallel_ui();
|
|
void render_parallel_set_ui();
|
|
void parallel_build();
|
|
void parallel_apply();
|
|
bool parallel_preview_visible() const;
|
|
void draw_parallel_preview() const;
|
|
|
|
// scenery templates standing by the track, placed and checked by the editor
|
|
struct template_item
|
|
{
|
|
std::string file;
|
|
glm::dvec3 location{0.0}; // world space
|
|
double yaw{0.0};
|
|
std::vector<std::string> rest; // parameters after the rotation
|
|
bool described{false};
|
|
std::string track;
|
|
glm::dvec2 tilt{0.0}; // rot.x and rot.z of a described template, degrees
|
|
std::map<std::string, std::string> values;
|
|
};
|
|
struct standing_template
|
|
{
|
|
scene::instance_handle instance{0};
|
|
std::string file; // lower case, forward slashes
|
|
glm::dvec3 location{0.0};
|
|
double yaw{0.0};
|
|
std::vector<std::string> values;
|
|
};
|
|
// includes of the templates whose file names the test accepts, with the location in p2..p4 and the rotation in p5
|
|
std::vector<standing_template> standing_templates(std::function<bool(std::string const &)> const &Accept) const;
|
|
bool find_switch_drive(TTrack const &Switch, standing_template &Drive, int &Family, int &Index);
|
|
bool replace_switch_drive(TTrack &Switch, int Family, bool Flip);
|
|
bool switch_drive_spot(TTrack const &Switch, glm::dvec3 &Location, double &Yaw, glm::dvec2 &Tilt, bool &Rightswitch) const;
|
|
void switch_drive_captured(TTrack const &Track);
|
|
void switch_drives_committed(std::vector<TTrack *> const &Tracks);
|
|
// places them in the active layer, as one step of the undo. returns: how many were placed
|
|
std::size_t place_templates(std::vector<template_item> const &Items, std::string &Error);
|
|
// moves them, as one step of the undo
|
|
std::size_t move_templates(std::vector<std::pair<standing_template, template_item>> const &Moves);
|
|
// takes them out, as one step of the undo
|
|
std::size_t remove_templates(std::vector<scene::instance_handle> const &Instances);
|
|
|
|
// hectometre posts along the line, at every 100 m of the kilometrage
|
|
struct hekto_post
|
|
{
|
|
int hectometres{0};
|
|
double chainage{0.0};
|
|
glm::dvec3 position{0.0};
|
|
double yaw{0.0};
|
|
int standing{-1}; // post already there with this number
|
|
};
|
|
struct hekto_found
|
|
{
|
|
standing_template post;
|
|
int hectometres{0};
|
|
double chainage{0.0}; // where it stands along the line
|
|
double expected{0.0}; // where its number belongs
|
|
double error{0.0}; // m along the line
|
|
double distance{0.0}; // m from where it belongs, in the plan
|
|
bool wrong{false};
|
|
bool duplicate{false}; // another post with the same number stands nearer to its place
|
|
bool outside{false}; // its number belongs past the ends of the line
|
|
};
|
|
struct hekto_tool
|
|
{
|
|
bool open{false};
|
|
bool expand{false};
|
|
TTrack *track{nullptr}; // the line runs through it
|
|
glm::dvec3 point{0.0}; // where the kilometrage is given
|
|
double km{0.0};
|
|
bool km_read{false}; // the kilometrage was read from the posts already standing
|
|
int growth{1}; // 1: grows along the line, -1: against
|
|
int side{1}; // 1: to the right of the growing kilometrage, -1: to the left, 0: alternately
|
|
double offset{3.0}; // m from the axis
|
|
int style{0}; // nowy, stary, zgnity, mixed
|
|
int turn{0}; // degrees added to the rotation
|
|
double reach{10000.0}; // m each way
|
|
double tolerance{5.0}; // m along the line, for the posts standing
|
|
bool dirty{true};
|
|
std::size_t history{0};
|
|
editor_track::route route;
|
|
std::vector<editor_track::route_sample> samples;
|
|
double origin{0.0}; // chainage of the point
|
|
std::vector<hekto_post> posts;
|
|
std::vector<hekto_found> found;
|
|
std::string status;
|
|
std::string error;
|
|
};
|
|
hekto_tool m_hekto;
|
|
void hekto_start(TTrack &Track, glm::dvec3 const &Point);
|
|
void hekto_update();
|
|
void hekto_read_kilometrage();
|
|
void hekto_place();
|
|
void hekto_fix();
|
|
void hekto_remove_duplicates();
|
|
void render_hekto_ui();
|
|
void draw_hekto_overlay() const;
|
|
|
|
// fouling point markers (W17) where the tracks meeting at a switch are the spacing apart
|
|
struct fouling_point
|
|
{
|
|
TTrack *track{nullptr}; // the switch
|
|
glm::dvec3 position{0.0};
|
|
double yaw{0.0};
|
|
int standing{-1}; // marker already there
|
|
};
|
|
struct fouling_found
|
|
{
|
|
standing_template marker;
|
|
int point{-1}; // the fouling point it belongs to
|
|
double error{0.0}; // m from where it belongs
|
|
double height_error{0.0}; // m over where it belongs
|
|
bool wrong{false};
|
|
};
|
|
struct fouling_tool
|
|
{
|
|
bool open{false};
|
|
bool expand{false};
|
|
int scope{0}; // 0: the selected switch, 1: around the camera, 2: the whole scenery
|
|
double reach{1500.0};
|
|
double spacing{3.5}; // m between the axes
|
|
double tolerance{2.0}; // m, a marker farther than this from its point is wrong
|
|
int height_mode{1}; // 0: on the ground, 1: at the height given over the rail heads
|
|
double height{0.0}; // m over the rail heads
|
|
double height_tolerance{0.03}; // m, a marker standing higher or lower than this is wrong
|
|
int model{0}; // w17, w17_new, w17_old, w17_dwuteo
|
|
int turn{0};
|
|
bool dirty{true};
|
|
std::size_t history{0};
|
|
TTrack *selected{nullptr};
|
|
std::vector<fouling_point> points;
|
|
std::vector<fouling_found> found;
|
|
std::string status;
|
|
std::string error;
|
|
};
|
|
fouling_tool m_fouling;
|
|
void fouling_update();
|
|
void fouling_place();
|
|
void fouling_fix();
|
|
void render_fouling_ui();
|
|
void draw_fouling_overlay() const;
|
|
|
|
// a vehicle put on the selected path, to be driven: it isn't written to the scenery files
|
|
struct vehicle_tool
|
|
{
|
|
bool open{false};
|
|
bool expand{false};
|
|
std::shared_ptr<ui::vehicles_bank> bank;
|
|
char search[64]{};
|
|
bool controllable_only{true};
|
|
std::shared_ptr<ui::vehicle_desc> vehicle;
|
|
std::shared_ptr<ui::skin_set> skin;
|
|
int driver{0}; // 0: a driver, to be driven; 1: nobody, parked
|
|
TTrack *track{nullptr};
|
|
glm::dvec3 point{0.0};
|
|
std::string placed;
|
|
std::string status;
|
|
bool leave{false}; // the editor gives way to the driving at the start of the next frame
|
|
struct consist_vehicle
|
|
{
|
|
std::shared_ptr<ui::vehicle_desc> vehicle;
|
|
std::shared_ptr<ui::skin_set> skin;
|
|
bool turned{false};
|
|
};
|
|
std::vector<consist_vehicle> consist;
|
|
int facing{1};
|
|
};
|
|
vehicle_tool m_vehicle;
|
|
void vehicle_start(TTrack &Track, glm::dvec3 const &Point);
|
|
bool vehicle_place();
|
|
void render_vehicle_ui();
|
|
void draw_vehicle_marker() const;
|
|
|
|
struct signal_template
|
|
{
|
|
std::string file;
|
|
std::string name;
|
|
std::string description;
|
|
std::string kind;
|
|
std::string mount;
|
|
std::string lamps;
|
|
std::string lean;
|
|
std::string read;
|
|
int parameters{0};
|
|
bool plate{false};
|
|
bool linked{false};
|
|
};
|
|
struct signal_spot
|
|
{
|
|
TTrack *track{nullptr};
|
|
int path{0};
|
|
glm::dvec3 axis{0.0};
|
|
glm::dvec2 travel{0.0, 1.0};
|
|
int side{1};
|
|
int list{2};
|
|
double offset{0.0};
|
|
glm::dvec3 position{0.0};
|
|
double yaw{0.0};
|
|
};
|
|
struct signal_standing
|
|
{
|
|
standing_template include;
|
|
int kind{-1};
|
|
std::string name;
|
|
std::string read;
|
|
TTrack *track{nullptr};
|
|
int list{0};
|
|
signal_spot spot;
|
|
bool on_track{false};
|
|
};
|
|
struct signal_tool
|
|
{
|
|
std::vector<signal_template> templates;
|
|
int revision{-1};
|
|
int kind{0};
|
|
std::array<int, 7> chosen{-1, -1, -1, -1, -1, -1, -1};
|
|
char filter[64]{};
|
|
char name[64]{"A"};
|
|
char plate[64]{};
|
|
char linked[64]{"none"};
|
|
float height{0.0f};
|
|
bool placing{false};
|
|
signal_spot press;
|
|
glm::vec2 pressed{0.f};
|
|
int facing{0};
|
|
std::vector<signal_standing> standing;
|
|
std::size_t standing_history{static_cast<std::size_t>(-1)};
|
|
std::size_t standing_redo{static_cast<std::size_t>(-1)};
|
|
double standing_time{-1.0};
|
|
int hover{-1};
|
|
scene::instance_handle selected{0};
|
|
bool moving{false};
|
|
signal_spot moved;
|
|
bool moved_valid{false};
|
|
std::string status;
|
|
};
|
|
signal_tool m_signal;
|
|
void signal_scan();
|
|
signal_template const *signal_armed() const;
|
|
signal_template const *signal_template_of(std::string const &File) const;
|
|
bool signal_spot_at(TTrack &Track, int const Path, glm::dvec3 const &Point, glm::dvec3 const &Cursor, int const Facing, int const Side, signal_template const &Template, signal_spot &Spot) const;
|
|
void signal_refresh(bool const Force = false);
|
|
int signal_hit() const;
|
|
void signal_press(track_intent const &Intent);
|
|
void signal_release();
|
|
bool signal_place(signal_spot const &Spot, signal_template const &Template);
|
|
void signal_move_update();
|
|
void signal_move_finish();
|
|
bool signal_delete(scene::instance_handle const Instance);
|
|
void signal_detach_events(std::string const &Read, std::vector<std::pair<TTrack *, editor_track::state>> &States);
|
|
void signal_renamed(std::string const &Before, std::string const &After, EditorSnapshot &Snapshot);
|
|
void signal_select(scene::instance_handle const Instance);
|
|
std::string signal_next_name(std::string const &Name) const;
|
|
void render_signal_ui();
|
|
void render_signal_palette();
|
|
void render_signal_selected();
|
|
void draw_signal_overlay() const;
|
|
std::string signal_intent_label(signal_template const &Template, signal_spot const &Spot) const;
|
|
std::string signal_variant(signal_template const &Template, signal_spot const &Spot) const;
|
|
|
|
// a model laid along the track: bent to follow it, or repeated along it
|
|
struct sweep_tool
|
|
{
|
|
bool open{false};
|
|
bool expand{false};
|
|
char model[256]{};
|
|
char skin[128]{"none"};
|
|
char search[64]{};
|
|
std::vector<std::string> models;
|
|
bool scanned{false};
|
|
// parameters of a template laid along the track, with their names
|
|
std::string parameters_for;
|
|
std::vector<std::array<char, 128>> parameters;
|
|
std::vector<std::string> parameter_labels;
|
|
std::vector<bool> parameter_placement; // filled in with zeros: the template is placed in its own space
|
|
std::vector<std::vector<std::string>> parameter_choices; // variants of the textures a parameter is a part of the name of
|
|
int length_mode{2}; // 0: one copy as long as the original, 1: the length given, 2: the whole stretch of the line
|
|
double length{200.0};
|
|
sweep_node::state settings;
|
|
int axis{0}; // 0: the longer one of the model, 1: z, 2: x
|
|
double model_length{0.0};
|
|
std::string measured; // model the length is of
|
|
// the curve: the axis of the track
|
|
TTrack *curve_for{nullptr};
|
|
std::size_t curve_history{0};
|
|
struct piece
|
|
{
|
|
TTrack *track{nullptr};
|
|
int path{0};
|
|
bool forward{true};
|
|
};
|
|
std::vector<piece> curve_pieces;
|
|
std::vector<segment_data> curve;
|
|
double curve_length{0.0};
|
|
std::vector<glm::dvec3> outline; // of the curve, for the preview
|
|
std::vector<double> stations;
|
|
// marking the stretch in the view: press where it starts, the distance from the track sets the offset, drag to the end
|
|
bool marking{false};
|
|
bool dragging{false};
|
|
double anchor{0.0}; // station the drag started at
|
|
sweep_node *edited{nullptr};
|
|
std::string status;
|
|
};
|
|
sweep_tool m_sweep;
|
|
// paths the models along curves were laid on, so they follow the changes of the paths
|
|
struct sweep_link
|
|
{
|
|
sweep_node *sweep{nullptr};
|
|
std::size_t piece{0};
|
|
TTrack *track{nullptr};
|
|
int path{0};
|
|
bool reversed{false};
|
|
};
|
|
std::vector<sweep_link> m_sweep_links;
|
|
void sweep_curve();
|
|
void sweep_outline();
|
|
bool sweep_extend(bool const Atend, glm::dvec3 const &Cursor);
|
|
double sweep_station(glm::dvec3 const &Point, double &Lateral) const;
|
|
void sweep_scan_models();
|
|
void sweep_measure();
|
|
sweep_node::state sweep_definition() const;
|
|
void sweep_create();
|
|
void sweep_apply();
|
|
void sweep_delete(sweep_node &Sweep);
|
|
void sweep_edit(sweep_node &Sweep);
|
|
std::vector<sweep_node *> sweeps_near(TTrack const &Track) const;
|
|
bool sweep_press();
|
|
void sweep_drag();
|
|
void sweep_release();
|
|
// the same at a point of the world: Anywhere takes the press however far from the track it is
|
|
bool sweep_press_at(glm::dvec3 const &Point, bool const Anywhere);
|
|
void sweep_drag_at(glm::dvec3 const &Point);
|
|
void render_sweep_ui();
|
|
void draw_sweep_overlay() const;
|
|
void restore_sweeps(EditorSnapshot &Snapshot);
|
|
void sweeps_captured(TTrack const &Track);
|
|
void sweeps_committed(std::vector<TTrack *> const &Tracks);
|
|
void sweeps_split(TTrack &Original, TTrack &Created);
|
|
|
|
struct bend_source
|
|
{
|
|
TAnimModel *model{nullptr};
|
|
scene::instance_handle instance{0};
|
|
std::string file;
|
|
std::string skin{"none"};
|
|
std::vector<std::string> parameters;
|
|
glm::dmat4 transform{1.0};
|
|
glm::dvec3 scale{1.0};
|
|
scene::layer_handle layer{null_handle};
|
|
};
|
|
struct bend_tool
|
|
{
|
|
int axis{0};
|
|
bool turned{false};
|
|
int along_anchor{1};
|
|
int side_anchor{0};
|
|
int height_anchor{0};
|
|
glm::dvec3 point{0.0};
|
|
bool picking{false};
|
|
void const *tracks_for{nullptr};
|
|
glm::dvec3 tracks_at{0.0};
|
|
std::vector<std::pair<TTrack *, double>> tracks;
|
|
int track{0};
|
|
sweep_node *edited{nullptr};
|
|
std::string signature;
|
|
bool marker_valid{false};
|
|
glm::dvec3 marker{0.0};
|
|
std::string status;
|
|
int dragging{0};
|
|
sweep_node::state drag_state;
|
|
glm::dvec3 drag_anchor{0.0};
|
|
double drag_station{0.0};
|
|
std::string drag_label;
|
|
sweep_node *hover{nullptr};
|
|
bool over_marker{false};
|
|
std::string label;
|
|
std::string details;
|
|
};
|
|
bend_tool m_bend;
|
|
bool bend_drag_start(int const Mods);
|
|
void bend_drag_update();
|
|
sweep_node *sweep_under(glm::dvec3 const &Point) const;
|
|
bool bend_click();
|
|
bool bend_shortcut();
|
|
bool bend_source_of_selection(bend_source &Source) const;
|
|
void bend_find_tracks(glm::dvec3 const &Point, void const *Key);
|
|
bool bend_definition(bend_source const &Source, TTrack &Track, sweep_node::state &State, std::vector<TTrack *> &Tracks);
|
|
void bend_selection();
|
|
void bend_edit(sweep_node::state const &State);
|
|
void bend_reanchor(sweep_node::state &State, int const Side, int const Height, glm::dvec3 const &Point) const;
|
|
double bend_fixed(sweep_node const &Sweep, glm::dvec3 const &Point) const;
|
|
void bend_stretch(sweep_node const &Sweep, double const Fixed, double const Length, glm::dvec3 const &Point, sweep_node::state &State) const;
|
|
void bend_pick(glm::dvec3 const &Point);
|
|
glm::dvec3 bend_marker(sweep_node const &Sweep) const;
|
|
void restore_bend(EditorSnapshot &Snapshot, bool const Undo);
|
|
void render_bend();
|
|
void draw_bend_overlay() const;
|
|
|
|
// script of editor actions given in the EU07_EDITOR_SELFTEST file, run a step at a time, for testing without a person
|
|
struct selftest
|
|
{
|
|
bool loaded{false};
|
|
std::vector<std::string> lines;
|
|
std::size_t next{0};
|
|
int wait{0};
|
|
bool quitting{false};
|
|
};
|
|
selftest m_selftest;
|
|
void selftest_step();
|
|
// a value typed during a gesture, taken instead of the one the cursor gives
|
|
std::string m_typed;
|
|
// what the typed value sets in the gesture under way, nullptr when the gesture takes none
|
|
char const *typed_meaning() const;
|
|
std::vector<double> typed_values() const;
|
|
bool typed_key(int const Key);
|
|
void typed_extend();
|
|
glm::dvec3 typed_lay(glm::dvec3 const &Mouse) const;
|
|
void typed_turn();
|
|
bool m_route_tab{true};
|
|
|
|
// vertical profile (grade line) along a route
|
|
struct profile_state
|
|
{
|
|
bool open{false};
|
|
TTrack *from{nullptr};
|
|
TTrack *to{nullptr};
|
|
TTrack *picked_from{nullptr};
|
|
TTrack *picked_to{nullptr};
|
|
double run_length{3000.0};
|
|
editor_track::route route;
|
|
std::vector<editor_track::route_sample> samples;
|
|
std::vector<double> terrain; // NaN where there's no terrain
|
|
profile::line line;
|
|
profile::context context;
|
|
std::vector<profile::issue> issues;
|
|
double origin{0.0}; // chainage of the start of the route
|
|
double departure{0.0}; // largest difference between the grade line and the track
|
|
profile::ground_fit fit;
|
|
// the heightmap terrain led to the grade line by a modifier: formation under the track, slopes down or up to the ground
|
|
struct earthworks
|
|
{
|
|
bool from_ballast{true};
|
|
double depth{0.7}; // m, formation below the top of the rail
|
|
double half_width{3.5}; // m, from the axis of the route to the edge of the formation
|
|
double slope{1.5}; // horizontal run of the slopes per metre of height
|
|
double reach{60.0}; // m, from the edge of the formation, where the slopes end even if they don't get to the ground
|
|
double rounding{1.5}; // m, over which the slopes bend into the formation and into the ground
|
|
bool generate{true};
|
|
// chunks of terrain made under the route by the last shaping
|
|
std::vector<std::pair<int, int>> generated;
|
|
// the modifier of the terrain made by the last shaping
|
|
std::string modifier;
|
|
} earthworks;
|
|
double view_from{0.0};
|
|
double view_to{100.0};
|
|
double view_centre{0.0};
|
|
float exaggeration{20.0f};
|
|
bool show_terrain{true};
|
|
bool show_track{true};
|
|
bool show_plan{true};
|
|
int selected{-1};
|
|
int dragging{-1};
|
|
int curve_grip{-1};
|
|
bool panning{false};
|
|
bool changed{false};
|
|
double hover{-1.0};
|
|
// under the cursor in the strip
|
|
int hover_point{-1};
|
|
int hover_grip{-1};
|
|
bool hover_line{false};
|
|
int shown{-2}; // point in the side panel
|
|
std::string status;
|
|
std::string error;
|
|
};
|
|
profile_state m_profile;
|
|
// grade lines designed in the editor, kept in the scenery files as "//$p" comment lines
|
|
struct stored_profile
|
|
{
|
|
scene::layer_handle layer{null_handle};
|
|
double length{0.0};
|
|
glm::dvec3 start{0.0}, end{0.0}, middle{0.0}; // of the route, world space
|
|
double origin{0.0};
|
|
profile::line line;
|
|
};
|
|
std::vector<stored_profile> m_profiles;
|
|
bool m_profiles_loaded{false};
|
|
void profile_library_load();
|
|
void profile_library_mark(std::vector<scene::layer_handle> Layers);
|
|
int profile_find(bool &Reversed) const;
|
|
bool profile_restore();
|
|
void profile_store();
|
|
void profile_forget();
|
|
void profile_open_stored(std::size_t const Index);
|
|
void profile_edited();
|
|
void render_profile_body();
|
|
void render_profile_strip();
|
|
void render_profile_toolbar();
|
|
void render_profile_points();
|
|
void render_profile_source();
|
|
bool render_profile_parameters();
|
|
void render_profile_issues();
|
|
void profile_open(TTrack *From, TTrack *To);
|
|
void profile_open_run(TTrack &Track);
|
|
void profile_take(editor_track::route Route, TTrack *From, TTrack *To);
|
|
void profile_resample();
|
|
void profile_recognize();
|
|
void profile_fit_ground();
|
|
static std::pair<double, double> profile_ballast(TTrack const &Track);
|
|
void profile_shape_ground();
|
|
std::pair<int, int> profile_generate_ground(std::vector<std::pair<double, double>> const &Beds, double const Reach);
|
|
void profile_restore_ground();
|
|
void render_profile_earthworks();
|
|
void profile_check();
|
|
void profile_apply();
|
|
void profile_fit_view();
|
|
void profile_after_undo();
|
|
struct profile_view;
|
|
void render_profile_canvas(float const Height);
|
|
void profile_fit_exaggeration(profile_view const &View);
|
|
void profile_navigate(profile_view const &View, bool const Hovered);
|
|
bool profile_canvas_edit(profile_view const &View, bool const Hovered);
|
|
void draw_profile_canvas(profile_view const &View, ImDrawList &Draw) const;
|
|
void profile_canvas_tooltip() const;
|
|
void render_profile_point();
|
|
void draw_profile_overlay() const;
|
|
|
|
// infrastructure along the track: objects bound to the paths follow their changes. bindings are kept in the
|
|
// scenery files as "//$b" comment lines
|
|
struct infra_candidate
|
|
{
|
|
infra::binding binding;
|
|
double offset{0.0}; // sideways from the axis
|
|
std::string reason;
|
|
bool chosen{false};
|
|
bool bound{false};
|
|
TTrack *reader{nullptr};
|
|
std::string event; // of that path
|
|
std::string cell;
|
|
bool loose{false};
|
|
TTrack *nearer{nullptr};
|
|
glm::dvec3 nearer_foot{0.0};
|
|
bool unread{false};
|
|
};
|
|
struct infra_window
|
|
{
|
|
bool open{false};
|
|
int scope{1}; // 0: the selected path, 1: the line through it, 2: the route of the vertical profile
|
|
float reach{1500.0f};
|
|
float corridor{8.0f};
|
|
bool follow{true};
|
|
bool show{true};
|
|
std::vector<infra::rule> rules{infra::default_rules()};
|
|
std::vector<TTrack *> tracks; // of the scope
|
|
std::vector<infra_candidate> candidates;
|
|
int filter{0};
|
|
int read{0};
|
|
int overruled{0};
|
|
int unread{0};
|
|
int elsewhere{0};
|
|
int hovered{-1};
|
|
std::string status;
|
|
std::string error;
|
|
};
|
|
infra_window m_infra;
|
|
std::vector<infra::binding> m_bindings;
|
|
std::vector<std::pair<scene::layer_handle, std::string>> m_bindings_unresolved; // lines kept as they are
|
|
bool m_bindings_loaded{false};
|
|
std::vector<infra::object_state> m_infra_buffer; // states of the objects before the change in progress
|
|
std::vector<std::pair<scene::instance_handle, std::string>> m_drive_buffer;
|
|
bool m_infra_suspended{false};
|
|
void infra_load();
|
|
void infra_store();
|
|
std::vector<TTrack *> infra_scope() const;
|
|
void infra_recognize();
|
|
void infra_bind_chosen();
|
|
void infra_unbind(std::vector<std::size_t> Indices);
|
|
infra::binding *infra_find(infra::binding const &Binding);
|
|
infra::object_state infra_state_of(infra::binding const &Binding) const;
|
|
void infra_restore_state(infra::object_state const &State);
|
|
void infra_buffer(infra::binding const &Binding, bool const Refresh);
|
|
// editor_track::observer
|
|
void track_captured(TTrack const &Track) override;
|
|
void tracks_committed(std::vector<TTrack *> const &Tracks) override;
|
|
void track_retired(TTrack &Track) override;
|
|
void track_split(TTrack &Original, TTrack &Created, segment_data const &First) override;
|
|
void infra_move(infra::binding &Binding);
|
|
void infra_attach(EditorSnapshot &Snapshot);
|
|
void render_infra_body();
|
|
|
|
// speed limits of the paths checked against what their geometry allows
|
|
struct speed_state
|
|
{
|
|
bool open{false};
|
|
int scope{0}; // 0: the line through the selected path, 1: the whole scenery
|
|
speed_check::options options;
|
|
std::vector<std::pair<TTrack *, int>> queue;
|
|
std::size_t next{0};
|
|
std::vector<speed_check::verdict> results;
|
|
bool show_all{false};
|
|
int limits{0}; // 0: all the paths, 1: only those with a speed limit set, 2: only those without
|
|
int selected{-1};
|
|
std::size_t history{0}; // size of the undo history at the check
|
|
std::string status;
|
|
};
|
|
speed_state m_speed;
|
|
void render_speed_body();
|
|
void speed_start();
|
|
void speed_step();
|
|
void speed_focus(int const Index);
|
|
// with Branches the limits of the switch branches count too, otherwise they're only warnings
|
|
void speed_apply(std::vector<int> const &Indices, bool const Branches);
|
|
std::vector<int> speed_listed() const;
|
|
void draw_speed_overlay() const;
|
|
|
|
// joints of the paths: ends which almost meet, steps, kinks, jumps of the cant and of the grade
|
|
struct joints_state
|
|
{
|
|
bool open{false};
|
|
int scope{0}; // 0: around the camera, 1: the whole scenery
|
|
double reach{2000.0};
|
|
double gap{1.0}; // m, free ends closer than this to another end
|
|
double step{0.005}; // m
|
|
double angle{0.2}; // deg
|
|
double cant{5.0}; // mm
|
|
double grade{10.0}; // per mille
|
|
bool free_ends{false};
|
|
std::vector<TTrack *> queue;
|
|
std::size_t next{0};
|
|
std::vector<joint_issue> issues;
|
|
int filter{-1};
|
|
int selected{-1};
|
|
std::size_t history{0};
|
|
std::string status;
|
|
};
|
|
joints_state m_joints;
|
|
void joints_start();
|
|
void joints_step();
|
|
void joints_check(TTrack &Track);
|
|
void joints_focus(int const Index);
|
|
bool joint_fix(joint_issue &Issue, std::vector<std::pair<TTrack *, editor_track::state>> &States, std::vector<TTrack *> &Changed);
|
|
void joints_fix(std::vector<int> const &Indices);
|
|
void render_joints_body();
|
|
void draw_joints_overlay() const;
|
|
void infra_rebase();
|
|
void render_infra_search();
|
|
void render_infra_candidates();
|
|
void render_infra_bound();
|
|
void draw_infra_overlay() const;
|
|
|
|
bool m_route_gizmo_using{false};
|
|
glm::mat4 m_route_gizmo{1.0f};
|
|
float m_track_snap_radius{1.0f};
|
|
bool m_track_align_tangent{true};
|
|
bool m_track_drag_connected{true};
|
|
|
|
// roads: a menu and a window of their own, independent from the edit modes of the toolset
|
|
// what a click of the build tool would make
|
|
struct road_plan
|
|
{
|
|
std::vector<segment_data> pieces; // axes of the pieces, in the order they're laid
|
|
editor_road::loose_end snap; // loose end of a road or arm of a junction the road is led to
|
|
editor_road::branch start; // junction made, or given another arm, for a road led out of the side of a road or out of a junction
|
|
bool hasstart{false};
|
|
editor_road::branch end; // the same for a road led into the side of a road or into a junction
|
|
bool hasend{false};
|
|
std::string error; // why there's nothing to make
|
|
};
|
|
struct road_tool
|
|
{
|
|
bool window{false}; // the road window is open, the left mouse button belongs to its tools
|
|
bool lanes{true}; // lanes of the roads are drawn as trajectories
|
|
int tool{0}; // 0: select, 1: build, 2: place
|
|
road_node *selected{nullptr};
|
|
junction_node *junction{nullptr}; // selected junction
|
|
roadpoint_node *marker{nullptr}; // selected level crossing or traffic point
|
|
std::vector<glm::dvec3> points; // selected points where the pieces meet or end
|
|
road_node::state settings; // layout shown in the window: of the selected piece, or of the pieces about to be built
|
|
bool whole{true}; // layout changes go to every piece of the road the selected one belongs to
|
|
bool varied{false}; // the selected piece is shown with lanes of its own for its end
|
|
bool chain{false}; // the point to build from is set
|
|
glm::dvec3 point{0.0}; // where the next piece starts
|
|
glm::dvec2 direction{0.0, 1.0}; // the way it has to leave that point
|
|
bool hasdirection{false};
|
|
bool reversed{false}; // the pieces are laid from their ends, to carry on a road from its start
|
|
double grade{0.0}; // slope the next piece has to start with, to carry on what it's attached to
|
|
bool hasgrade{false};
|
|
// the road being built leaves the side of a road, or a junction between its roads. the point to build from is the centre
|
|
// of the junction, which gets made, or gets another arm, along with the first piece
|
|
road_node *branchroad{nullptr};
|
|
double branchat{0.0}; // value of the curve parameter of the axis of that road
|
|
junction_node *branchjunction{nullptr};
|
|
float offset{0.1f}; // height above the ground the roads are built at
|
|
bool follow{true}; // built roads are led over the ground between the clicked points
|
|
float crossingspeed{30.f}; // speed limit on the junctions being made
|
|
float heightstep{0.1f}; // how much the selected points are raised or lowered at a time
|
|
bool ring{false}; // the build tool makes roundabouts
|
|
float ringradius{15.f}; // of the middle of the road going around
|
|
std::string island{"none"}; // what the middle of a roundabout is covered with
|
|
float bankgrade{1.5f}; // metres a bank led to the ground runs for each metre it drops
|
|
// level crossings and traffic points
|
|
int placekind{0}; // what the place tool puts down: 0: level crossing, 1: spawn point, 2: removal point, 3: pedestrian crossing
|
|
roadpoint_node::state placing[4]; // what the next point of each kind is going to be like
|
|
float stopmargin{5.f}; // distance from the outermost track of a new crossing to the places the vehicles stop at
|
|
roadpoint_node::state target; // the point a click of the place tool would make at the moment
|
|
bool hastarget{false};
|
|
glm::dvec3 targetdirection{0.0}; // the way the traffic goes there
|
|
std::string targetnote; // what's worth knowing about that point, or why there's none
|
|
glm::dvec3 aimed{0.0}; // where the cursor was when the target was worked out, and for what kind of point
|
|
int aimedkind{-1};
|
|
char vehicles[4096]{}; // vehicles to add to the set of a spawn point, written the way they're put in a scenery
|
|
road_plan plan; // what a click would make at the moment, worked out once a frame for display
|
|
glm::dvec3 hover{0.0}; // point of a road or a junction under the cursor a road can be led out of
|
|
bool hashover{false};
|
|
glm::dvec3 mouse{0.0};
|
|
std::string status;
|
|
char surface[128]{};
|
|
char sides[2][128]{};
|
|
char filter[64]{"asph"};
|
|
char kerbtext[128]{};
|
|
char mediantext[128]{};
|
|
char banktext[128]{};
|
|
char islandtext[128]{"none"};
|
|
};
|
|
void render_road_tool_options();
|
|
// the change of the road tool
|
|
void road_choose_tool(int const Tool);
|
|
void render_road_selection();
|
|
bool render_road_layout(road_node::state &State);
|
|
bool render_road_material(char const *Label, char *Buffer, std::size_t const Size, std::string &Value);
|
|
void render_junction_layout(junction_node &Junction);
|
|
void junction_apply(junction_node &Junction, junction_node::state const &State);
|
|
// leads the banks of the corners of a junction to the ground beside it
|
|
void junction_bank_to_ground(junction_node &Junction);
|
|
// how far from specified point, and how far down, a slope of the grade set for the banks gets to the ground.
|
|
// Outwards: the way the slope leads, seen from above
|
|
std::pair<float, float> bank_reach(glm::dvec3 const &Edge, glm::dvec3 const &Outwards);
|
|
void draw_road_overlay() const;
|
|
void update_road_tool();
|
|
void road_click();
|
|
void road_select(road_node *Road);
|
|
void junction_select(junction_node *Junction);
|
|
void road_apply();
|
|
bool road_delete();
|
|
bool road_split();
|
|
void road_cancel();
|
|
// sets the width of the road at the selected points, or the height of each of them
|
|
void road_point_width(float const Width);
|
|
void road_point_heights(std::vector<double> const &Heights);
|
|
bool road_points_apply(std::vector<std::pair<road_node *, road_node::state>> const &Changes, std::vector<std::pair<junction_node *, junction_node::state>> const &Junctions);
|
|
// takes the selected points out of their roads
|
|
bool road_points_delete();
|
|
// puts a roundabout around specified point
|
|
void road_roundabout(glm::dvec3 const &Ground);
|
|
// leads the banks of the selected piece, or of the whole road, to the ground beside it
|
|
void road_bank_to_ground();
|
|
// level crossings and traffic points
|
|
void roadpoint_select(roadpoint_node *Point);
|
|
// works out the point a click of the place tool would make. Fresh: even if the cursor is where it was the last time
|
|
void roadpoint_aim(bool const Fresh);
|
|
void roadpoint_place();
|
|
void roadpoint_apply(roadpoint_node &Point, roadpoint_node::state const &State);
|
|
// Point: the point the controls are for, nullptr for the one about to be made
|
|
bool render_roadpoint_layout(roadpoint_node::state &State, roadpoint_node const *Point);
|
|
// Fresh: the ground which wasn't looked up yet is gathered on the spot, instead of a bit of it each frame
|
|
road_plan road_preview(bool const Fresh);
|
|
// Firstgrade, Lastgrade: slopes the road has to start and end with, nullptr for none
|
|
void road_profile(std::vector<segment_data> &Pieces, double const *Firstgrade, double const *Lastgrade, bool const Fresh);
|
|
// height of the ground under each of the points, with the roads not taken for the ground. a point with nothing under it keeps its height
|
|
// Found: set for the points which have the ground under them
|
|
std::vector<double> ground_heights(std::vector<glm::dvec3> const &Points, bool const Fresh, std::vector<char> *Found = nullptr) const;
|
|
void push_road_snapshot(editor_road::record Record);
|
|
void restore_road_snapshot(EditorSnapshot const &Snapshot, std::vector<EditorSnapshot> &Opposite, bool const Undo);
|
|
road_tool m_roadtool;
|
|
};
|