17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-11-01 23:59:12 +01:00

feat(sleepers): the timbers of a switch get the fastenings of the rails from the submodels plate, chair and check of their model: a plate under each rail where it lies on a timber, slide chairs under the stock rails along the blades, a fastening of both where a check rail or another rail of the frog lies close beside, the legs of the nose in one plate where they're joined; the rails sit on the plate, the blades ride on the slide plates of the chairs as high as the model has them, their webs shortened; the fastenings drawn within 120 m; selftest fastenings

This commit is contained in:
maj00r
2026-10-23 16:46:58 +02:00
parent fc714d50ec
commit 5aedbcdc1b
12 changed files with 440 additions and 10 deletions

View File

@@ -25,6 +25,7 @@ http://mozilla.org/MPL/2.0/.
#include <filesystem>
#include <fstream>
#include <iterator>
#include <optional>
namespace track_sleepers {
@@ -50,6 +51,40 @@ void vertices_of( TSubModel *Submodel, Visit Visitor ) {
}
}
// the highest the faces of the submodel reach over the point of the plan of the model, in the space of the model; none where
// no face lies over it
std::optional<float> surface_at( TSubModel *Submodel, float const X, float const Z ) {
glm::mat4 transform { 1.f };
if( ( Submodel->iFlags & 0xC000 ) && ( Submodel->GetMatrix() != nullptr ) ) {
transform = glm::make_mat4( Submodel->GetMatrix()->readArray() );
}
auto const indexed { Submodel->m_geometry.handle != null_handle };
auto const &vertices { indexed ? GfxRenderer->Vertices( Submodel->m_geometry.handle ) : Submodel->Vertices };
auto const &indices { indexed ? GfxRenderer->Indices( Submodel->m_geometry.handle ) : Submodel->Indices };
auto const count { indices.empty() ? vertices.size() : indices.size() };
auto const corner = [&]( std::size_t const Index ) {
return glm::vec3 { transform * glm::vec4 { vertices[ indices.empty() ? Index : indices[ Index ] ].position, 1.f } }; };
std::optional<float> result;
for( std::size_t index = 0; index + 2 < count; index += 3 ) {
auto const a { corner( index ) };
auto const b { corner( index + 1 ) };
auto const c { corner( index + 2 ) };
auto const area { ( b.x - a.x ) * ( c.z - a.z ) - ( c.x - a.x ) * ( b.z - a.z ) };
if( std::abs( area ) < 1e-9f ) {
continue;
}
auto const u { ( ( X - a.x ) * ( c.z - a.z ) - ( c.x - a.x ) * ( Z - a.z ) ) / area };
auto const v { ( ( b.x - a.x ) * ( Z - a.z ) - ( X - a.x ) * ( b.z - a.z ) ) / area };
if( u < -1e-5f || v < -1e-5f || u + v > 1.f + 1e-5f ) {
continue;
}
auto const height { a.y + u * ( b.y - a.y ) + v * ( c.y - a.y ) };
result = ( result ? std::max( *result, height ) : height );
}
return result;
}
// 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 ) {
@@ -74,16 +109,16 @@ beam_model const *find_model( std::string const &Name, std::string const &Skinna
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" };
std::array<char const *, part_count> const names { "begin", "main", "end", "plate", "chair", "check" };
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;
model.stretchable = model.stretchable && ( part >= timber_parts || 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 ) {
for( int part = 0; part < timber_parts; ++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 ) };
@@ -112,8 +147,14 @@ beam_model const *find_model( std::string const &Name, std::string const &Skinna
}
}
std::sort( model.bands.begin(), model.bands.end() );
// the rails sit on the seat of the sleeper, below the fastenings sticking up over it
// the rails sit on the seat of the sleeper, below the fastenings sticking up over it, or on the plate of the fastening
model.seat = ( Seat >= 0.f ? Seat : top );
if( Seat < 0.f && model.fastened() ) {
model.seat = std::max( top, surface_at( model.parts[ part_plate ], 0.f, 0.f ).value_or( top ) );
}
if( model.fastened() && model.parts[ part_chair ] != nullptr ) {
model.slide = std::max( 0.f, surface_at( model.parts[ part_chair ], static_cast<float>( -slide_probe ), 0.f ).value_or( model.seat ) - model.seat );
}
if( model.length() < 0.5f ) {
ErrorLog( "Bad sleepers3d: model \"" + Name + "\" has no beam along x" );
model.model = nullptr;
@@ -125,7 +166,11 @@ beam_model const *find_model( std::string const &Name, std::string const &Skinna
+ ( 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 ) );
+ ( model.fastened() ?
std::string { ", fastenings plate" } + ( model.parts[ part_chair ] != nullptr ? " chair" : "" ) + ( model.parts[ part_check ] != nullptr ? " check" : "" ) :
std::string {} )
+ ", seat " + to_string( model.seat, 3 )
+ ( model.slide > 0.f ? ", slide " + to_string( model.slide, 3 ) : std::string {} ) );
return &model;
}
return ( found->second.model != nullptr ? &found->second : nullptr );