mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-07-17 23:39:18 +02:00
499 lines
20 KiB
C++
499 lines
20 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 <vector>
|
||
#include <deque>
|
||
#include <array>
|
||
#include <stack>
|
||
#include <unordered_set>
|
||
#include <unordered_map>
|
||
#include <map>
|
||
|
||
#include "utilities/parser.h"
|
||
#include "rendering/geometrybank.h"
|
||
#include "scene/scenenode.h"
|
||
#include "world/Track.h"
|
||
#include "world/Traction.h"
|
||
#include "audio/sound.h"
|
||
#include "input/command.h"
|
||
#include "utilities/utilities.h"
|
||
|
||
class opengl_renderer;
|
||
class opengl33_renderer;
|
||
|
||
namespace scene {
|
||
|
||
int constexpr EU07_CELLSIZE = 250;
|
||
int constexpr EU07_SECTIONSIZE = 1000;
|
||
int constexpr EU07_REGIONSIDESECTIONCOUNT = 500; // number of sections along a side of square region
|
||
|
||
struct scratch_data {
|
||
|
||
struct binary_data {
|
||
|
||
bool terrain{ false };
|
||
bool terrain_included{false};
|
||
} binary;
|
||
|
||
struct location_data {
|
||
|
||
std::stack<glm::dvec3> offset;
|
||
// per-axis scale stack — mirrors `offset` for the `scale`/`endscale`
|
||
// scenario directives. Effective scale at any nesting depth is the
|
||
// component-wise product of all stack entries (outer `scale 2 2 2` ×
|
||
// inner `scale 1.5 1.5 1.5` yields (3,3,3)). Empty stack means (1,1,1).
|
||
std::stack<glm::vec3> scale;
|
||
glm::vec3 rotation;
|
||
} location;
|
||
|
||
struct trainset_data {
|
||
|
||
std::string name;
|
||
std::string track;
|
||
float offset { 0.f };
|
||
float velocity { 0.f };
|
||
std::vector<TDynamicObject *> vehicles;
|
||
std::vector<int> couplings;
|
||
TDynamicObject * driver { nullptr };
|
||
bool is_open { false };
|
||
std::unordered_map<std::string, std::string> assignment;
|
||
} trainset;
|
||
|
||
std::string name;
|
||
std::string terrain_name;
|
||
|
||
bool initialized { false };
|
||
bool time_initialized { false };
|
||
};
|
||
|
||
// basic element of rudimentary partitioning scheme for the section. fixed size, no further subdivision
|
||
// TBD, TODO: replace with quadtree scheme?
|
||
class basic_cell {
|
||
|
||
friend opengl_renderer;
|
||
friend opengl33_renderer;
|
||
|
||
public:
|
||
// constructors
|
||
basic_cell() = default;
|
||
// methods
|
||
// potentially activates event handler with the same name as provided node, and within handler activation range
|
||
void
|
||
on_click( TAnimModel const *Instance );
|
||
// legacy method, finds and assigns traction piece to specified pantograph of provided vehicle
|
||
void
|
||
update_traction( TDynamicObject *Vehicle, int const Pantographindex );
|
||
// legacy method, polls event launchers within radius around specified point
|
||
void
|
||
update_events();
|
||
// legacy method, updates sounds within radius around specified point
|
||
void
|
||
update_sounds();
|
||
// legacy method, triggers radio-stop procedure for all vehicles located on paths in the cell
|
||
void
|
||
radio_stop();
|
||
// legacy method, adds specified path to the list of pieces undergoing state change
|
||
bool
|
||
RaTrackAnimAdd( TTrack *Track );
|
||
// legacy method, updates geometry for pieces in the animation list
|
||
void
|
||
RaAnimate( unsigned int const Framestamp );
|
||
// sends content of the class to provided stream
|
||
void
|
||
serialize( std::ostream &Output ) const;
|
||
// restores content of the class from provided stream
|
||
void
|
||
deserialize( std::istream &Input );
|
||
// sends content of the class in legacy (text) format to provided stream
|
||
void
|
||
export_as_text( std::ostream &Output ) const;
|
||
// adds provided shape to the cell
|
||
void
|
||
insert( shape_node Shape );
|
||
// adds provided lines to the cell
|
||
void
|
||
insert( lines_node Lines );
|
||
// adds provided path to the cell
|
||
void
|
||
insert( TTrack *Path );
|
||
// adds provided path to the cell
|
||
void
|
||
insert( TTraction *Traction );
|
||
// adds provided model instance to the cell
|
||
void
|
||
insert( TAnimModel *Instance );
|
||
// adds provided sound instance to the cell
|
||
void
|
||
insert( sound_source *Sound );
|
||
// adds provided event launcher to the cell
|
||
void
|
||
insert( TEventLauncher *Launcher );
|
||
// adds provided memory cell to the cell
|
||
void
|
||
insert( TMemCell *Memorycell );
|
||
// registers provided path in the lookup directory of the cell
|
||
void
|
||
register_end( TTrack *Path );
|
||
// registers provided traction piece in the lookup directory of the cell
|
||
void
|
||
register_end( TTraction *Traction );
|
||
// removes provided model instance from the cell
|
||
void
|
||
erase( TAnimModel *Instance );
|
||
// removes provided memory cell from the cell
|
||
void
|
||
erase( TMemCell *Memorycell );
|
||
// find a vehicle located nearest to specified point, within specified radius. reurns: located vehicle and distance
|
||
std::tuple<TDynamicObject *, float>
|
||
find( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled, bool const Findbycoupler ) const;
|
||
// finds a path with one of its ends located in specified point. returns: located path and id of the matching endpoint
|
||
std::tuple<TTrack *, int>
|
||
find( glm::dvec3 const &Point, TTrack const *Exclude ) const;
|
||
// finds a traction piece with one of its ends located in specified point. returns: located traction piece and id of the matching endpoint
|
||
std::tuple<TTraction *, int>
|
||
find( glm::dvec3 const &Point, TTraction const *Exclude ) const;
|
||
// finds a traction piece located nearest to specified point, sharing section with specified other piece and powered in specified direction. returns: located traction piece
|
||
std::tuple<TTraction *, int, float>
|
||
find( glm::dvec3 const &Point, TTraction const *Other, int const Currentdirection ) const;
|
||
// sets center point of the cell
|
||
void
|
||
center( glm::dvec3 Center );
|
||
// generates renderable version of held non-instanced geometry in specified geometry bank
|
||
void
|
||
create_geometry( gfx::geometrybank_handle const &Bank );
|
||
void
|
||
create_map_geometry(std::vector<gfx::basic_vertex> &Bank, const gfx::geometrybank_handle Extra);
|
||
void
|
||
get_map_active_paths(map_colored_paths &handles);
|
||
glm::vec3 find_nearest_track_point(const glm::dvec3 &pos);
|
||
// provides access to bounding area data
|
||
bounding_area const &
|
||
area() const {
|
||
return m_area; }
|
||
//private:
|
||
// types
|
||
using path_sequence = std::vector<TTrack *>;
|
||
using shapenode_sequence = std::vector<shape_node>;
|
||
using linesnode_sequence = std::vector<lines_node>;
|
||
using traction_sequence = std::vector<TTraction *>;
|
||
using instance_sequence = std::vector<TAnimModel *>;
|
||
// Composite key: instances of the same TModel3d sharing the same replacable
|
||
// skin set can be GPU-batched together, but instances with different skins
|
||
// must go to different buckets so each batched draw call uses one consistent
|
||
// material binding. Sub-bucketing here keeps Render_Instanced correct without
|
||
// forcing per-instance material switching inside a single batch.
|
||
struct instance_bucket_key {
|
||
TModel3d *pModel { nullptr };
|
||
std::array<material_handle, 5> skins {};
|
||
|
||
bool operator==( instance_bucket_key const &other ) const {
|
||
return pModel == other.pModel && skins == other.skins;
|
||
}
|
||
};
|
||
struct instance_bucket_key_hash {
|
||
std::size_t operator()( instance_bucket_key const &k ) const {
|
||
std::size_t h = std::hash<TModel3d *>()( k.pModel );
|
||
for( auto s : k.skins ) {
|
||
// boost-style hash combine
|
||
h ^= std::hash<int>()( s ) + 0x9e3779b9 + ( h << 6 ) + ( h >> 2 );
|
||
}
|
||
return h;
|
||
}
|
||
};
|
||
using instance_bucket_map = std::unordered_map< instance_bucket_key, std::vector<TAnimModel *>, instance_bucket_key_hash >;
|
||
using sound_sequence = std::vector<sound_source *>;
|
||
using eventlauncher_sequence = std::vector<TEventLauncher *>;
|
||
using memorycell_sequence = std::vector<TMemCell *>;
|
||
// methods
|
||
void
|
||
launch_event(TEventLauncher *Launcher, bool local_only);
|
||
void
|
||
enclose_area( scene::basic_node *Node );
|
||
// members
|
||
scene::bounding_area m_area { glm::dvec3(), static_cast<float>( 0.5 * M_SQRT2 * EU07_CELLSIZE ) };
|
||
bool m_active { false }; // whether the cell holds any actual data content
|
||
shapenode_sequence m_shapesopaque; // opaque pieces of geometry
|
||
shapenode_sequence m_shapestranslucent; // translucent pieces of geometry
|
||
linesnode_sequence m_lines;
|
||
path_sequence m_paths; // path pieces
|
||
instance_sequence m_instancesopaque;
|
||
instance_sequence m_instancetranslucent;
|
||
// batched instance buckets keyed by shared TModel3d*; populated alongside
|
||
// m_instancesopaque for nodes whose TAnimModel::m_instanceable == true.
|
||
// The renderer uses these to amortise per-model state setup across many instances.
|
||
instance_bucket_map m_instancebuckets_opaque;
|
||
traction_sequence m_traction;
|
||
sound_sequence m_sounds;
|
||
eventlauncher_sequence m_eventlaunchers;
|
||
memorycell_sequence m_memorycells;
|
||
// search helpers
|
||
struct lookup_data {
|
||
path_sequence paths;
|
||
traction_sequence traction;
|
||
} m_directories;
|
||
// animation of owned items (legacy code, clean up along with track refactoring)
|
||
bool m_geometrycreated { false };
|
||
unsigned int m_framestamp { 0 }; // id of last rendered gfx frame
|
||
TTrack *tTrackAnim = nullptr; // obiekty do przeliczenia animacji
|
||
command_relay m_relay;
|
||
};
|
||
|
||
// basic scene partitioning structure, holds terrain geometry and collection of cells
|
||
class basic_section {
|
||
|
||
friend opengl_renderer;
|
||
friend opengl33_renderer;
|
||
|
||
public:
|
||
// constructors
|
||
basic_section() = default;
|
||
// methods
|
||
// potentially activates event handler with the same name as provided node, and within handler activation range
|
||
void
|
||
on_click( TAnimModel const *Instance );
|
||
// legacy method, finds and assigns traction piece to specified pantograph of provided vehicle
|
||
void
|
||
update_traction( TDynamicObject *Vehicle, int const Pantographindex );
|
||
// legacy method, updates sounds and polls event launchers within radius around specified point
|
||
void
|
||
update_events( glm::dvec3 const &Location, float const Radius );
|
||
// legacy method, updates sounds and polls event launchers within radius around specified point
|
||
void
|
||
update_sounds( glm::dvec3 const &Location, float const Radius );
|
||
// legacy method, triggers radio-stop procedure for all vehicles in 2km radius around specified location
|
||
void
|
||
radio_stop( glm::dvec3 const &Location, float const Radius );
|
||
// sends content of the class to provided stream
|
||
void
|
||
serialize( std::ostream &Output ) const;
|
||
// restores content of the class from provided stream
|
||
void
|
||
deserialize( std::istream &Input );
|
||
// sends content of the class in legacy (text) format to provided stream
|
||
void
|
||
export_as_text( std::ostream &Output ) const;
|
||
// adds provided shape to the section
|
||
void
|
||
insert( shape_node Shape );
|
||
// adds provided lines to the section
|
||
void
|
||
insert( lines_node Lines );
|
||
// adds provided node to the section
|
||
template <class Type_>
|
||
void
|
||
insert( Type_ *Node ) {
|
||
auto &targetcell { cell( Node->location() ) };
|
||
targetcell.insert( Node );
|
||
// some node types can extend bounding area of the target cell
|
||
m_area.radius = std::max(
|
||
m_area.radius,
|
||
static_cast<float>( glm::length( m_area.center - targetcell.area().center ) + targetcell.area().radius ) ); }
|
||
// erases provided node from the section
|
||
template <class Type_>
|
||
void
|
||
erase( Type_ *Node ) {
|
||
auto &targetcell { cell( Node->location() ) };
|
||
// TODO: re-calculate bounding area after removal
|
||
targetcell.erase( Node ); }
|
||
// registers provided node in the lookup directory of the section enclosing specified point
|
||
template <class Type_>
|
||
void
|
||
register_node( Type_ *Node, glm::dvec3 const &Point ) {
|
||
cell( Point ).register_end( Node ); }
|
||
// find a vehicle located nearest to specified point, within specified radius. reurns: located vehicle and distance
|
||
std::tuple<TDynamicObject *, float>
|
||
find( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled, bool const Findbycoupler );
|
||
// finds a path with one of its ends located in specified point. returns: located path and id of the matching endpoint
|
||
std::tuple<TTrack *, int>
|
||
find( glm::dvec3 const &Point, TTrack const *Exclude );
|
||
// finds a traction piece with one of its ends located in specified point. returns: located traction piece and id of the matching endpoint
|
||
std::tuple<TTraction *, int>
|
||
find( glm::dvec3 const &Point, TTraction const *Exclude );
|
||
// finds a traction piece located nearest to specified point, sharing section with specified other piece and powered in specified direction. returns: located traction piece
|
||
std::tuple<TTraction *, int, float>
|
||
find( glm::dvec3 const &Point, TTraction const *Other, int const Currentdirection );
|
||
// sets center point of the section
|
||
void
|
||
center( glm::dvec3 Center );
|
||
// generates renderable version of held non-instanced geometry
|
||
void
|
||
create_geometry();
|
||
void
|
||
create_map_geometry(const gfx::geometrybank_handle handle);
|
||
void
|
||
get_map_active_paths(map_colored_paths &handles);
|
||
// provides access to bounding area data
|
||
bounding_area const &
|
||
area() const {
|
||
return m_area; }
|
||
|
||
const gfx::geometrybank_handle get_map_geometry()
|
||
{ return m_map_geometryhandle;}
|
||
glm::vec3 find_nearest_track_point(const glm::dvec3 &point);
|
||
|
||
//private:
|
||
// types
|
||
using cell_array = std::array<basic_cell, (EU07_SECTIONSIZE / EU07_CELLSIZE) * (EU07_SECTIONSIZE / EU07_CELLSIZE)>;
|
||
using shapenode_sequence = std::vector<shape_node>;
|
||
// methods
|
||
// provides access to section enclosing specified point
|
||
basic_cell &
|
||
cell(glm::dvec3 const &Location, const glm::ivec2 &offset = glm::ivec2(0));
|
||
// members
|
||
// placement and visibility
|
||
|
||
scene::bounding_area m_area { glm::dvec3(), static_cast<float>( 0.5 * M_SQRT2 * EU07_SECTIONSIZE ) };
|
||
// content
|
||
cell_array m_cells; // partitioning scheme
|
||
shapenode_sequence m_shapes; // large pieces of opaque geometry and (legacy) terrain
|
||
// TODO: implement dedicated, higher fidelity, fixed resolution terrain mesh item
|
||
// gfx renderer data
|
||
gfx::geometrybank_handle m_geometrybank;
|
||
bool m_geometrycreated { false };
|
||
|
||
gfx::geometrybank_handle m_map_geometryhandle;
|
||
};
|
||
|
||
// top-level of scene spatial structure, holds collection of sections
|
||
class basic_region {
|
||
|
||
friend opengl_renderer;
|
||
friend opengl33_renderer;
|
||
|
||
public:
|
||
// constructors
|
||
basic_region();
|
||
// destructor
|
||
~basic_region();
|
||
// methods
|
||
// potentially activates event handler with the same name as provided node, and within handler activation range
|
||
void
|
||
on_click( TAnimModel const *Instance );
|
||
// legacy method, finds and assigns traction piece to specified pantograph of provided vehicle
|
||
void
|
||
update_traction( TDynamicObject *Vehicle, int const Pantographindex );
|
||
// legacy method, polls event launchers around camera
|
||
void
|
||
update_events();
|
||
// legacy method, updates sounds around camera
|
||
void
|
||
update_sounds();
|
||
// checks whether specified file is a valid region data file
|
||
bool
|
||
is_scene( std::string const &Scenariofile ) const;
|
||
// stores content of the class in file with specified name
|
||
void
|
||
serialize( std::string const &Scenariofile ) const;
|
||
// restores content of the class from file with specified name. returns: true on success, false otherwise
|
||
bool
|
||
deserialize( std::string const &Scenariofile );
|
||
// sends content of the class in legacy (text) format to provided stream
|
||
void
|
||
export_as_text( std::ostream &Output ) const;
|
||
// legacy method, links specified path piece with potential neighbours
|
||
void
|
||
TrackJoin( TTrack *Track );
|
||
// legacy method, triggers radio-stop procedure for all vehicles in 2km radius around specified location
|
||
void
|
||
RadioStop( glm::dvec3 const &Location );
|
||
// inserts provided shape in the region
|
||
void
|
||
insert( shape_node Shape, scratch_data &Scratchpad, bool const Transform );
|
||
// inserts provided lines in the region
|
||
void
|
||
insert( lines_node Lines, scratch_data &Scratchpad );
|
||
// inserts provided node in the region
|
||
template <class Type_>
|
||
void
|
||
insert( Type_ *Node ) {
|
||
auto const location { Node->location() };
|
||
if( false == point_inside( location ) ) {
|
||
// NOTE: nodes placed outside of region boundaries are discarded
|
||
// TBD, TODO: clamp coordinates to region boundaries?
|
||
return; }
|
||
section( location ).insert( Node ); }
|
||
// inserts provided node in the region and registers its ends in lookup directory
|
||
template <class Type_>
|
||
void
|
||
insert_and_register( Type_ *Node ) {
|
||
insert( Node );
|
||
for( auto const &point : Node->endpoints() ) {
|
||
if( point_inside( point ) ) {
|
||
section( point ).register_node( Node, point ); } } }
|
||
// removes specified node from the region
|
||
template <class Type_>
|
||
void
|
||
erase( Type_ *Node ) {
|
||
auto const location{ Node->location() };
|
||
if( point_inside( location ) ) {
|
||
section( location ).erase( Node ); } }
|
||
// find a vehicle located nearest to specified point, within specified radius. reurns: located vehicle and distance
|
||
std::tuple<TDynamicObject *, float>
|
||
find_vehicle( glm::dvec3 const &Point, float const Radius, bool const Onlycontrolled, bool const Findbycoupler );
|
||
// finds a path with one of its ends located in specified point. returns: located path and id of the matching endpoint
|
||
std::tuple<TTrack *, int>
|
||
find_path( glm::dvec3 const &Point, TTrack const *Exclude );
|
||
// finds a traction piece with one of its ends located in specified point. returns: located traction piece and id of the matching endpoint
|
||
std::tuple<TTraction *, int>
|
||
find_traction( glm::dvec3 const &Point, TTraction const *Exclude );
|
||
// finds a traction piece located nearest to specified point, sharing section with specified other piece and powered in specified direction. returns: located traction piece
|
||
std::tuple<TTraction *, int>
|
||
find_traction( glm::dvec3 const &Point, TTraction const *Other, int const Currentdirection );
|
||
// finds sections inside specified sphere. returns: list of sections
|
||
std::vector<basic_section *> const &
|
||
sections( glm::dvec3 const &Point, float const Radius );
|
||
void
|
||
create_map_geometry();
|
||
void
|
||
update_poi_geometry();
|
||
basic_section* get_section(size_t section)
|
||
{ return m_sections[section]; }
|
||
gfx::geometrybank_handle
|
||
get_map_poi_geometry() { return m_map_poipoints; }
|
||
glm::vec3 find_nearest_track_point(const glm::dvec3 &pos)
|
||
{ return section(pos).find_nearest_track_point(pos); }
|
||
|
||
//private:
|
||
// types
|
||
using section_array = std::array<basic_section *, sq(EU07_REGIONSIDESECTIONCOUNT)>;
|
||
|
||
struct region_scratchpad {
|
||
|
||
std::vector<basic_section *> sections;
|
||
};
|
||
|
||
gfx::geometrybank_handle m_map_geometrybank;
|
||
gfx::geometrybank_handle m_map_poipoints;
|
||
|
||
// methods
|
||
// checks whether specified point is within boundaries of the region
|
||
bool
|
||
point_inside( glm::dvec3 const &Location );
|
||
// legacy method, trims provided shape to fit into a section. adds trimmed part at the end of provided list, returns true if changes were made
|
||
static
|
||
bool
|
||
RaTriangleDivider( shape_node &Shape, std::deque<shape_node> &Shapes );
|
||
// provides access to section enclosing specified point
|
||
basic_section &
|
||
section( glm::dvec3 const &Location );
|
||
|
||
// members
|
||
section_array m_sections;
|
||
region_scratchpad m_scratchpad;
|
||
|
||
};
|
||
|
||
// global hierarchy map for scene nodes
|
||
extern std::map<std::string, basic_node *> Hierarchy;
|
||
} // scene
|
||
|
||
//---------------------------------------------------------------------------
|