mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-20 15:04:54 +02:00
- SleeperRules: the rules beams are laid by and the faults counted where the layout breaks them - SleeperGeometry: plan helpers, beam boxes with their overlap and support tests and a grid of cells, shared by laying, filling gaps, keeping beams apart and checking instead of three copies of each - SleeperPaths: sampled paths, how near they run and whether one carries another; the tracks near a track and the stretches of a path where a test holds, shared by paths_around/unsettle_around and shared_zones/zones_of - SleeperFeatures: frogs and crossings the timbers turn to; SleeperLines: lines of beams, the rails they carry, where they part - SleeperLayout: clusters and their layout, given the epoch instead of reading it; SleeperModels: the models of the beams - SleeperWorker: the layouts kept per epoch and worked out in the background, behind a small interface - TrackSleepers: the facade, laying the instances, the shared zones and the check - sleepercheck reports a digest of all beams; geomhash skips empty chunks instead of crashing The layout is unchanged: geomhash, sleepercheck with the digest, wings, space, guards, crossings and switches give the same output on baltyk, zz_rozjazdy_id4, zz_rf_slips, zz_rf_wide, zz_slips_s42, zz_demo_przechylka and zz_connections.
230 lines
9.5 KiB
C++
230 lines
9.5 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/.
|
|
*/
|
|
|
|
#include "stdafx.h"
|
|
|
|
#include "world/TrackSleepers.h"
|
|
#include "world/TrackFeatures.h"
|
|
#include "world/SleeperRules.h"
|
|
#include "world/Track.h"
|
|
#include "model/Model3d.h"
|
|
#include "model/MdlMngr.h"
|
|
#include "rendering/renderer.h"
|
|
#include "utilities/Globals.h"
|
|
#include "utilities/Logs.h"
|
|
#include "utilities/utilities.h"
|
|
|
|
#include <glm/gtc/type_ptr.hpp>
|
|
|
|
#include <filesystem>
|
|
#include <fstream>
|
|
#include <iterator>
|
|
|
|
namespace track_sleepers {
|
|
|
|
namespace {
|
|
|
|
// the models of the beams found so far, by their names and skins and seats
|
|
std::map<std::string, beam_model> &known_models() {
|
|
static std::map<std::string, beam_model> models;
|
|
return models;
|
|
}
|
|
|
|
// the vertices of the submodel in the space of the model, through its own transform
|
|
template <typename Visit>
|
|
void vertices_of( TSubModel *Submodel, Visit Visitor ) {
|
|
|
|
glm::mat4 transform { 1.f };
|
|
if( ( Submodel->iFlags & 0xC000 ) && ( Submodel->GetMatrix() != nullptr ) ) {
|
|
transform = glm::make_mat4( Submodel->GetMatrix()->readArray() );
|
|
}
|
|
auto const &vertices { Submodel->m_geometry.handle != null_handle ? GfxRenderer->Vertices( Submodel->m_geometry.handle ) : Submodel->Vertices };
|
|
for( auto const &vertex : vertices ) {
|
|
Visitor( glm::vec3 { transform * glm::vec4 { vertex.position, 1.f } } );
|
|
}
|
|
}
|
|
|
|
// the model of the beams of the name and skin, loaded and measured on the first use; null when it can't be loaded. the feet of
|
|
// the rails go to the given seat in it, or to its top where the seat is below zero
|
|
beam_model const *find_model( std::string const &Name, std::string const &Skinname, float const Seat ) {
|
|
|
|
if( Name.empty() ) {
|
|
return nullptr;
|
|
}
|
|
auto const Skin { Skinname == "none" ? std::string {} : Skinname };
|
|
auto const key { Name + '|' + Skin + '|' + std::to_string( Seat ) };
|
|
auto &models { known_models() };
|
|
auto found { models.find( key ) };
|
|
if( found == models.end() ) {
|
|
auto &model { models[ key ] };
|
|
model.name = Name;
|
|
model.skinname = Skin;
|
|
model.model = TModelsManager::GetModel( Name, false );
|
|
if( model.model == nullptr ) {
|
|
ErrorLog( "Bad sleepers3d: model \"" + Name + "\" failed to load" );
|
|
return nullptr;
|
|
}
|
|
if( false == Skin.empty() ) {
|
|
model.skin = GfxRenderer->Fetch_Material( Skin );
|
|
}
|
|
// a timber put together from its parts, with a part between the ends stretched and repeated to the length
|
|
std::array<char const *, part_count> const names { "begin", "main", "end" };
|
|
model.stretchable = true;
|
|
for( int part = 0; part < part_count; ++part ) {
|
|
model.parts[ part ] = model.model->GetFromName( names[ part ] );
|
|
model.stretchable = model.stretchable && model.parts[ part ] != nullptr;
|
|
}
|
|
auto top { std::numeric_limits<float>::lowest() };
|
|
model.span = { std::numeric_limits<float>::max(), std::numeric_limits<float>::lowest() };
|
|
if( model.stretchable ) {
|
|
for( int part = 0; part < part_count; ++part ) {
|
|
glm::vec2 span { std::numeric_limits<float>::max(), std::numeric_limits<float>::lowest() };
|
|
vertices_of( model.parts[ part ], [&]( glm::vec3 const &Position ) {
|
|
span = { std::min( span.x, Position.x ), std::max( span.y, Position.x ) };
|
|
top = std::max( top, Position.y ); } );
|
|
model.spans[ part ] = span;
|
|
model.span = { std::min( model.span.x, span.x ), std::max( model.span.y, span.y ) };
|
|
}
|
|
model.stretchable = model.spans[ part_main ].y > model.spans[ part_main ].x;
|
|
}
|
|
else {
|
|
// a whole model, measured on its most detailed submodels, the others being the same beam seen from further
|
|
model.parts = {};
|
|
for( auto *submodel { model.model->Root }; submodel != nullptr; submodel = submodel->Next ) {
|
|
if( submodel->fSquareMinDist > 0.f ) {
|
|
continue;
|
|
}
|
|
vertices_of( submodel, [&]( glm::vec3 const &Position ) {
|
|
model.span = { std::min( model.span.x, Position.x ), std::max( model.span.y, Position.x ) };
|
|
top = std::max( top, Position.y ); } );
|
|
}
|
|
}
|
|
// the levels of detail, by the distances they're seen from
|
|
for( auto *submodel { model.model->Root }; submodel != nullptr; submodel = submodel->Next ) {
|
|
if( std::find( model.bands.begin(), model.bands.end(), submodel->fSquareMinDist ) == model.bands.end() ) {
|
|
model.bands.push_back( submodel->fSquareMinDist );
|
|
}
|
|
}
|
|
std::sort( model.bands.begin(), model.bands.end() );
|
|
// the rails sit on the seat of the sleeper, below the fastenings sticking up over it
|
|
model.seat = ( Seat >= 0.f ? Seat : top );
|
|
if( model.length() < 0.5f ) {
|
|
ErrorLog( "Bad sleepers3d: model \"" + Name + "\" has no beam along x" );
|
|
model.model = nullptr;
|
|
return nullptr;
|
|
}
|
|
WriteLog(
|
|
"sleepers3d: model \"" + Name + "\"" + ( model.stretchable ? std::string { " of parts" } : std::string { " whole" } )
|
|
+ ", " + to_string( model.span.x, 3 ) + ".." + to_string( model.span.y, 3 )
|
|
+ ( model.stretchable ?
|
|
", main " + to_string( model.spans[ part_main ].x, 3 ) + ".." + to_string( model.spans[ part_main ].y, 3 ) :
|
|
std::string {} )
|
|
+ ", seat " + to_string( model.seat, 3 ) );
|
|
return &model;
|
|
}
|
|
return ( found->second.model != nullptr ? &found->second : nullptr );
|
|
}
|
|
|
|
} // namespace
|
|
|
|
beam_model const *timber_model() {
|
|
|
|
if( false == track_features::sleepers ) {
|
|
return nullptr;
|
|
}
|
|
return find_model( Global.Sleepers3D, std::string {}, -1.f );
|
|
}
|
|
|
|
beam_model const *plain_model() {
|
|
|
|
return find_model( Global.Sleepers3DPlain, Global.Sleepers3DPlainSkin, Global.Sleepers3DPlainSeat );
|
|
}
|
|
|
|
beam_model const *timber_model( TTrack const &Track ) {
|
|
|
|
if( false == Track.m_timber_model_name.empty() ) {
|
|
if( auto const *model { find_model( Track.m_timber_model_name, Track.m_timber_skin_name, -1.f ) }; model != nullptr ) {
|
|
return model;
|
|
}
|
|
}
|
|
return timber_model();
|
|
}
|
|
|
|
beam_model const *plain_model( TTrack const &Track ) {
|
|
|
|
if( Track.m_sleeper_enabled && false == Track.m_sleeper_model_name.empty() ) {
|
|
// the sleepers of the track drawn as they are in the model, their tops at the feet of the rails
|
|
if( auto const *model { find_model( Track.m_sleeper_model_name, Track.m_sleeper_skin_name, -1.f ) }; model != nullptr ) {
|
|
return model;
|
|
}
|
|
}
|
|
return plain_model();
|
|
}
|
|
|
|
double plain_length( TTrack const &Track ) {
|
|
|
|
if( auto const *model { plain_model( Track ) }; model != nullptr ) {
|
|
return model->length();
|
|
}
|
|
return 2.0 * ( 0.5 * std::abs( Track.fTrackWidth ) + rail_reach + overhang );
|
|
}
|
|
|
|
std::vector<pack_model> const &pack_models() {
|
|
|
|
static std::vector<pack_model> models;
|
|
static auto scanned { false };
|
|
if( scanned ) {
|
|
return models;
|
|
}
|
|
scanned = true;
|
|
namespace fs = std::filesystem;
|
|
std::error_code error;
|
|
auto const root { fs::path { paths::models } };
|
|
for( fs::recursive_directory_iterator entry { root, fs::directory_options::follow_directory_symlink | fs::directory_options::skip_permission_denied, error }, end;
|
|
entry != end; entry.increment( error ) ) {
|
|
if( error ) {
|
|
break;
|
|
}
|
|
if( false == entry->is_regular_file( error ) ) {
|
|
continue;
|
|
}
|
|
auto const extension { ToLower( entry->path().extension().string() ) };
|
|
if( extension != ".t3d" && extension != ".e3d" ) {
|
|
continue;
|
|
}
|
|
auto name { ToLower( fs::relative( entry->path(), root, error ).replace_extension().generic_string() ) };
|
|
if( error || name.empty() ) {
|
|
continue;
|
|
}
|
|
if( name.find( "podklad" ) == std::string::npos && name.find( "podrozjazd" ) == std::string::npos
|
|
&& name.find( "sleeper" ) == std::string::npos && name.find( "timber" ) == std::string::npos ) {
|
|
continue;
|
|
}
|
|
// a timber model has the parts it's put together from, told by their names in the file
|
|
std::ifstream file { entry->path(), std::ios::binary };
|
|
std::string const content { std::istreambuf_iterator<char>( file ), std::istreambuf_iterator<char>() };
|
|
auto const has = [&]( std::string const &Part ) {
|
|
return (
|
|
extension == ".t3d" ?
|
|
content.find( "Name: " + Part + "\n" ) != std::string::npos || content.find( "Name: " + Part + " " ) != std::string::npos
|
|
|| content.find( "Name: " + Part + "\r" ) != std::string::npos :
|
|
content.find( std::string { '\0' } + Part + '\0' ) != std::string::npos ); };
|
|
auto const timber { has( "begin" ) && has( "main" ) && has( "end" ) };
|
|
if( std::none_of( models.begin(), models.end(), [&]( pack_model const &Model ) { return Model.name == name; } ) ) {
|
|
models.push_back( { name, timber } );
|
|
}
|
|
}
|
|
std::sort( models.begin(), models.end(), []( pack_model const &Left, pack_model const &Right ) {
|
|
return ( Left.timber != Right.timber ? Left.timber : Left.name < Right.name ); } );
|
|
return models;
|
|
}
|
|
|
|
|
|
} // namespace track_sleepers
|