mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-07-22 13:59:19 +02:00
Merge branch 'milek-dev' into gfx-work
This commit is contained in:
@@ -414,11 +414,9 @@ void TAnimContainer::EventAssign(basic_event *ev)
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
TAnimModel::TAnimModel( scene::node_data const &Nodedata ) : basic_node( Nodedata ) {
|
||||
// TODO: wrap these in a tuple and move to underlying model
|
||||
for( int index = 0; index < iMaxNumLights; ++index ) {
|
||||
LightsOn[ index ] = LightsOff[ index ] = nullptr; // normalnie nie ma
|
||||
lsLights[ index ] = ls_Off; // a jeśli są, to wyłączone
|
||||
}
|
||||
|
||||
m_lightcolors.fill( glm::vec3{ -1.f } );
|
||||
m_lightopacities.fill( 1.f );
|
||||
}
|
||||
|
||||
TAnimModel::~TAnimModel()
|
||||
@@ -456,7 +454,7 @@ bool TAnimModel::Init(std::string const &asName, std::string const &asReplacable
|
||||
bool TAnimModel::Load(cParser *parser, bool ter)
|
||||
{ // rozpoznanie wpisu modelu i ustawienie świateł
|
||||
std::string name = parser->getToken<std::string>();
|
||||
std::string texture = parser->getToken<std::string>(); // tekstura (zmienia na małe)
|
||||
std::string texture = parser->getToken<std::string>( false ); // tekstura (zmienia na małe)
|
||||
replace_slashes( name );
|
||||
if (!Init( name, texture ))
|
||||
{
|
||||
@@ -499,20 +497,44 @@ bool TAnimModel::Load(cParser *parser, bool ter)
|
||||
if (LightsOn[i] || LightsOff[i]) // Ra: zlikwidowałem wymóg istnienia obu
|
||||
iNumLights = i + 1;
|
||||
|
||||
if ( parser->getToken<std::string>() == "lights" )
|
||||
{
|
||||
int i = 0;
|
||||
std::string token = parser->getToken<std::string>();
|
||||
while( ( token != "" )
|
||||
&& ( token != "endmodel" ) ) {
|
||||
std::string token;
|
||||
do {
|
||||
token = parser->getToken<std::string>();
|
||||
|
||||
LightSet( i, std::stof( token ) ); // stan światła jest liczbą z ułamkiem
|
||||
++i;
|
||||
if( token == "lights" ) {
|
||||
auto i{ 0 };
|
||||
while( ( false == ( token = parser->getToken<std::string>() ).empty() )
|
||||
&& ( token != "lightcolors" )
|
||||
&& ( token != "endmodel" ) ) {
|
||||
|
||||
if( i < iNumLights ) {
|
||||
// stan światła jest liczbą z ułamkiem
|
||||
LightSet( i, std::stof( token ) );
|
||||
}
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
if( token == "lightcolors" ) {
|
||||
auto i{ 0 };
|
||||
while( ( false == ( token = parser->getToken<std::string>() ).empty() )
|
||||
&& ( token != "lights" )
|
||||
&& ( token != "endmodel" ) ) {
|
||||
|
||||
if( ( i < iNumLights )
|
||||
&& ( token != "-1" ) ) { // -1 leaves the default color intact
|
||||
auto const lightcolor { std::stoi( token, 0, 16 ) };
|
||||
m_lightcolors[i] = {
|
||||
( ( lightcolor >> 16 ) & 0xff ) / 255.f,
|
||||
( ( lightcolor >> 8 ) & 0xff ) / 255.f,
|
||||
( ( lightcolor ) & 0xff ) / 255.f };
|
||||
}
|
||||
++i;
|
||||
}
|
||||
}
|
||||
} while( ( false == token.empty() )
|
||||
&& ( token != "endmodel" ) );
|
||||
|
||||
token = "";
|
||||
parser->getTokens(); *parser >> token;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -684,6 +706,10 @@ void TAnimModel::RaPrepare()
|
||||
if (LightsOff[i])
|
||||
LightsOff[i]->iVisible = !state;
|
||||
}
|
||||
// potentially modify freespot colors
|
||||
if( LightsOn[i] ) {
|
||||
LightsOn[i]->SetDiffuseOverride( m_lightcolors[i], true);
|
||||
}
|
||||
}
|
||||
TSubModel::iInstance = reinterpret_cast<std::uintptr_t>( this ); //żeby nie robić cudzych animacji
|
||||
TSubModel::pasText = &asText; // przekazanie tekstu do wyświetlacza (!!!! do przemyślenia)
|
||||
|
||||
16
AnimModel.h
16
AnimModel.h
@@ -179,19 +179,19 @@ private:
|
||||
// members
|
||||
TAnimContainer *pRoot { nullptr }; // pojemniki sterujące, tylko dla aniomowanych submodeli
|
||||
TModel3d *pModel { nullptr };
|
||||
// double fBlinkTimer { 0.0 };
|
||||
int iNumLights { 0 };
|
||||
TSubModel *LightsOn[ iMaxNumLights ]; // Ra: te wskaźniki powinny być w ramach TModel3d
|
||||
TSubModel *LightsOff[ iMaxNumLights ];
|
||||
glm::vec3 vAngle; // bazowe obroty egzemplarza względem osi
|
||||
material_data m_materialdata;
|
||||
|
||||
std::string asText; // tekst dla wyświetlacza znakowego
|
||||
TAnimAdvanced *pAdvanced { nullptr };
|
||||
// TODO: wrap into a light state struct
|
||||
float lsLights[ iMaxNumLights ];
|
||||
std::array<float, iMaxNumLights> m_lighttimers { 0.f };
|
||||
std::array<float, iMaxNumLights> m_lightopacities { 1.f };
|
||||
// TODO: wrap into a light state struct, remove fixed element count
|
||||
int iNumLights { 0 };
|
||||
std::array<TSubModel *, iMaxNumLights> LightsOn {}; // Ra: te wskaźniki powinny być w ramach TModel3d
|
||||
std::array<TSubModel *, iMaxNumLights> LightsOff {};
|
||||
std::array<float, iMaxNumLights> lsLights {}; // ls_Off
|
||||
std::array<glm::vec3, iMaxNumLights> m_lightcolors; // -1 in constructor
|
||||
std::array<float, iMaxNumLights> m_lighttimers {};
|
||||
std::array<float, iMaxNumLights> m_lightopacities; // {1} in constructor
|
||||
float fOnTime { 1.f / 2 };// { 60.f / 45.f / 2 };
|
||||
float fOffTime { 1.f / 2 };// { 60.f / 45.f / 2 }; // były stałymi, teraz mogą być zmienne dla każdego egzemplarza
|
||||
float fTransitionTime { fOnTime * 0.9f }; // time
|
||||
|
||||
73
DynObj.cpp
73
DynObj.cpp
@@ -3946,6 +3946,13 @@ void TDynamicObject::RenderSounds() {
|
||||
|
||||
if( Global.iPause != 0 ) { return; }
|
||||
|
||||
if( ( m_startjoltplayed )
|
||||
&& ( ( std::abs( MoverParameters->AccSVBased ) < 0.01 )
|
||||
|| ( GetVelocity() < 0.01 ) ) ) {
|
||||
// if the vehicle comes to a stop set the movement jolt to play when it starts moving again
|
||||
m_startjoltplayed = false;
|
||||
}
|
||||
|
||||
double const dt{ Timer::GetDeltaRenderTime() };
|
||||
double volume{ 0.0 };
|
||||
double frequency{ 1.0 };
|
||||
@@ -4116,6 +4123,10 @@ void TDynamicObject::RenderSounds() {
|
||||
volume = rsPisk.m_amplitudeoffset + interpolate( -1.0, 1.0, brakeforceratio ) * rsPisk.m_amplitudefactor;
|
||||
if( volume > 0.075 ) {
|
||||
rsPisk
|
||||
.pitch(
|
||||
true == rsPisk.is_combined() ?
|
||||
MoverParameters->Vel * 0.01f :
|
||||
rsPisk.m_frequencyoffset + rsPisk.m_frequencyfactor * 1.f )
|
||||
.gain( volume )
|
||||
.play( sound_flags::exclusive | sound_flags::looping );
|
||||
}
|
||||
@@ -4145,19 +4156,22 @@ void TDynamicObject::RenderSounds() {
|
||||
}
|
||||
// NBMX sygnal odjazdu
|
||||
if( MoverParameters->DoorClosureWarning ) {
|
||||
if( ( MoverParameters->DepartureSignal )
|
||||
for( auto &door : m_doorsounds ) {
|
||||
// TBD, TODO: per-location door state triggers?
|
||||
if( ( MoverParameters->DepartureSignal )
|
||||
/*
|
||||
|| ( ( MoverParameters->DoorCloseCtrl = control::autonomous )
|
||||
&& ( ( ( false == MoverParameters->DoorLeftOpened ) && ( dDoorMoveL > 0.0 ) )
|
||||
|| ( ( false == MoverParameters->DoorRightOpened ) && ( dDoorMoveR > 0.0 ) ) ) )
|
||||
|| ( ( MoverParameters->DoorCloseCtrl = control::autonomous )
|
||||
&& ( ( ( false == MoverParameters->DoorLeftOpened ) && ( dDoorMoveL > 0.0 ) )
|
||||
|| ( ( false == MoverParameters->DoorRightOpened ) && ( dDoorMoveR > 0.0 ) ) ) )
|
||||
*/
|
||||
) {
|
||||
// for the autonomous doors play the warning automatically whenever a door is closing
|
||||
// MC: pod warunkiem ze jest zdefiniowane w chk
|
||||
sDepartureSignal.play( sound_flags::exclusive | sound_flags::looping );
|
||||
}
|
||||
else {
|
||||
sDepartureSignal.stop();
|
||||
) {
|
||||
// for the autonomous doors play the warning automatically whenever a door is closing
|
||||
// MC: pod warunkiem ze jest zdefiniowane w chk
|
||||
door.sDepartureSignal.play( sound_flags::exclusive | sound_flags::looping );
|
||||
}
|
||||
else {
|
||||
door.sDepartureSignal.stop();
|
||||
}
|
||||
}
|
||||
}
|
||||
// NBMX Obsluga drzwi, MC: zuniwersalnione
|
||||
@@ -4401,6 +4415,17 @@ void TDynamicObject::RenderSounds() {
|
||||
rscurve.stop();
|
||||
}
|
||||
|
||||
// movement start jolt
|
||||
if( false == m_startjoltplayed ) {
|
||||
auto const velocity { GetVelocity() };
|
||||
if( ( MoverParameters->V > 0.0 ? ( MoverParameters->AccSVBased > 0.1 ) : ( MoverParameters->AccSVBased < 0.1 ) )
|
||||
&& ( velocity > 1.0 )
|
||||
&& ( velocity < 15.0 ) ) {
|
||||
m_startjolt.play( sound_flags::exclusive );
|
||||
m_startjoltplayed = true;
|
||||
}
|
||||
}
|
||||
|
||||
// McZapkie! - to wazne - SoundFlag wystawiane jest przez moje moduly
|
||||
// gdy zachodza pewne wydarzenia komentowane dzwiekiem.
|
||||
if( TestFlag( MoverParameters->SoundFlag, sound::pneumatic ) ) {
|
||||
@@ -5413,12 +5438,6 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co
|
||||
sHorn3.owner( this );
|
||||
}
|
||||
|
||||
else if( token == "departuresignal:" ) {
|
||||
// pliki z sygnalem odjazdu
|
||||
sDepartureSignal.deserialize( parser, sound_type::multipart, sound_parameters::range );
|
||||
sDepartureSignal.owner( this );
|
||||
}
|
||||
|
||||
else if( token == "pantographup:" ) {
|
||||
// pliki dzwiekow pantografow
|
||||
sound_source pantographup { sound_placement::external };
|
||||
@@ -5470,6 +5489,19 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co
|
||||
sSmallCompressor.owner( this );
|
||||
}
|
||||
|
||||
else if( token == "departuresignal:" ) {
|
||||
// pliki z sygnalem odjazdu
|
||||
sound_source soundtemplate { sound_placement::general, 25.f };
|
||||
soundtemplate.deserialize( parser, sound_type::multipart, sound_parameters::range );
|
||||
soundtemplate.owner( this );
|
||||
for( auto &door : m_doorsounds ) {
|
||||
// apply configuration to all defined doors, but preserve their individual offsets
|
||||
auto const dooroffset { door.lock.offset() };
|
||||
door.sDepartureSignal = soundtemplate;
|
||||
door.sDepartureSignal.offset( dooroffset );
|
||||
}
|
||||
}
|
||||
|
||||
else if( token == "dooropen:" ) {
|
||||
sound_source soundtemplate { sound_placement::general, 25.f };
|
||||
soundtemplate.deserialize( parser, sound_type::single );
|
||||
@@ -5619,6 +5651,7 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co
|
||||
|| ( sides == "left" ) ) {
|
||||
// left...
|
||||
auto const location { glm::vec3 { MoverParameters->Dim.W * 0.5f, MoverParameters->Dim.H * 0.5f, offset } };
|
||||
door.sDepartureSignal.offset( location );
|
||||
door.rsDoorClose.offset( location );
|
||||
door.rsDoorOpen.offset( location );
|
||||
door.lock.offset( location );
|
||||
@@ -5631,6 +5664,7 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co
|
||||
|| ( sides == "right" ) ) {
|
||||
// ...and right
|
||||
auto const location { glm::vec3 { MoverParameters->Dim.W * -0.5f, MoverParameters->Dim.H * 0.5f, offset } };
|
||||
door.sDepartureSignal.offset( location );
|
||||
door.rsDoorClose.offset( location );
|
||||
door.rsDoorOpen.offset( location );
|
||||
door.lock.offset( location );
|
||||
@@ -5816,6 +5850,11 @@ void TDynamicObject::LoadMMediaFile( std::string const &TypeName, std::string co
|
||||
couplersounds.dsbBufferClamp_loud = bufferclash;
|
||||
}
|
||||
}
|
||||
else if( token == "startjolt:" ) {
|
||||
// movement start jolt
|
||||
m_startjolt.deserialize( parser, sound_type::single );
|
||||
m_startjolt.owner( this );
|
||||
}
|
||||
|
||||
} while( token != "" );
|
||||
|
||||
|
||||
4
DynObj.h
4
DynObj.h
@@ -309,6 +309,7 @@ private:
|
||||
};
|
||||
|
||||
struct door_sounds {
|
||||
sound_source sDepartureSignal { sound_placement::general };
|
||||
sound_source rsDoorOpen { sound_placement::general }; // Ra: przeniesione z kabiny
|
||||
sound_source rsDoorClose { sound_placement::general };
|
||||
sound_source lock { sound_placement::general };
|
||||
@@ -429,11 +430,12 @@ private:
|
||||
sound_source rsSlippery { sound_placement::external, EU07_SOUND_BRAKINGCUTOFFRANGE }; // moved from cab
|
||||
sound_source sSand { sound_placement::external };
|
||||
// moving part and other external sounds
|
||||
sound_source m_startjolt { sound_placement::general }; // movement start jolt, played once on initial acceleration at slow enough speed
|
||||
bool m_startjoltplayed { false };
|
||||
std::array<coupler_sounds, 2> m_couplersounds; // always front and rear
|
||||
std::vector<pantograph_sounds> m_pantographsounds; // typically 2 but can be less (or more?)
|
||||
std::vector<door_sounds> m_doorsounds; // can expect symmetrical arrangement, but don't count on it
|
||||
bool m_doorlocks { false }; // sound helper, current state of door locks
|
||||
sound_source sDepartureSignal { sound_placement::general };
|
||||
sound_source sHorn1 { sound_placement::external, 5 * EU07_SOUND_RUNNINGNOISECUTOFFRANGE };
|
||||
sound_source sHorn2 { sound_placement::external, 5 * EU07_SOUND_RUNNINGNOISECUTOFFRANGE };
|
||||
sound_source sHorn3 { sound_placement::external, 5 * EU07_SOUND_RUNNINGNOISECUTOFFRANGE };
|
||||
|
||||
@@ -1485,7 +1485,7 @@ lights_event::deserialize_( cParser &Input, scene::scratch_data &Scratchpad ) {
|
||||
}
|
||||
while( lightidx < lightcountlimit ) {
|
||||
// HACK: mark unspecified lights with magic value
|
||||
m_lights[ lightidx++ ] = -2.f;
|
||||
m_lights[ lightidx++ ] = std::numeric_limits<float>::quiet_NaN();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1498,11 +1498,11 @@ lights_event::run_() {
|
||||
if( targetmodel == nullptr ) { continue; }
|
||||
// event effect code
|
||||
for( auto lightidx = 0; lightidx < iMaxNumLights; ++lightidx ) {
|
||||
if( m_lights[ lightidx ] == -2.f ) {
|
||||
if( std::isnan( m_lights[ lightidx ] ) ) {
|
||||
// processed all supplied values, bail out
|
||||
break;
|
||||
}
|
||||
if( m_lights[ lightidx ] >= 0.f ) {
|
||||
if( m_lights[ lightidx ] != -1.f ) {
|
||||
// -1 zostawia bez zmiany
|
||||
targetmodel->LightSet(
|
||||
lightidx,
|
||||
@@ -1518,7 +1518,7 @@ lights_event::export_as_text_( std::ostream &Output ) const {
|
||||
|
||||
auto lightidx{ 0 };
|
||||
while( ( lightidx < iMaxNumLights )
|
||||
&& ( m_lights[ lightidx ] > -2.0 ) ) {
|
||||
&& ( false == std::isnan( m_lights[ lightidx ] ) ) ) {
|
||||
Output << m_lights[ lightidx ] << ' ';
|
||||
++lightidx;
|
||||
}
|
||||
|
||||
@@ -4314,7 +4314,8 @@ double TMoverParameters::CouplerForce(int CouplerN, double dt)
|
||||
else if( ( Couplers[ CouplerN ].CouplingFlag != coupling::faux )
|
||||
&& ( newdist > 0.001 )
|
||||
&& ( Couplers[ CouplerN ].Dist <= 0.001 )
|
||||
&& ( absdV > 0.005 ) ) {
|
||||
&& ( absdV > 0.005 )
|
||||
&& ( Vel > 1.0 ) ) {
|
||||
// 090503: dzwieki pracy sprzegu
|
||||
SetFlag(
|
||||
Couplers[ CouplerN ].sounds,
|
||||
|
||||
19
Model3d.cpp
19
Model3d.cpp
@@ -72,6 +72,25 @@ void TSubModel::Name(std::string const &Name)
|
||||
pName = Name;
|
||||
};
|
||||
|
||||
// sets rgb components of diffuse color override to specified value
|
||||
void
|
||||
TSubModel::SetDiffuseOverride( glm::vec3 const &Color, bool const Includechildren, bool const Includesiblings ) {
|
||||
|
||||
if( eType == TP_FREESPOTLIGHT ) {
|
||||
DiffuseOverride = Color;
|
||||
}
|
||||
if( true == Includesiblings ) {
|
||||
auto sibling { this };
|
||||
while( ( sibling = sibling->Next ) != nullptr ) {
|
||||
sibling->SetDiffuseOverride( Color, Includechildren, false ); // no need for all siblings to duplicate the work
|
||||
}
|
||||
}
|
||||
if( ( true == Includechildren )
|
||||
&& ( Child != nullptr ) ) {
|
||||
Child->SetDiffuseOverride( Color, Includechildren, true ); // node's children include child's siblings and children
|
||||
}
|
||||
}
|
||||
|
||||
// sets visibility level (alpha component) to specified value
|
||||
void
|
||||
TSubModel::SetVisibilityLevel( float const Level, bool const Includechildren, bool const Includesiblings ) {
|
||||
|
||||
@@ -106,6 +106,7 @@ private:
|
||||
f4Diffuse { 1.0f,1.0f,1.0f,1.0f },
|
||||
f4Specular { 0.0f,0.0f,0.0f,1.0f },
|
||||
f4Emision { 1.0f,1.0f,1.0f,1.0f };
|
||||
glm::vec3 DiffuseOverride { -1.f };
|
||||
normalization m_normalizenormals { normalization::none }; // indicates vectors need to be normalized due to scaling etc
|
||||
float fWireSize { 0.0f }; // nie używane, ale wczytywane
|
||||
float fSquareMaxDist { 10000.0f * 10000.0f };
|
||||
@@ -195,6 +196,8 @@ public:
|
||||
uint32_t Flags() const { return iFlags; };
|
||||
void UnFlagNext() { iFlags &= 0x00FFFFFF; };
|
||||
void ColorsSet( glm::vec3 const &Ambient, glm::vec3 const &Diffuse, glm::vec3 const &Specular );
|
||||
// sets rgb components of diffuse color override to specified value
|
||||
void SetDiffuseOverride( glm::vec3 const &Color, bool const Includechildren = false, bool const Includesiblings = false );
|
||||
// sets visibility level (alpha component) to specified value
|
||||
void SetVisibilityLevel( float const Level, bool const Includechildren = false, bool const Includesiblings = false );
|
||||
// sets light level (alpha component of illumination color) to specified value
|
||||
|
||||
80
PyInt.cpp
80
PyInt.cpp
@@ -27,12 +27,11 @@ void render_task::run() {
|
||||
if( output != nullptr ) {
|
||||
auto *outputwidth { PyObject_CallMethod( m_renderer, "get_width", nullptr ) };
|
||||
auto *outputheight { PyObject_CallMethod( m_renderer, "get_height", nullptr ) };
|
||||
opengl_texture &tex = GfxRenderer.Texture( m_target );
|
||||
// upload texture data
|
||||
if( ( outputwidth != nullptr )
|
||||
&& ( outputheight != nullptr )
|
||||
&& tex.id != -1) {
|
||||
::glBindTexture( GL_TEXTURE_2D, tex.id );
|
||||
&& m_target != -1) {
|
||||
::glBindTexture( GL_TEXTURE_2D, m_target );
|
||||
|
||||
int w = PyInt_AsLong( outputwidth );
|
||||
int h = PyInt_AsLong( outputheight );
|
||||
@@ -108,8 +107,6 @@ auto python_taskqueue::init() -> bool {
|
||||
PyObject *stringioclassname { nullptr };
|
||||
PyObject *stringioobject { nullptr };
|
||||
|
||||
// save a pointer to the main PyThreadState object
|
||||
m_mainthread = PyThreadState_Get();
|
||||
// do the setup work while we hold the lock
|
||||
m_main = PyImport_ImportModule("__main__");
|
||||
if (m_main == nullptr) {
|
||||
@@ -135,8 +132,8 @@ auto python_taskqueue::init() -> bool {
|
||||
|
||||
if( false == run_file( "abstractscreenrenderer" ) ) { goto release_and_exit; }
|
||||
|
||||
// release the lock
|
||||
PyEval_ReleaseLock();
|
||||
// release the lock, save the state for future use
|
||||
m_mainthread = PyEval_SaveThread();
|
||||
|
||||
WriteLog( "Python Interpreter setup complete" );
|
||||
|
||||
@@ -144,12 +141,11 @@ auto python_taskqueue::init() -> bool {
|
||||
for( auto &worker : m_workers ) {
|
||||
|
||||
auto *openglcontextwindow { Application.window( -1 ) };
|
||||
worker =
|
||||
std::make_unique<std::thread>(
|
||||
&python_taskqueue::run, this,
|
||||
openglcontextwindow, std::ref( m_tasks ), std::ref( m_condition ), std::ref( m_exit ) );
|
||||
worker = std::thread(
|
||||
&python_taskqueue::run, this,
|
||||
openglcontextwindow, std::ref( m_tasks ), std::ref( m_condition ), std::ref( m_exit ) );
|
||||
|
||||
if( worker == nullptr ) { return false; }
|
||||
if( false == worker.joinable() ) { return false; }
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -168,9 +164,8 @@ void python_taskqueue::exit() {
|
||||
m_exit = true;
|
||||
m_condition.notify_all();
|
||||
// let them free up their shit before we proceed
|
||||
for( auto const &worker : m_workers ) {
|
||||
if (worker && worker->joinable())
|
||||
worker->join();
|
||||
for( auto &worker : m_workers ) {
|
||||
worker.join();
|
||||
}
|
||||
// get rid of the leftover tasks
|
||||
// with the workers dead we don't have to worry about concurrent access anymore
|
||||
@@ -178,8 +173,7 @@ void python_taskqueue::exit() {
|
||||
task->cancel();
|
||||
}
|
||||
// take a bow
|
||||
PyEval_AcquireLock();
|
||||
PyThreadState_Swap( m_mainthread );
|
||||
acquire_lock();
|
||||
Py_Finalize();
|
||||
}
|
||||
|
||||
@@ -188,7 +182,7 @@ auto python_taskqueue::insert( task_request const &Task ) -> bool {
|
||||
|
||||
if( ( Task.renderer.empty() )
|
||||
|| ( Task.input == nullptr )
|
||||
|| ( Task.target == null_handle ) ) { return false; }
|
||||
|| ( Task.target == 0 ) ) { return false; }
|
||||
|
||||
auto *renderer { fetch_renderer( Task.renderer ) };
|
||||
if( renderer == nullptr ) { return false; }
|
||||
@@ -202,11 +196,11 @@ auto python_taskqueue::insert( task_request const &Task ) -> bool {
|
||||
for( auto &task : m_tasks.data ) {
|
||||
if( task->target() == Task.target ) {
|
||||
// replace pending task in the slot with the more recent one
|
||||
PyEval_AcquireLock();
|
||||
acquire_lock();
|
||||
{
|
||||
task->cancel();
|
||||
}
|
||||
PyEval_ReleaseLock();
|
||||
release_lock();
|
||||
task = newtask;
|
||||
newtaskinserted = true;
|
||||
break;
|
||||
@@ -240,6 +234,18 @@ auto python_taskqueue::run_file( std::string const &File, std::string const &Pat
|
||||
return true;
|
||||
}
|
||||
|
||||
// acquires the python gil and sets the main thread as current
|
||||
void python_taskqueue::acquire_lock() {
|
||||
|
||||
PyEval_RestoreThread( m_mainthread );
|
||||
}
|
||||
|
||||
// releases the python gil and swaps the main thread out
|
||||
void python_taskqueue::release_lock() {
|
||||
|
||||
PyEval_SaveThread();
|
||||
}
|
||||
|
||||
auto python_taskqueue::fetch_renderer( std::string const Renderer ) ->PyObject * {
|
||||
|
||||
auto const lookup { m_renderers.find( Renderer ) };
|
||||
@@ -250,17 +256,15 @@ auto python_taskqueue::fetch_renderer( std::string const Renderer ) ->PyObject *
|
||||
auto const path { substr_path( Renderer ) };
|
||||
auto const file { Renderer.substr( path.size() ) };
|
||||
PyObject *renderer { nullptr };
|
||||
PyObject *rendererarguments { nullptr };
|
||||
PyObject *renderername { nullptr };
|
||||
|
||||
PyEval_AcquireLock();
|
||||
|
||||
if( m_main == nullptr ) {
|
||||
ErrorLog( "Python Renderer: __main__ module is missing" );
|
||||
goto cache_and_return;
|
||||
}
|
||||
|
||||
PyObject *rendererarguments { nullptr };
|
||||
PyObject *renderername { nullptr };
|
||||
acquire_lock();
|
||||
{
|
||||
if( m_main == nullptr ) {
|
||||
ErrorLog( "Python Renderer: __main__ module is missing" );
|
||||
goto cache_and_return;
|
||||
}
|
||||
|
||||
if( false == run_file( file, path ) ) {
|
||||
goto cache_and_return;
|
||||
}
|
||||
@@ -287,7 +291,7 @@ cache_and_return:
|
||||
Py_DECREF( rendererarguments );
|
||||
}
|
||||
}
|
||||
PyEval_ReleaseLock();
|
||||
release_lock();
|
||||
// cache the failures as well so we don't try again on subsequent requests
|
||||
m_renderers.emplace( Renderer, renderer );
|
||||
return renderer;
|
||||
@@ -320,14 +324,14 @@ void python_taskqueue::run( GLFWwindow *Context, rendertask_sequence &Tasks, thr
|
||||
}
|
||||
if( task != nullptr ) {
|
||||
// swap in my thread state
|
||||
PyEval_AcquireLock();
|
||||
PyThreadState_Swap( threadstate );
|
||||
// execute python code
|
||||
task->run();
|
||||
error();
|
||||
PyEval_RestoreThread( threadstate );
|
||||
{
|
||||
// execute python code
|
||||
task->run();
|
||||
error();
|
||||
}
|
||||
// clear the thread state
|
||||
PyThreadState_Swap( nullptr );
|
||||
PyEval_ReleaseLock();
|
||||
PyEval_SaveThread();
|
||||
}
|
||||
// TBD, TODO: add some idle time between tasks in case we're on a single thread cpu?
|
||||
} while( task != nullptr );
|
||||
|
||||
14
PyInt.h
14
PyInt.h
@@ -56,7 +56,7 @@ class render_task {
|
||||
|
||||
public:
|
||||
// constructors
|
||||
render_task( PyObject *Renderer, PyObject *Input, texture_handle Target ) :
|
||||
render_task( PyObject *Renderer, PyObject *Input, GLuint Target ) :
|
||||
m_renderer( Renderer ), m_input( Input ), m_target( Target )
|
||||
{}
|
||||
// methods
|
||||
@@ -68,9 +68,11 @@ private:
|
||||
// members
|
||||
PyObject *m_renderer {nullptr};
|
||||
PyObject *m_input { nullptr };
|
||||
texture_handle m_target { null_handle };
|
||||
GLuint m_target { 0 };
|
||||
};
|
||||
|
||||
|
||||
|
||||
class python_taskqueue {
|
||||
|
||||
public:
|
||||
@@ -79,7 +81,7 @@ public:
|
||||
|
||||
std::string const &renderer;
|
||||
PyObject *input;
|
||||
texture_handle target;
|
||||
GLuint target;
|
||||
};
|
||||
// constructors
|
||||
python_taskqueue() = default;
|
||||
@@ -92,11 +94,15 @@ public:
|
||||
auto insert( task_request const &Task ) -> bool;
|
||||
// executes python script stored in specified file. returns true on success
|
||||
auto run_file( std::string const &File, std::string const &Path = "" ) -> bool;
|
||||
// acquires the python gil and sets the main thread as current
|
||||
void acquire_lock();
|
||||
// releases the python gil and swaps the main thread out
|
||||
void release_lock();
|
||||
|
||||
private:
|
||||
// types
|
||||
static int const WORKERCOUNT { 1 };
|
||||
using worker_array = std::array<std::unique_ptr<std::thread>, WORKERCOUNT >;
|
||||
using worker_array = std::array<std::thread, WORKERCOUNT >;
|
||||
using rendertask_sequence = threading::lockable< std::deque<render_task *> >;
|
||||
// methods
|
||||
auto fetch_renderer( std::string const Renderer ) -> PyObject *;
|
||||
|
||||
39
Segment.cpp
39
Segment.cpp
@@ -372,12 +372,10 @@ Math3D::vector3 TSegment::FastGetPoint(double const t) const
|
||||
interpolate( Point1, Point2, t ) );
|
||||
}
|
||||
|
||||
bool TSegment::RenderLoft( gfx::vertex_array &Output, Math3D::vector3 const &Origin, const gfx::vertex_array &ShapePoints, int iNumShapePoints, double fTextureLength, double Texturescale, int iSkip, int iEnd, float fOffsetX, glm::vec3 **p, bool bRender)
|
||||
bool TSegment::RenderLoft( gfx::vertex_array &Output, Math3D::vector3 const &Origin, const gfx::vertex_array &ShapePoints, int iNumShapePoints, double fTextureLength, double Texturescale, int iSkip, int iEnd, std::pair<float, float> fOffsetX, glm::vec3 **p, bool bRender)
|
||||
{ // generowanie trójkątów dla odcinka trajektorii ruchu
|
||||
// standardowo tworzy triangle_strip dla prostego albo ich zestaw dla łuku
|
||||
// po modyfikacji - dla ujemnego (iNumShapePoints) w dodatkowych polach tabeli
|
||||
// podany jest przekrój końcowy
|
||||
// podsypka toru jest robiona za pomocą 6 punktów, szyna 12, drogi i rzeki na 3+2+3
|
||||
// po modyfikacji - dla ujemnego (iNumShapePoints) w dodatkowych polach tabeli podany jest przekrój końcowy
|
||||
|
||||
if( fTsBuffer.empty() )
|
||||
return false; // prowizoryczne zabezpieczenie przed wysypem - ustalić faktyczną przyczynę
|
||||
@@ -408,8 +406,12 @@ bool TSegment::RenderLoft( gfx::vertex_array &Output, Math3D::vector3 const &Ori
|
||||
if( iEnd == 0 )
|
||||
iEnd = iSegCount;
|
||||
fEnd = fLength * double( iEnd ) / double( iSegCount );
|
||||
/*
|
||||
m2 = s / fEnd;
|
||||
jmm2 = 1.0 - m2;
|
||||
*/
|
||||
m2 = static_cast<float>( i - iSkip ) / ( iEnd - iSkip );
|
||||
|
||||
jmm2 = 1.f - m2;
|
||||
|
||||
while( i < iEnd ) {
|
||||
|
||||
@@ -417,13 +419,17 @@ bool TSegment::RenderLoft( gfx::vertex_array &Output, Math3D::vector3 const &Ori
|
||||
s += step; // końcowa pozycja segmentu [m]
|
||||
m1 = m2;
|
||||
jmm1 = jmm2; // stara pozycja
|
||||
/*
|
||||
m2 = s / fEnd;
|
||||
jmm2 = 1.0 - m2; // nowa pozycja
|
||||
*/
|
||||
m2 = static_cast<float>( i - iSkip ) / ( iEnd - iSkip );
|
||||
|
||||
jmm2 = 1.f - m2; // nowa pozycja
|
||||
if( i == iEnd ) { // gdy przekroczyliśmy koniec - stąd dziury w torach...
|
||||
step -= ( s - fEnd ); // jeszcze do wyliczenia mapowania potrzebny
|
||||
s = fEnd;
|
||||
m2 = 1.0;
|
||||
jmm2 = 0.0;
|
||||
m2 = 1.f;
|
||||
jmm2 = 0.f;
|
||||
}
|
||||
|
||||
while( tv1 < 0.0 ) {
|
||||
@@ -441,11 +447,11 @@ bool TSegment::RenderLoft( gfx::vertex_array &Output, Math3D::vector3 const &Ori
|
||||
}
|
||||
parallel2 = glm::normalize( parallel2 );
|
||||
|
||||
// TODO: refactor the loop, there's no need to calculate starting points for each segment when we can copy the end points of the previous one
|
||||
if( trapez ) {
|
||||
for( int j = 0; j < iNumShapePoints; ++j ) {
|
||||
pt = parallel1 * ( jmm1 * ( ShapePoints[ j ].position.x - fOffsetX ) + m1 * ShapePoints[ j + iNumShapePoints ].position.x ) + pos1;
|
||||
pt = parallel1 * ( jmm1 * ( ShapePoints[ j ].position.x - fOffsetX.first ) + m1 * ( ShapePoints[ j + iNumShapePoints ].position.x - fOffsetX.second ) ) + pos1;
|
||||
pt.y += jmm1 * ShapePoints[ j ].position.y + m1 * ShapePoints[ j + iNumShapePoints ].position.y;
|
||||
// pt -= Origin;
|
||||
norm = ( jmm1 * ShapePoints[ j ].normal.x + m1 * ShapePoints[ j + iNumShapePoints ].normal.x ) * parallel1;
|
||||
norm.y += jmm1 * ShapePoints[ j ].normal.y + m1 * ShapePoints[ j + iNumShapePoints ].normal.y;
|
||||
if( bRender ) {
|
||||
@@ -463,11 +469,10 @@ bool TSegment::RenderLoft( gfx::vertex_array &Output, Math3D::vector3 const &Ori
|
||||
( *p )++;
|
||||
} // zapamiętanie brzegu jezdni
|
||||
// dla trapezu drugi koniec ma inne współrzędne
|
||||
pt = parallel2 * ( jmm2 * ( ShapePoints[ j ].position.x - fOffsetX ) + m2 * ShapePoints[ j + iNumShapePoints ].position.x ) + pos2;
|
||||
pt = parallel2 * ( jmm2 * ( ShapePoints[ j ].position.x - fOffsetX.first ) + m2 * ( ShapePoints[ j + iNumShapePoints ].position.x - fOffsetX.second ) ) + pos2;
|
||||
pt.y += jmm2 * ShapePoints[ j ].position.y + m2 * ShapePoints[ j + iNumShapePoints ].position.y;
|
||||
// pt -= Origin;
|
||||
norm = ( jmm1 * ShapePoints[ j ].normal.x + m1 * ShapePoints[ j + iNumShapePoints ].normal.x ) * parallel2;
|
||||
norm.y += jmm1 * ShapePoints[ j ].normal.y + m1 * ShapePoints[ j + iNumShapePoints ].normal.y;
|
||||
norm = ( jmm2 * ShapePoints[ j ].normal.x + m2 * ShapePoints[ j + iNumShapePoints ].normal.x ) * parallel2;
|
||||
norm.y += jmm2 * ShapePoints[ j ].normal.y + m2 * ShapePoints[ j + iNumShapePoints ].normal.y;
|
||||
if( bRender ) {
|
||||
// skrzyżowania podczas łączenia siatek mogą nie renderować poboczy, ale potrzebować punktów
|
||||
Output.emplace_back(
|
||||
@@ -488,9 +493,8 @@ bool TSegment::RenderLoft( gfx::vertex_array &Output, Math3D::vector3 const &Ori
|
||||
if( bRender ) {
|
||||
for( int j = 0; j < iNumShapePoints; ++j ) {
|
||||
//łuk z jednym profilem
|
||||
pt = parallel1 * ( ShapePoints[ j ].position.x - fOffsetX ) + pos1;
|
||||
pt = parallel1 * ( jmm1 * ( ShapePoints[ j ].position.x - fOffsetX.first ) + m1 * ( ShapePoints[ j ].position.x - fOffsetX.second ) ) + pos1;
|
||||
pt.y += ShapePoints[ j ].position.y;
|
||||
// pt -= Origin;
|
||||
norm = ShapePoints[ j ].normal.x * parallel1;
|
||||
norm.y += ShapePoints[ j ].normal.y;
|
||||
|
||||
@@ -499,9 +503,8 @@ bool TSegment::RenderLoft( gfx::vertex_array &Output, Math3D::vector3 const &Ori
|
||||
glm::normalize( norm ),
|
||||
glm::vec2 { ShapePoints[ j ].texture.x / texturescale, tv1 } );
|
||||
|
||||
pt = parallel2 * ShapePoints[ j ].position.x + pos2;
|
||||
pt = parallel2 * ( jmm2 * ( ShapePoints[ j ].position.x - fOffsetX.first ) + m2 * ( ShapePoints[ j ].position.x - fOffsetX.second ) ) + pos2;
|
||||
pt.y += ShapePoints[ j ].position.y;
|
||||
// pt -= Origin;
|
||||
norm = ShapePoints[ j ].normal.x * parallel2;
|
||||
norm.y += ShapePoints[ j ].normal.y;
|
||||
|
||||
|
||||
@@ -116,7 +116,7 @@ public:
|
||||
r2 = fRoll2; }
|
||||
|
||||
bool
|
||||
RenderLoft( gfx::vertex_array &Output, Math3D::vector3 const &Origin, gfx::vertex_array const &ShapePoints, int iNumShapePoints, double fTextureLength, double Texturescale = 1.0, int iSkip = 0, int iEnd = 0, float fOffsetX = 0.f, glm::vec3 **p = nullptr, bool bRender = true);
|
||||
RenderLoft( gfx::vertex_array &Output, Math3D::vector3 const &Origin, gfx::vertex_array const &ShapePoints, int iNumShapePoints, double fTextureLength, double Texturescale = 1.0, int iSkip = 0, int iEnd = 0, std::pair<float, float> fOffsetX = {0.f, 0.f}, glm::vec3 **p = nullptr, bool bRender = true );
|
||||
/*
|
||||
void
|
||||
Render();
|
||||
|
||||
219
Texture.cpp
219
Texture.cpp
@@ -16,6 +16,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include "stdafx.h"
|
||||
#include "Texture.h"
|
||||
|
||||
#include "application.h"
|
||||
#include "utilities.h"
|
||||
#include "Globals.h"
|
||||
#include "Logs.h"
|
||||
@@ -159,19 +160,24 @@ void opengl_texture::gles_match_internalformat(GLuint internalformat)
|
||||
void
|
||||
opengl_texture::load() {
|
||||
|
||||
if( name.size() < 3 ) { goto fail; }
|
||||
if( type == "make:" ) {
|
||||
// for generated texture we delay data creation until texture is bound
|
||||
// this ensures the script will receive all simulation data it might potentially want
|
||||
// as any binding will happen after simulation is loaded, initialized and running
|
||||
// until then we supply data for a tiny 2x2 grey stub
|
||||
make_stub();
|
||||
}
|
||||
else {
|
||||
|
||||
WriteLog( "Loading texture data from \"" + name + "\"", logtype::texture );
|
||||
WriteLog( "Loading texture data from \"" + name + type + "\"", logtype::texture );
|
||||
|
||||
data_state = resource_state::loading;
|
||||
{
|
||||
std::string const extension = name.substr( name.size() - 3, 3 );
|
||||
data_state = resource_state::loading;
|
||||
|
||||
if( extension == "dds" ) { load_DDS(); }
|
||||
else if( extension == "tga" ) { load_TGA(); }
|
||||
else if( extension == "png" ) { load_PNG(); }
|
||||
else if( extension == "bmp" ) { load_BMP(); }
|
||||
else if( extension == "tex" ) { load_TEX(); }
|
||||
if( type == ".dds" ) { load_DDS(); }
|
||||
else if( type == ".tga" ) { load_TGA(); }
|
||||
else if( type == ".png" ) { load_PNG(); }
|
||||
else if( type == ".bmp" ) { load_BMP(); }
|
||||
else if( type == ".tex" ) { load_TEX(); }
|
||||
else { goto fail; }
|
||||
}
|
||||
|
||||
@@ -190,7 +196,7 @@ opengl_texture::load() {
|
||||
|
||||
fail:
|
||||
data_state = resource_state::failed;
|
||||
ErrorLog( "Bad texture: failed to load texture \"" + name + "\"" );
|
||||
ErrorLog( "Bad texture: failed to load texture \"" + name + type + "\"" );
|
||||
// NOTE: temporary workaround for texture assignment errors
|
||||
id = 0;
|
||||
return;
|
||||
@@ -243,10 +249,48 @@ void opengl_texture::load_PNG()
|
||||
data_state = resource_state::good;
|
||||
}
|
||||
|
||||
void
|
||||
opengl_texture::make_stub() {
|
||||
|
||||
data_width = 2;
|
||||
data_height = 2;
|
||||
data.resize( data_width * data_height * 3 );
|
||||
std::fill( std::begin( data ), std::end( data ), static_cast<char>( 0xc0 ) );
|
||||
data_mapcount = 1;
|
||||
data_format = GL_RGB;
|
||||
data_components = GL_RGB;
|
||||
data_state = resource_state::good;
|
||||
}
|
||||
|
||||
void
|
||||
opengl_texture::make_request() {
|
||||
|
||||
auto const components { Split( name, '?' ) };
|
||||
auto const query { Split( components.back(), '&' ) };
|
||||
|
||||
PyObject *pythondictionary { nullptr };
|
||||
Application.acquire_python_lock();
|
||||
{
|
||||
pythondictionary = PyDict_New();
|
||||
if( pythondictionary != nullptr ) {
|
||||
for( auto const &querypair : query ) {
|
||||
auto const valuepos { querypair.find( '=' ) };
|
||||
PyDict_SetItemString(
|
||||
pythondictionary,
|
||||
ToLower( querypair.substr( 0, valuepos ) ).c_str(),
|
||||
PyGetString( querypair.substr( valuepos + 1 ).c_str() ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
Application.release_python_lock();
|
||||
|
||||
Application.request( { ToLower( components.front() ), pythondictionary, id } );
|
||||
}
|
||||
|
||||
void
|
||||
opengl_texture::load_BMP() {
|
||||
|
||||
std::ifstream file( name, std::ios::binary ); file.unsetf( std::ios::skipws );
|
||||
std::ifstream file( name + type, std::ios::binary ); file.unsetf( std::ios::skipws );
|
||||
|
||||
BITMAPFILEHEADER header;
|
||||
|
||||
@@ -368,7 +412,7 @@ DDSURFACEDESC2 opengl_texture::deserialize_ddsd(std::istream &s)
|
||||
void
|
||||
opengl_texture::load_DDS() {
|
||||
|
||||
std::ifstream file( name, std::ios::binary | std::ios::ate ); file.unsetf( std::ios::skipws );
|
||||
std::ifstream file( name + type, std::ios::binary | std::ios::ate ); file.unsetf( std::ios::skipws );
|
||||
std::size_t filesize = static_cast<size_t>(file.tellg()); // ios::ate already positioned us at the end of the file
|
||||
file.seekg( 0, std::ios::beg ); // rewind the caret afterwards
|
||||
|
||||
@@ -426,12 +470,12 @@ opengl_texture::load_DDS() {
|
||||
data_width /= 2;
|
||||
data_height /= 2;
|
||||
--data_mapcount;
|
||||
WriteLog( "Texture size exceeds specified limits, skipping mipmap level" );
|
||||
WriteLog( "Texture pixelcount exceeds specified limits, skipping mipmap level" );
|
||||
};
|
||||
|
||||
if( data_mapcount <= 0 ) {
|
||||
// there's a chance we've discarded the provided mipmap(s) as too large
|
||||
WriteLog( "Texture \"" + name + "\" has no mipmaps which can fit currently set texture size limits." );
|
||||
WriteLog( "Texture \"" + name + "\" has no mipmaps which can fit currently set texture pixelcount limits." );
|
||||
data_state = resource_state::failed;
|
||||
return;
|
||||
}
|
||||
@@ -492,7 +536,7 @@ opengl_texture::load_DDS() {
|
||||
void
|
||||
opengl_texture::load_TEX() {
|
||||
|
||||
std::ifstream file( name, std::ios::binary ); file.unsetf( std::ios::skipws );
|
||||
std::ifstream file( name + type, std::ios::binary ); file.unsetf( std::ios::skipws );
|
||||
|
||||
char head[ 5 ];
|
||||
file.read( head, 4 );
|
||||
@@ -539,7 +583,7 @@ opengl_texture::load_TEX() {
|
||||
void
|
||||
opengl_texture::load_TGA() {
|
||||
|
||||
std::ifstream file( name, std::ios::binary ); file.unsetf( std::ios::skipws );
|
||||
std::ifstream file( name + type, std::ios::binary ); file.unsetf( std::ios::skipws );
|
||||
|
||||
// Read the header of the TGA, compare it with the known headers for compressed and uncompressed TGAs
|
||||
unsigned char tgaheader[ 18 ];
|
||||
@@ -955,6 +999,12 @@ opengl_texture::create() {
|
||||
data_state = resource_state::none;
|
||||
}
|
||||
}
|
||||
|
||||
if( type == "make:" ) {
|
||||
// for generated textures send a request to have the actual content of the texture generated
|
||||
make_request();
|
||||
}
|
||||
|
||||
is_ready = true;
|
||||
}
|
||||
|
||||
@@ -971,31 +1021,37 @@ opengl_texture::release() {
|
||||
// if resource move is enabled we don't keep a cpu side copy after upload
|
||||
// so need to re-acquire the data before release
|
||||
// TBD, TODO: instead of vram-ram transfer fetch the data 'normally' from the disk using worker thread
|
||||
::glBindTexture(target, id );
|
||||
GLint datasize {};
|
||||
GLint iscompressed {};
|
||||
::glGetTexLevelParameteriv(target, 0, GL_TEXTURE_COMPRESSED, &iscompressed );
|
||||
if( iscompressed == GL_TRUE ) {
|
||||
// texture is compressed on the gpu side
|
||||
// query texture details needed to perform the backup...
|
||||
::glGetTexLevelParameteriv(target, 0, GL_TEXTURE_INTERNAL_FORMAT, &data_format );
|
||||
::glGetTexLevelParameteriv(target, 0, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &datasize );
|
||||
data.resize( datasize );
|
||||
// ...fetch the data...
|
||||
::glGetCompressedTexImage(target, 0, &data[ 0 ] );
|
||||
if( type == "make:" ) {
|
||||
// auto generated textures only store a stub
|
||||
make_stub();
|
||||
}
|
||||
else {
|
||||
// for whatever reason texture didn't get compressed during upload
|
||||
// fallback on plain rgba storage...
|
||||
data_format = GL_RGBA;
|
||||
data_type = GL_UNSIGNED_BYTE;
|
||||
data.resize( data_width * data_height * 4 );
|
||||
// ...fetch the data...
|
||||
::glGetTexImage(target, 0, data_format, GL_UNSIGNED_BYTE, &data[ 0 ] );
|
||||
::glBindTexture(target, id );
|
||||
GLint datasize {};
|
||||
GLint iscompressed {};
|
||||
::glGetTexLevelParameteriv(target, 0, GL_TEXTURE_COMPRESSED, &iscompressed );
|
||||
if( iscompressed == GL_TRUE ) {
|
||||
// texture is compressed on the gpu side
|
||||
// query texture details needed to perform the backup...
|
||||
::glGetTexLevelParameteriv(target, 0, GL_TEXTURE_INTERNAL_FORMAT, &data_format );
|
||||
::glGetTexLevelParameteriv(target, 0, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &datasize );
|
||||
data.resize( datasize );
|
||||
// ...fetch the data...
|
||||
::glGetCompressedTexImage(target, 0, &data[ 0 ] );
|
||||
}
|
||||
else {
|
||||
// for whatever reason texture didn't get compressed during upload
|
||||
// fallback on plain rgba storage...
|
||||
data_format = GL_RGBA;
|
||||
data_type = GL_UNSIGNED_BYTE;
|
||||
data.resize( data_width * data_height * 4 );
|
||||
// ...fetch the data...
|
||||
::glGetTexImage(target, 0, data_format, GL_UNSIGNED_BYTE, &data[ 0 ] );
|
||||
}
|
||||
// ...and update texture object state
|
||||
data_mapcount = 1; // we keep copy of only top mipmap level
|
||||
data_state = resource_state::good;
|
||||
}
|
||||
// ...and update texture object state
|
||||
data_mapcount = 1; // we keep copy of only top mipmap level
|
||||
data_state = resource_state::good;
|
||||
}
|
||||
// release opengl resources
|
||||
::glDeleteTextures( 1, &id );
|
||||
@@ -1053,7 +1109,7 @@ opengl_texture::downsize( GLuint const Format ) {
|
||||
break;
|
||||
}
|
||||
|
||||
WriteLog( "Texture size exceeds specified limits, downsampling data" );
|
||||
WriteLog( "Texture pixelcount exceeds specified limits, downsampling data" );
|
||||
// trim potential odd texture sizes
|
||||
data_width -= ( data_width % 2 );
|
||||
data_height -= ( data_height % 2 );
|
||||
@@ -1092,49 +1148,78 @@ texture_manager::create(std::string Filename, bool const Loadnow , GLint fh) {
|
||||
if( Filename.find( '|' ) != std::string::npos )
|
||||
Filename.erase( Filename.find( '|' ) ); // po | może być nazwa kolejnej tekstury
|
||||
|
||||
std::pair<std::string, std::string> locator; // resource name, resource type
|
||||
std::string traits;
|
||||
auto const traitpos = Filename.find( ':' );
|
||||
if( traitpos != std::string::npos ) {
|
||||
// po dwukropku mogą być podane dodatkowe informacje niebędące nazwą tekstury
|
||||
if( Filename.size() > traitpos + 1 )
|
||||
traits = Filename.substr( traitpos + 1 );
|
||||
Filename.erase( traitpos );
|
||||
|
||||
// discern textures generated by a script
|
||||
// TBD: support file: for file resources?
|
||||
auto const isgenerated { Filename.find( "make:" ) == 0 };
|
||||
// process supplied resource name
|
||||
if( isgenerated ) {
|
||||
// generated resource
|
||||
// scheme:(user@)path?query
|
||||
|
||||
// remove scheme indicator
|
||||
Filename.erase( 0, Filename.find( ':' ) + 1 );
|
||||
// TODO: extract traits specification from the query
|
||||
// clean up slashes
|
||||
erase_leading_slashes( Filename );
|
||||
}
|
||||
else {
|
||||
// regular file resource
|
||||
// (filepath/)(#)filename.extension(:traits)
|
||||
|
||||
erase_extension( Filename );
|
||||
|
||||
if( Filename[ 0 ] == '/' ) {
|
||||
// filename can potentially begin with a slash, and we don't need it
|
||||
Filename.erase( 0, 1 );
|
||||
// extract trait specifications
|
||||
auto const traitpos = Filename.rfind( ':' );
|
||||
if( traitpos != std::string::npos ) {
|
||||
// po dwukropku mogą być podane dodatkowe informacje niebędące nazwą tekstury
|
||||
if( Filename.size() > traitpos + 1 ) {
|
||||
traits = Filename.substr( traitpos + 1 );
|
||||
}
|
||||
Filename.erase( traitpos );
|
||||
}
|
||||
// potentially trim file type indicator since we check for multiple types
|
||||
erase_extension( Filename );
|
||||
// clean up slashes
|
||||
erase_leading_slashes( Filename );
|
||||
// temporary code for legacy assets -- textures with names beginning with # are to be sharpened
|
||||
if( ( Filename[ 0 ] == '#' )
|
||||
|| ( Filename.find( "/#" ) != std::string::npos ) ) {
|
||||
traits += '#';
|
||||
}
|
||||
}
|
||||
|
||||
// try to locate requested texture in the databank
|
||||
auto lookup { find_in_databank( Filename ) };
|
||||
// TBD, TODO: include trait specification in the resource id in the databank?
|
||||
auto const lookup { find_in_databank( Filename ) };
|
||||
if( lookup != npos ) {
|
||||
return lookup;
|
||||
}
|
||||
// if we don't have the texture in the databank, check if it's on disk
|
||||
auto const disklookup { find_on_disk( Filename ) };
|
||||
|
||||
if( true == disklookup.first.empty() ) {
|
||||
// there's nothing matching in the databank nor on the disk, report failure
|
||||
ErrorLog( "Bad file: failed do locate texture file \"" + Filename + "\"", logtype::file );
|
||||
return npos;
|
||||
// if the lookup fails...
|
||||
if( isgenerated ) {
|
||||
// TODO: verify presence of the generator script
|
||||
locator.first = Filename;
|
||||
locator.second = "make:";
|
||||
}
|
||||
else {
|
||||
// ...for file resources check if it's on disk
|
||||
locator = find_on_disk( Filename );
|
||||
if( true == locator.first.empty() ) {
|
||||
// there's nothing matching in the databank nor on the disk, report failure
|
||||
ErrorLog( "Bad file: failed do locate texture file \"" + Filename + "\"", logtype::file );
|
||||
return npos;
|
||||
}
|
||||
}
|
||||
|
||||
auto texture = new opengl_texture();
|
||||
texture->name = disklookup.first + disklookup.second;
|
||||
if( Filename.find('#') != std::string::npos ) {
|
||||
// temporary code for legacy assets -- textures with names beginning with # are to be sharpened
|
||||
traits += '#';
|
||||
}
|
||||
texture->name = locator.first;
|
||||
texture->type = locator.second;
|
||||
texture->traits = traits;
|
||||
texture->components_hint = fh;
|
||||
auto const textureindex = (texture_handle)m_textures.size();
|
||||
m_textures.emplace_back( texture, std::chrono::steady_clock::time_point() );
|
||||
m_texturemappings.emplace( disklookup.first, textureindex );
|
||||
m_texturemappings.emplace( locator.first, textureindex );
|
||||
|
||||
WriteLog( "Created texture object for \"" + disklookup.first + disklookup.second + "\"", logtype::texture );
|
||||
WriteLog( "Created texture object for \"" + locator.first + "\"", logtype::texture );
|
||||
|
||||
if( true == Loadnow ) {
|
||||
|
||||
|
||||
@@ -53,6 +53,7 @@ struct opengl_texture {
|
||||
bool is_ready{ false }; // indicates the texture was processed and is ready for use
|
||||
std::string traits; // requested texture attributes: wrapping modes etc
|
||||
std::string name; // name of the texture source file
|
||||
std::string type; // type of the texture source file
|
||||
std::size_t size{ 0 }; // size of the texture data, in kb
|
||||
GLint components_hint = 0; // components that material wants
|
||||
|
||||
@@ -61,6 +62,8 @@ struct opengl_texture {
|
||||
|
||||
private:
|
||||
// methods
|
||||
void make_stub();
|
||||
void make_request();
|
||||
void load_BMP();
|
||||
void load_PNG();
|
||||
void load_DDS();
|
||||
@@ -72,8 +75,8 @@ private:
|
||||
void gles_match_internalformat(GLuint format);
|
||||
|
||||
// members
|
||||
bool is_rendertarget; // is used as postfx rendertarget, without loaded data
|
||||
int samples;
|
||||
bool is_rendertarget = false; // is used as postfx rendertarget, without loaded data
|
||||
int samples = 1;
|
||||
|
||||
std::vector<char> data; // texture data (stored GL-style, bottom-left origin)
|
||||
resource_state data_state{ resource_state::none }; // current state of texture data
|
||||
|
||||
408
Track.cpp
408
Track.cpp
@@ -25,6 +25,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include "Timer.h"
|
||||
#include "Logs.h"
|
||||
#include "renderer.h"
|
||||
#include "utilities.h"
|
||||
|
||||
// 101206 Ra: trapezoidalne drogi i tory
|
||||
// 110720 Ra: rozprucie zwrotnicy i odcinki izolowane
|
||||
@@ -42,6 +43,9 @@ const int iProsto3[4] = {1, -1, 2, 1}; // segmenty do jazdy prosto
|
||||
const int iEnds3[13] = {3, 0, 2, 1, 2, 0, -1, 1, 0, 2, 0, 3, 1}; // numer sąsiedniego toru na końcu segmentu "-1"
|
||||
TIsolated *TIsolated::pRoot = NULL;
|
||||
|
||||
TTrack::profiles_array TTrack::m_profiles;
|
||||
TTrack::profiles_map TTrack::m_profilesmap;
|
||||
|
||||
TSwitchExtension::TSwitchExtension(TTrack *owner, int const what)
|
||||
{ // na początku wszystko puste
|
||||
pNexts[0] = nullptr; // wskaźniki do kolejnych odcinków ruchu
|
||||
@@ -523,8 +527,10 @@ void TTrack::Load(cParser *parser, glm::dvec3 const &pOrigin)
|
||||
|
||||
if (iCategoryFlag & 1)
|
||||
{ // zero na główce szyny
|
||||
p1.y += 0.18;
|
||||
p2.y += 0.18;
|
||||
// TODO: delay these calculations unti rail profile and thus height is known
|
||||
auto const railheight { 0.18 };
|
||||
p1.y += railheight;
|
||||
p2.y += railheight;
|
||||
// na przechyłce doliczyć jeszcze pół przechyłki
|
||||
}
|
||||
|
||||
@@ -624,8 +630,10 @@ void TTrack::Load(cParser *parser, glm::dvec3 const &pOrigin)
|
||||
|
||||
if (iCategoryFlag & 1)
|
||||
{ // zero na główce szyny
|
||||
p1.y += 0.18;
|
||||
p2.y += 0.18;
|
||||
// TODO: delay these calculations unti rail profile and thus height is known
|
||||
auto const railheight { 0.18 };
|
||||
p1.y += railheight;
|
||||
p2.y += railheight;
|
||||
// na przechyłce doliczyć jeszcze pół przechyłki?
|
||||
}
|
||||
|
||||
@@ -686,8 +694,10 @@ void TTrack::Load(cParser *parser, glm::dvec3 const &pOrigin)
|
||||
|
||||
if (iCategoryFlag & 1)
|
||||
{ // zero na główce szyny
|
||||
p3.y += 0.18;
|
||||
p4.y += 0.18;
|
||||
// TODO: delay these calculations unti rail profile and thus height is known
|
||||
auto const railheight{ 0.18 };
|
||||
p3.y += railheight;
|
||||
p4.y += railheight;
|
||||
// na przechyłce doliczyć jeszcze pół przechyłki?
|
||||
}
|
||||
|
||||
@@ -883,8 +893,15 @@ void TTrack::Load(cParser *parser, glm::dvec3 const &pOrigin)
|
||||
SwitchExtension->m_material3 = GfxRenderer.Fetch_Material( trackbedtexture );
|
||||
}
|
||||
}
|
||||
else if( str == "railprofile" ) {
|
||||
// rail profile
|
||||
auto const railprofile { parser->getToken<std::string>() };
|
||||
if( iCategoryFlag == 1 ) {
|
||||
m_profile1 = fetch_track_rail_profile( railprofile );
|
||||
}
|
||||
}
|
||||
else
|
||||
ErrorLog("Unknown property: \"" + str + "\" in track \"" + m_name + "\"");
|
||||
ErrorLog("Bad track: unknown property: \"" + str + "\" defined for track \"" + m_name + "\"");
|
||||
parser->getTokens();
|
||||
*parser >> token;
|
||||
str = token;
|
||||
@@ -1016,41 +1033,6 @@ bool TTrack::AddDynamicObject(TDynamicObject *Dynamic)
|
||||
};
|
||||
|
||||
const int numPts = 4;
|
||||
const int nnumPts = 12;
|
||||
|
||||
// szyna - vextor6(x,y,mapowanie tekstury,xn,yn,zn)
|
||||
// tę wersję opracował Tolein (bez pochylenia)
|
||||
// TODO: profile definitions in external files
|
||||
gfx::basic_vertex const szyna[ nnumPts ] = {
|
||||
{{ 0.111f, -0.180f, 0.f}, { 1.000f, 0.000f, 0.f}, {0.00f, 0.f}},
|
||||
{{ 0.046f, -0.150f, 0.f}, { 0.707f, 0.707f, 0.f}, {0.15f, 0.f}},
|
||||
{{ 0.044f, -0.050f, 0.f}, { 0.707f, -0.707f, 0.f}, {0.25f, 0.f}},
|
||||
{{ 0.073f, -0.038f, 0.f}, { 0.707f, -0.707f, 0.f}, {0.35f, 0.f}},
|
||||
{{ 0.072f, -0.010f, 0.f}, { 0.707f, 0.707f, 0.f}, {0.40f, 0.f}},
|
||||
{{ 0.052f, -0.000f, 0.f}, { 0.000f, 1.000f, 0.f}, {0.45f, 0.f}},
|
||||
{{ 0.020f, -0.000f, 0.f}, { 0.000f, 1.000f, 0.f}, {0.55f, 0.f}},
|
||||
{{ 0.000f, -0.010f, 0.f}, {-0.707f, 0.707f, 0.f}, {0.60f, 0.f}},
|
||||
{{-0.001f, -0.038f, 0.f}, {-0.707f, -0.707f, 0.f}, {0.65f, 0.f}},
|
||||
{{ 0.028f, -0.050f, 0.f}, {-0.707f, -0.707f, 0.f}, {0.75f, 0.f}},
|
||||
{{ 0.026f, -0.150f, 0.f}, {-0.707f, 0.707f, 0.f}, {0.85f, 0.f}},
|
||||
{{-0.039f, -0.180f, 0.f}, {-1.000f, 0.000f, 0.f}, {1.00f, 0.f}} };
|
||||
|
||||
// iglica - vextor3(x,y,mapowanie tekstury)
|
||||
// 1 mm więcej, żeby nie nachodziły tekstury?
|
||||
// TODO: automatic generation from base profile TBD: reuse base profile?
|
||||
gfx::basic_vertex const iglica[ nnumPts ] = {
|
||||
{{ 0.010f, -0.180f, 0.f}, { 1.000f, 0.000f, 0.f}, {0.00f, 0.f}},
|
||||
{{ 0.010f, -0.155f, 0.f}, { 1.000f, 0.000f, 0.f}, {0.15f, 0.f}},
|
||||
{{ 0.010f, -0.070f, 0.f}, { 1.000f, 0.000f, 0.f}, {0.25f, 0.f}},
|
||||
{{ 0.010f, -0.040f, 0.f}, { 1.000f, 0.000f, 0.f}, {0.35f, 0.f}},
|
||||
{{ 0.010f, -0.010f, 0.f}, { 1.000f, 0.000f, 0.f}, {0.40f, 0.f}},
|
||||
{{ 0.010f, -0.000f, 0.f}, { 0.707f, 0.707f, 0.f}, {0.45f, 0.f}},
|
||||
{{ 0.000f, -0.000f, 0.f}, { 0.707f, 0.707f, 0.f}, {0.55f, 0.f}},
|
||||
{{ 0.000f, -0.010f, 0.f}, {-1.000f, 0.000f, 0.f}, {0.60f, 0.f}},
|
||||
{{ 0.000f, -0.040f, 0.f}, {-1.000f, 0.000f, 0.f}, {0.65f, 0.f}},
|
||||
{{ 0.000f, -0.070f, 0.f}, {-1.000f, 0.000f, 0.f}, {0.75f, 0.f}},
|
||||
{{ 0.000f, -0.155f, 0.f}, {-0.707f, 0.707f, 0.f}, {0.85f, 0.f}},
|
||||
{{-0.040f, -0.180f, 0.f}, {-1.000f, 0.000f, 0.f}, {1.00f, 0.f}} };
|
||||
|
||||
bool TTrack::CheckDynamicObject(TDynamicObject *Dynamic)
|
||||
{ // sprawdzenie, czy pojazd jest przypisany do toru
|
||||
@@ -1182,6 +1164,7 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) {
|
||||
}
|
||||
if (m_material1)
|
||||
{ // szyny - generujemy dwie, najwyżej rysować się będzie jedną
|
||||
auto const nnumPts { track_rail_profile( m_profile1.second ).size() / 2 };
|
||||
auto const texturelength { texture_length( m_material1 ) };
|
||||
gfx::vertex_array vertices;
|
||||
if( ( Bank != 0 ) && ( true == Geometry1.empty() ) ) {
|
||||
@@ -1208,34 +1191,39 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) {
|
||||
create_track_blade_profile( rpts3, rpts4 );
|
||||
// TODO, TBD: change all track geometry to triangles, to allow packing data in less, larger buffers
|
||||
auto const bladelength { static_cast<int>( std::ceil( SwitchExtension->Segments[ 0 ]->RaSegCount() * 0.65 ) ) };
|
||||
auto const nnumPts { track_rail_profile( m_profile1.second ).size() / 2 };
|
||||
if (SwitchExtension->RightSwitch)
|
||||
{ // nowa wersja z SPKS, ale odwrotnie lewa/prawa
|
||||
gfx::vertex_array vertices;
|
||||
if( m_material1 ) {
|
||||
auto const texturelength { texture_length( m_material1 ) };
|
||||
// fixed parts
|
||||
SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts2, nnumPts, texturelength );
|
||||
// left blade
|
||||
// composed from two parts: transition from blade to regular rail, and regular rail
|
||||
SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts3, -nnumPts, texturelength, 1.0, 0, bladelength / 2, { SwitchExtension->fOffset2, SwitchExtension->fOffset2 / 2 } );
|
||||
SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts1, nnumPts, texturelength, 1.0, bladelength / 2, bladelength, { SwitchExtension->fOffset2 / 2, 0.f } );
|
||||
Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) );
|
||||
vertices.clear();
|
||||
// fixed parts
|
||||
SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts1, nnumPts, texturelength, 1.0, bladelength );
|
||||
Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) );
|
||||
vertices.clear();
|
||||
// left blade
|
||||
SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts3, -nnumPts, texturelength, 1.0, 0, bladelength, SwitchExtension->fOffset2 );
|
||||
SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts2, nnumPts, texturelength );
|
||||
Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) );
|
||||
vertices.clear();
|
||||
}
|
||||
if( m_material2 ) {
|
||||
auto const texturelength { texture_length( m_material2 ) };
|
||||
// fixed parts
|
||||
SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts1, nnumPts, texturelength );
|
||||
// right blade
|
||||
// composed from two parts: transition from blade to regular rail, and regular rail
|
||||
SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts4, -nnumPts, texturelength, 1.0, 0, bladelength / 2, { -fMaxOffset + SwitchExtension->fOffset1, ( -fMaxOffset + SwitchExtension->fOffset1 ) / 2 } );
|
||||
SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts2, nnumPts, texturelength, 1.0, bladelength / 2, bladelength, { ( -fMaxOffset + SwitchExtension->fOffset1 ) / 2, 0.f } );
|
||||
Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) );
|
||||
vertices.clear();
|
||||
// fixed parts
|
||||
SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts2, nnumPts, texturelength, 1.0, bladelength );
|
||||
Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) );
|
||||
vertices.clear();
|
||||
// right blade
|
||||
SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts4, -nnumPts, texturelength, 1.0, 0, bladelength, -fMaxOffset + SwitchExtension->fOffset1 );
|
||||
SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts1, nnumPts, texturelength );
|
||||
Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) );
|
||||
vertices.clear();
|
||||
}
|
||||
@@ -1245,29 +1233,33 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) {
|
||||
gfx::vertex_array vertices;
|
||||
if( m_material1 ) {
|
||||
auto const texturelength { texture_length( m_material1 ) };
|
||||
// fixed parts
|
||||
SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts1, nnumPts, texturelength ); // lewa szyna normalna cała
|
||||
// right blade
|
||||
// composed from two parts: transition from blade to regular rail, and regular rail
|
||||
SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts4, -nnumPts, texturelength, 1.0, 0, bladelength / 2, { -SwitchExtension->fOffset2, -SwitchExtension->fOffset2 / 2 } );
|
||||
SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts2, nnumPts, texturelength, 1.0, bladelength / 2, bladelength, { -SwitchExtension->fOffset2 / 2, 0.f } );
|
||||
Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) );
|
||||
vertices.clear();
|
||||
// fixed parts
|
||||
SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts2, nnumPts, texturelength, 1.0, bladelength ); // prawa szyna za iglicą
|
||||
Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) );
|
||||
vertices.clear();
|
||||
// right blade
|
||||
SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts4, -nnumPts, texturelength, 1.0, 0, bladelength, -SwitchExtension->fOffset2 );
|
||||
SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts1, nnumPts, texturelength ); // lewa szyna normalna cała
|
||||
Geometry1.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) );
|
||||
vertices.clear();
|
||||
}
|
||||
if( m_material2 ) {
|
||||
auto const texturelength { texture_length( m_material2 ) };
|
||||
// fixed parts
|
||||
SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts2, nnumPts, texturelength ); // prawa szyna normalnie cała
|
||||
// left blade
|
||||
// composed from two parts: transition from blade to regular rail, and regular rail
|
||||
SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts3, -nnumPts, texturelength, 1.0, 0, bladelength / 2, { fMaxOffset - SwitchExtension->fOffset1, ( fMaxOffset - SwitchExtension->fOffset1 ) / 2 } );
|
||||
SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts1, nnumPts, texturelength, 1.0, bladelength / 2, bladelength, { ( fMaxOffset - SwitchExtension->fOffset1 ) / 2, 0.f } );
|
||||
Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) );
|
||||
vertices.clear();
|
||||
// fixed parts
|
||||
SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts1, nnumPts, texturelength, 1.0, bladelength ); // lewa szyna za iglicą
|
||||
Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) );
|
||||
vertices.clear();
|
||||
// left blade
|
||||
SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts3, -nnumPts, texturelength, 1.0, 0, bladelength, fMaxOffset - SwitchExtension->fOffset1 );
|
||||
SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts2, nnumPts, texturelength ); // prawa szyna normalnie cała
|
||||
Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) );
|
||||
vertices.clear();
|
||||
}
|
||||
@@ -1408,22 +1400,22 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) {
|
||||
if (SwitchExtension->iRoads == 4)
|
||||
{ // pobocza do trapezowatej nawierzchni - dodatkowe punkty z drugiej strony odcinka
|
||||
if( ( fTexHeight1 >= 0.0 ) || ( side != 0.0 ) ) {
|
||||
SwitchExtension->Segments[ 2 ]->RenderLoft( vertices, m_origin, rpts2, -3, texturelength, 1.0, 0, 0, 0.0, &b, render );
|
||||
SwitchExtension->Segments[ 2 ]->RenderLoft( vertices, m_origin, rpts2, -3, texturelength, 1.0, 0, 0, {}, &b, render );
|
||||
if( true == render ) {
|
||||
Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) );
|
||||
vertices.clear();
|
||||
}
|
||||
SwitchExtension->Segments[ 3 ]->RenderLoft( vertices, m_origin, rpts2, -3, texturelength, 1.0, 0, 0, 0.0, &b, render );
|
||||
SwitchExtension->Segments[ 3 ]->RenderLoft( vertices, m_origin, rpts2, -3, texturelength, 1.0, 0, 0, {}, &b, render );
|
||||
if( true == render ) {
|
||||
Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) );
|
||||
vertices.clear();
|
||||
}
|
||||
SwitchExtension->Segments[ 4 ]->RenderLoft( vertices, m_origin, rpts2, -3, texturelength, 1.0, 0, 0, 0.0, &b, render );
|
||||
SwitchExtension->Segments[ 4 ]->RenderLoft( vertices, m_origin, rpts2, -3, texturelength, 1.0, 0, 0, {}, &b, render );
|
||||
if( true == render ) {
|
||||
Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) );
|
||||
vertices.clear();
|
||||
}
|
||||
SwitchExtension->Segments[ 5 ]->RenderLoft( vertices, m_origin, rpts2, -3, texturelength, 1.0, 0, 0, 0.0, &b, render );
|
||||
SwitchExtension->Segments[ 5 ]->RenderLoft( vertices, m_origin, rpts2, -3, texturelength, 1.0, 0, 0, {}, &b, render );
|
||||
if( true == render ) {
|
||||
Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) );
|
||||
vertices.clear();
|
||||
@@ -1433,17 +1425,17 @@ void TTrack::create_geometry( gfx::geometrybank_handle const &Bank ) {
|
||||
else {
|
||||
// punkt 3 pokrywa się z punktem 1, jak w zwrotnicy; połączenie 1->2 nie musi być prostoliniowe
|
||||
if( ( fTexHeight1 >= 0.0 ) || ( side != 0.0 ) ) {
|
||||
SwitchExtension->Segments[ 2 ]->RenderLoft( vertices, m_origin, rpts2, -3, texturelength, 1.0, 0, 0, 0.0, &b, render ); // z P2 do P4
|
||||
SwitchExtension->Segments[ 2 ]->RenderLoft( vertices, m_origin, rpts2, -3, texturelength, 1.0, 0, 0, {}, &b, render ); // z P2 do P4
|
||||
if( true == render ) {
|
||||
Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) );
|
||||
vertices.clear();
|
||||
}
|
||||
SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts2, -3, texturelength, 1.0, 0, 0, 0.0, &b, render ); // z P4 do P3=P1 (odwrócony)
|
||||
SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts2, -3, texturelength, 1.0, 0, 0, {}, &b, render ); // z P4 do P3=P1 (odwrócony)
|
||||
if( true == render ) {
|
||||
Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) );
|
||||
vertices.clear();
|
||||
}
|
||||
SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts2, -3, texturelength, 1.0, 0, 0, 0.0, &b, render ); // z P1 do P2
|
||||
SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts2, -3, texturelength, 1.0, 0, 0, {}, &b, render ); // z P1 do P2
|
||||
if( true == render ) {
|
||||
Geometry2.emplace_back( GfxRenderer.Insert( vertices, Bank, GL_TRIANGLE_STRIP ) );
|
||||
vertices.clear();
|
||||
@@ -1805,24 +1797,31 @@ TTrack * TTrack::RaAnimate()
|
||||
&& ( ( false == Geometry1.empty() )
|
||||
|| ( false == Geometry2.empty() ) ) ) {
|
||||
// iglice liczone tylko dla zwrotnic
|
||||
gfx::vertex_array rpts1, rpts2;
|
||||
create_track_rail_profile( rpts1, rpts2 );
|
||||
gfx::vertex_array rpts3, rpts4;
|
||||
create_track_blade_profile( rpts3, rpts4 );
|
||||
gfx::vertex_array vertices;
|
||||
auto const bladelength { static_cast<int>( std::ceil( SwitchExtension->Segments[ 0 ]->RaSegCount() * 0.65 ) ) };
|
||||
auto const nnumPts { track_rail_profile( m_profile1.second ).size() / 2 };
|
||||
if (SwitchExtension->RightSwitch)
|
||||
{ // nowa wersja z SPKS, ale odwrotnie lewa/prawa
|
||||
if( m_material1 ) {
|
||||
auto const texturelength { texture_length( m_material1 ) };
|
||||
// left blade
|
||||
SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts3, -nnumPts, texturelength, 1.0, 0, bladelength, SwitchExtension->fOffset2 );
|
||||
GfxRenderer.Replace( vertices, Geometry1[ 2 ], GL_TRIANGLE_STRIP );
|
||||
// composed from two parts: transition from blade to regular rail, and regular rail
|
||||
SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts3, -nnumPts, texturelength, 1.0, 0, bladelength / 2, { SwitchExtension->fOffset2, SwitchExtension->fOffset2 / 2 } );
|
||||
SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts1, nnumPts, texturelength, 1.0, bladelength / 2, bladelength, { SwitchExtension->fOffset2 / 2, 0.f } );
|
||||
GfxRenderer.Replace( vertices, Geometry1[ 0 ], GL_TRIANGLE_STRIP );
|
||||
vertices.clear();
|
||||
}
|
||||
if( m_material2 ) {
|
||||
auto const texturelength { texture_length( m_material2 ) };
|
||||
// right blade
|
||||
SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts4, -nnumPts, texturelength, 1.0, 0, bladelength, -fMaxOffset + SwitchExtension->fOffset1 );
|
||||
GfxRenderer.Replace( vertices, Geometry2[ 2 ], GL_TRIANGLE_STRIP );
|
||||
// composed from two parts: transition from blade to regular rail, and regular rail
|
||||
SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts4, -nnumPts, texturelength, 1.0, 0, bladelength / 2, { -fMaxOffset + SwitchExtension->fOffset1, ( -fMaxOffset + SwitchExtension->fOffset1 ) / 2 } );
|
||||
SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts2, nnumPts, texturelength, 1.0, bladelength / 2, bladelength, { ( -fMaxOffset + SwitchExtension->fOffset1 ) / 2, 0.f } );
|
||||
GfxRenderer.Replace( vertices, Geometry2[ 0 ], GL_TRIANGLE_STRIP );
|
||||
vertices.clear();
|
||||
}
|
||||
}
|
||||
@@ -1830,15 +1829,19 @@ TTrack * TTrack::RaAnimate()
|
||||
if( m_material1 ) {
|
||||
auto const texturelength { texture_length( m_material1 ) };
|
||||
// right blade
|
||||
SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts4, -nnumPts, texturelength, 1.0, 0, bladelength, -SwitchExtension->fOffset2 );
|
||||
GfxRenderer.Replace( vertices, Geometry1[ 2 ], GL_TRIANGLE_STRIP );
|
||||
// composed from two parts: transition from blade to regular rail, and regular rail
|
||||
SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts4, -nnumPts, texturelength, 1.0, 0, bladelength / 2, { -SwitchExtension->fOffset2, -SwitchExtension->fOffset2 / 2 } );
|
||||
SwitchExtension->Segments[ 0 ]->RenderLoft( vertices, m_origin, rpts2, nnumPts, texturelength, 1.0, bladelength / 2, bladelength, { -SwitchExtension->fOffset2 / 2, 0.f } );
|
||||
GfxRenderer.Replace( vertices, Geometry1[ 0 ], GL_TRIANGLE_STRIP );
|
||||
vertices.clear();
|
||||
}
|
||||
if( m_material2 ) {
|
||||
auto const texturelength { texture_length( m_material2 ) };
|
||||
// left blade
|
||||
SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts3, -nnumPts, texturelength, 1.0, 0, bladelength, fMaxOffset - SwitchExtension->fOffset1 );
|
||||
GfxRenderer.Replace( vertices, Geometry2[ 2 ], GL_TRIANGLE_STRIP );
|
||||
// composed from two parts: transition from blade to regular rail, and regular rail
|
||||
SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts3, -nnumPts, texturelength, 1.0, 0, bladelength / 2, { fMaxOffset - SwitchExtension->fOffset1, ( fMaxOffset - SwitchExtension->fOffset1 ) / 2 } );
|
||||
SwitchExtension->Segments[ 1 ]->RenderLoft( vertices, m_origin, rpts1, nnumPts, texturelength, 1.0, bladelength / 2, bladelength, { ( fMaxOffset - SwitchExtension->fOffset1 ) / 2, 0.f } );
|
||||
GfxRenderer.Replace( vertices, Geometry2[ 0 ], GL_TRIANGLE_STRIP );
|
||||
vertices.clear();
|
||||
}
|
||||
}
|
||||
@@ -2171,12 +2174,122 @@ TTrack::export_as_text_( std::ostream &Output ) const {
|
||||
if( fVerticalRadius != 0.f ) {
|
||||
Output << "vradius " << fVerticalRadius << ' ';
|
||||
}
|
||||
if( ( eType == tt_Switch )
|
||||
&& ( SwitchExtension->m_material3 != null_handle ) ) {
|
||||
auto texturefile { GfxRenderer.Material( m_material2 ).name };
|
||||
if( texturefile.find( szTexturePath ) == 0 ) {
|
||||
// don't include 'textures/' in the path
|
||||
texturefile.erase( 0, std::string{ szTexturePath }.size() );
|
||||
}
|
||||
Output << "trackbed " << texturefile << ' ';
|
||||
}
|
||||
// footer
|
||||
Output
|
||||
<< "endtrack"
|
||||
<< "\n";
|
||||
}
|
||||
|
||||
// locates specified profile in the profile database, potentially loading it from a file
|
||||
// returns: pair <profile name, profile handle>
|
||||
std::pair<std::string, int>
|
||||
TTrack::fetch_track_rail_profile( std::string const &Profile ) {
|
||||
|
||||
auto const railprofilepath { std::string( szModelPath ) + "tory/railprofile_" };
|
||||
auto const railkeyprefix { std::string( "rail_" ) };
|
||||
|
||||
if( m_profiles.empty() ) {
|
||||
// ensure the default profile is always first in the database
|
||||
fetch_default_profiles();
|
||||
}
|
||||
// try to locate specified rail profile...
|
||||
auto const lookup { m_profilesmap.find( railkeyprefix + Profile ) };
|
||||
if( lookup != m_profilesmap.end() ) {
|
||||
// ...if it works, we're done...
|
||||
return { Profile, lookup->second };
|
||||
}
|
||||
// ... and if it fails try to add the profile to the database from a data file
|
||||
auto profilehandle { 0 }; // fallback link to default profile if loading it fails
|
||||
auto profiledata { deserialize_profile( railprofilepath + Profile ) };
|
||||
if( false == profiledata.empty() ) {
|
||||
// if we get the profile data add it to the database and calculate a link to it
|
||||
profilehandle = static_cast<int>( m_profiles.size() );
|
||||
m_profiles.emplace_back( profiledata );
|
||||
}
|
||||
m_profilesmap.emplace( railkeyprefix + Profile, profilehandle );
|
||||
return { Profile, profilehandle };
|
||||
}
|
||||
|
||||
void
|
||||
TTrack::fetch_default_profiles() {
|
||||
|
||||
if( false == m_profiles.empty() ) { return; }
|
||||
|
||||
auto const railprofilepath { std::string( szModelPath ) + "tory/railprofile_" };
|
||||
auto const railkeyprefix { std::string( "rail_" ) };
|
||||
|
||||
m_profiles.emplace_back( deserialize_profile( railprofilepath + "default" ) );
|
||||
if( m_profiles.back().empty() ) {
|
||||
// fallback to prevent utter start failure, supply legacy track profile
|
||||
m_profiles.back() = {
|
||||
// szyna - vextor6(x,y,mapowanie tekstury,xn,yn,zn)
|
||||
// tę wersję opracował Tolein (bez pochylenia)
|
||||
{{ 0.111f, -0.180f, 0.f}, { 1.000f, 0.000f, 0.f}, {0.00f, 0.f}},
|
||||
{{ 0.046f, -0.150f, 0.f}, { 0.707f, 0.707f, 0.f}, {0.15f, 0.f}},
|
||||
{{ 0.044f, -0.050f, 0.f}, { 0.707f, -0.707f, 0.f}, {0.25f, 0.f}},
|
||||
{{ 0.073f, -0.038f, 0.f}, { 0.707f, -0.707f, 0.f}, {0.35f, 0.f}},
|
||||
{{ 0.072f, -0.010f, 0.f}, { 0.707f, 0.707f, 0.f}, {0.40f, 0.f}},
|
||||
{{ 0.052f, -0.000f, 0.f}, { 0.000f, 1.000f, 0.f}, {0.45f, 0.f}},
|
||||
{{ 0.020f, -0.000f, 0.f}, { 0.000f, 1.000f, 0.f}, {0.55f, 0.f}},
|
||||
{{ 0.000f, -0.010f, 0.f}, {-0.707f, 0.707f, 0.f}, {0.60f, 0.f}},
|
||||
{{-0.001f, -0.038f, 0.f}, {-0.707f, -0.707f, 0.f}, {0.65f, 0.f}},
|
||||
{{ 0.028f, -0.050f, 0.f}, {-0.707f, -0.707f, 0.f}, {0.75f, 0.f}},
|
||||
{{ 0.026f, -0.150f, 0.f}, {-0.707f, 0.707f, 0.f}, {0.85f, 0.f}},
|
||||
{{-0.039f, -0.180f, 0.f}, {-1.000f, 0.000f, 0.f}, {1.00f, 0.f}},
|
||||
// iglica - vextor3(x,y,mapowanie tekstury)
|
||||
// 1 mm więcej, żeby nie nachodziły tekstury?
|
||||
{{ 0.010f, -0.180f, 0.f}, { 1.000f, 0.000f, 0.f}, {0.00f, 0.f}},
|
||||
{{ 0.010f, -0.155f, 0.f}, { 1.000f, 0.000f, 0.f}, {0.15f, 0.f}},
|
||||
{{ 0.010f, -0.070f, 0.f}, { 1.000f, 0.000f, 0.f}, {0.25f, 0.f}},
|
||||
{{ 0.010f, -0.040f, 0.f}, { 1.000f, 0.000f, 0.f}, {0.35f, 0.f}},
|
||||
{{ 0.010f, -0.010f, 0.f}, { 1.000f, 0.000f, 0.f}, {0.40f, 0.f}},
|
||||
{{ 0.010f, -0.000f, 0.f}, { 0.707f, 0.707f, 0.f}, {0.45f, 0.f}},
|
||||
{{ 0.000f, -0.000f, 0.f}, { 0.707f, 0.707f, 0.f}, {0.55f, 0.f}},
|
||||
{{ 0.000f, -0.010f, 0.f}, {-1.000f, 0.000f, 0.f}, {0.60f, 0.f}},
|
||||
{{ 0.000f, -0.040f, 0.f}, {-1.000f, 0.000f, 0.f}, {0.65f, 0.f}},
|
||||
{{ 0.000f, -0.070f, 0.f}, {-1.000f, 0.000f, 0.f}, {0.75f, 0.f}},
|
||||
{{ 0.000f, -0.155f, 0.f}, {-0.707f, 0.707f, 0.f}, {0.85f, 0.f}},
|
||||
{{-0.040f, -0.180f, 0.f}, {-1.000f, 0.000f, 0.f}, {1.00f, 0.f}} };
|
||||
}
|
||||
m_profilesmap.emplace( railkeyprefix + "default", 0 );
|
||||
}
|
||||
|
||||
gfx::vertex_array
|
||||
TTrack::deserialize_profile( std::string const &Profile ) {
|
||||
|
||||
gfx::vertex_array profiledata;
|
||||
|
||||
cParser input { Profile + ".txt", cParser::buffer_FILE };
|
||||
|
||||
while( input.getTokens( 5, true, "\n\r\t ;,{}" ) ) {
|
||||
gfx::basic_vertex vertex;
|
||||
input
|
||||
>> vertex.position.x
|
||||
>> vertex.position.y
|
||||
>> vertex.normal.x
|
||||
>> vertex.normal.y
|
||||
>> vertex.texture.s;
|
||||
profiledata.emplace_back( vertex );
|
||||
}
|
||||
|
||||
return profiledata;
|
||||
}
|
||||
|
||||
gfx::vertex_array const &
|
||||
TTrack::track_rail_profile( int const Profile ) {
|
||||
|
||||
return m_profiles[ Profile ];
|
||||
}
|
||||
|
||||
float
|
||||
TTrack::texture_length( material_handle const Material ) {
|
||||
|
||||
@@ -2325,66 +2438,70 @@ TTrack::create_track_rail_profile( gfx::vertex_array &Right, gfx::vertex_array &
|
||||
sin2 { std::sin( roll2 ) },
|
||||
cos2 { std::cos( roll2 ) };
|
||||
|
||||
auto const pointcount { iTrapezoid == 0 ? 12 : 24 };
|
||||
Right.resize( pointcount );
|
||||
Left.resize( pointcount );
|
||||
auto const &railprofile { track_rail_profile( m_profile1.second ) };
|
||||
// NOTE: rail profile defines both regular rail and switch blade profiles, so we halve total point count
|
||||
auto const pointcount { railprofile.size() / 2 };
|
||||
Right.resize( pointcount * ( iTrapezoid == 0 ? 1 : 2 ) );
|
||||
Left.resize( pointcount * ( iTrapezoid == 0 ? 1 : 2 ) );
|
||||
|
||||
for( int i = 0; i < 12; ++i ) {
|
||||
auto const *szyna { railprofile.data() };
|
||||
|
||||
for( int i = 0; i < pointcount; ++i ) {
|
||||
|
||||
Right[ i ] = {
|
||||
// position
|
||||
{( fHTW + szyna[ i ].position.x ) * cos1 + szyna[ i ].position.y * sin1,
|
||||
-( fHTW + szyna[ i ].position.x ) * sin1 + szyna[ i ].position.y * cos1,
|
||||
szyna[ i ].position.z},
|
||||
// normal
|
||||
{ szyna[ i ].normal.x * cos1 + szyna[ i ].normal.y * sin1,
|
||||
-szyna[ i ].normal.x * sin1 + szyna[ i ].normal.y * cos1,
|
||||
szyna[ i ].normal.z },
|
||||
// texture
|
||||
{ szyna[ i ].texture.x,
|
||||
szyna[ i ].texture.y } };
|
||||
szyna[ i ].position.z},
|
||||
// normal
|
||||
{ szyna[ i ].normal.x * cos1 + szyna[ i ].normal.y * sin1,
|
||||
-szyna[ i ].normal.x * sin1 + szyna[ i ].normal.y * cos1,
|
||||
szyna[ i ].normal.z },
|
||||
// texture
|
||||
{ szyna[ i ].texture.s,
|
||||
szyna[ i ].texture.t } };
|
||||
|
||||
Left[ 11 - i ] = {
|
||||
Left[ pointcount - 1 - i ] = {
|
||||
// position
|
||||
{(-fHTW - szyna[ i ].position.x ) * cos1 + szyna[ i ].position.y * sin1,
|
||||
-(-fHTW - szyna[ i ].position.x ) * sin1 + szyna[ i ].position.y * cos1,
|
||||
szyna[ i ].position.z},
|
||||
// normal
|
||||
{-szyna[ i ].normal.x * cos1 + szyna[ i ].normal.y * sin1,
|
||||
szyna[ i ].normal.x * sin1 + szyna[ i ].normal.y * cos1,
|
||||
szyna[ i ].normal.z },
|
||||
// texture
|
||||
{ szyna[ i ].texture.x,
|
||||
szyna[ i ].texture.y } };
|
||||
szyna[ i ].position.z},
|
||||
// normal
|
||||
{-szyna[ i ].normal.x * cos1 + szyna[ i ].normal.y * sin1,
|
||||
szyna[ i ].normal.x * sin1 + szyna[ i ].normal.y * cos1,
|
||||
szyna[ i ].normal.z },
|
||||
// texture
|
||||
{ szyna[ i ].texture.s,
|
||||
szyna[ i ].texture.t } };
|
||||
|
||||
if( iTrapezoid == 0 ) { continue; }
|
||||
// trapez albo przechyłki, to oddzielne punkty na końcu
|
||||
|
||||
Right[ 12 + i ] = {
|
||||
// trapez albo przechyłki, to oddzielne punkty na końcu
|
||||
Right[ pointcount + i ] = {
|
||||
// position
|
||||
{( fHTW + szyna[ i ].position.x ) * cos2 + szyna[ i ].position.y * sin2,
|
||||
-( fHTW + szyna[ i ].position.x ) * sin2 + szyna[ i ].position.y * cos2,
|
||||
szyna[ i ].position.z},
|
||||
// normal
|
||||
{ szyna[ i ].normal.x * cos2 + szyna[ i ].normal.y * sin2,
|
||||
-szyna[ i ].normal.x * sin2 + szyna[ i ].normal.y * cos2,
|
||||
szyna[ i ].normal.z },
|
||||
// texture
|
||||
{ szyna[ i ].texture.x,
|
||||
szyna[ i ].texture.y } };
|
||||
szyna[ i ].position.z},
|
||||
// normal
|
||||
{ szyna[ i ].normal.x * cos2 + szyna[ i ].normal.y * sin2,
|
||||
-szyna[ i ].normal.x * sin2 + szyna[ i ].normal.y * cos2,
|
||||
szyna[ i ].normal.z },
|
||||
// texture
|
||||
{ szyna[ i ].texture.s,
|
||||
szyna[ i ].texture.t } };
|
||||
|
||||
Left[ 23 - i ] = {
|
||||
Left[ pointcount * 2 - 1 - i ] = {
|
||||
// position
|
||||
{(-fHTW - szyna[ i ].position.x ) * cos2 + szyna[ i ].position.y * sin2,
|
||||
-(-fHTW - szyna[ i ].position.x ) * sin2 + szyna[ i ].position.y * cos2,
|
||||
szyna[ i ].position.z},
|
||||
// normal
|
||||
{-szyna[ i ].normal.x * cos2 + szyna[ i ].normal.y * sin2,
|
||||
szyna[ i ].normal.x * sin2 + szyna[ i ].normal.y * cos2,
|
||||
szyna[ i ].normal.z },
|
||||
// texture
|
||||
{ szyna[ i ].texture.x,
|
||||
szyna[ i ].texture.y } };
|
||||
szyna[ i ].position.z},
|
||||
// normal
|
||||
{-szyna[ i ].normal.x * cos2 + szyna[ i ].normal.y * sin2,
|
||||
szyna[ i ].normal.x * sin2 + szyna[ i ].normal.y * cos2,
|
||||
szyna[ i ].normal.z },
|
||||
// texture
|
||||
{ szyna[ i ].texture.s,
|
||||
szyna[ i ].texture.t } };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2412,37 +2529,43 @@ TTrack::create_track_blade_profile( gfx::vertex_array &Right, gfx::vertex_array
|
||||
sin2 { std::sin( roll2 ) },
|
||||
cos2 { std::cos( roll2 ) };
|
||||
|
||||
auto const pointcount { 24 };
|
||||
Right.resize( pointcount );
|
||||
Left.resize( pointcount );
|
||||
auto const &railprofile { track_rail_profile( m_profile1.second ) };
|
||||
// NOTE: rail profile defines both regular rail and switch blade profiles, so we halve total point count
|
||||
auto const pointcount { railprofile.size() / 2 };
|
||||
|
||||
Right.resize( pointcount * 2 );
|
||||
Left.resize( pointcount * 2 );
|
||||
|
||||
auto const *szyna { railprofile.data() };
|
||||
auto const *iglica { szyna + pointcount };
|
||||
|
||||
glm::vec3 const flipxvalue { -1, 1, 1 };
|
||||
for( int i = 0; i < 12; ++i ) {
|
||||
for( int i = 0; i < pointcount; ++i ) {
|
||||
|
||||
Right[ i ] = {
|
||||
{+( fHTW + iglica[ i ].position.x ) * cos1 + iglica[ i ].position.y * sin1,
|
||||
-( fHTW + iglica[ i ].position.x ) * sin1 + iglica[ i ].position.y * cos1,
|
||||
0.f},
|
||||
{iglica[ i ].normal},
|
||||
{iglica[ i ].texture.x, 0.f} };
|
||||
Right[ i + 12 ] = {
|
||||
{iglica[ i ].texture.s, 0.f} };
|
||||
Right[ i + pointcount ] = {
|
||||
{+( fHTW2 + szyna[ i ].position.x ) * cos2 + szyna[ i ].position.y * sin2,
|
||||
-( fHTW2 + szyna[ i ].position.x ) * sin2 + iglica[ i ].position.y * cos2,
|
||||
-( fHTW2 + szyna[ i ].position.x ) * sin2 + szyna[ i ].position.y * cos2,
|
||||
0.f},
|
||||
{szyna[ i ].normal},
|
||||
{szyna[ i ].texture.x, 0.f} };
|
||||
Left[ 11 - i ] = {
|
||||
{szyna[ i ].texture.s, 0.f} };
|
||||
Left[ pointcount - 1 - i ] = {
|
||||
{ ( -fHTW - iglica[ i ].position.x ) * cos1 + iglica[ i ].position.y * sin1,
|
||||
-( -fHTW - iglica[ i ].position.x ) * sin1 + iglica[ i ].position.y * cos1,
|
||||
0.f},
|
||||
{iglica[ i ].normal * flipxvalue},
|
||||
{iglica[ i ].texture.x, 0.f} };
|
||||
Left[ 23 - i ] = {
|
||||
{iglica[ i ].texture.s, 0.f} };
|
||||
Left[ pointcount * 2 - 1 - i ] = {
|
||||
{ ( -fHTW2 - szyna[ i ].position.x ) * cos2 + szyna[ i ].position.y * sin2,
|
||||
-( -fHTW2 - szyna[ i ].position.x ) * sin2 + iglica[ i ].position.y * cos2,
|
||||
-( -fHTW2 - szyna[ i ].position.x ) * sin2 + szyna[ i ].position.y * cos2,
|
||||
0.f},
|
||||
{szyna[ i ].normal * flipxvalue},
|
||||
{szyna[ i ].texture.x, 0.f} };
|
||||
{szyna[ i ].texture.s, 0.f} };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2526,7 +2649,7 @@ TTrack::create_track_bed_profile( gfx::vertex_array &Output, TTrack const *Previ
|
||||
Output.resize( pointcount );
|
||||
// potentially retrieve texture length override from the assigned material
|
||||
auto const texturelength { texture_length( copy_adjacent_trackbed_material() ) };
|
||||
auto const railheight { 0.18f };
|
||||
auto const railheight { std::abs( track_rail_profile( m_profile1.second ).front().position.y ) };
|
||||
if( texturelength == 4.f ) {
|
||||
// stare mapowanie z różną gęstością pikseli i oddzielnymi teksturami na każdy profil
|
||||
auto const normalx = std::cos( glm::radians( 75.f ) );
|
||||
@@ -3080,7 +3203,7 @@ path_table::InitTracks() {
|
||||
auto const trackname { track->name() };
|
||||
|
||||
switch (track->eType) {
|
||||
// TODO: re-enable
|
||||
|
||||
case tt_Table: {
|
||||
// obrotnicę też łączymy na starcie z innymi torami
|
||||
// szukamy modelu o tej samej nazwie
|
||||
@@ -3175,10 +3298,6 @@ path_table::InitTracks() {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if( Global.CreateSwitchTrackbeds ) {
|
||||
// when autogenerating trackbeds, try to restore trackbeds for tracks neighbouring double slips
|
||||
track->copy_adjacent_trackbed_material();
|
||||
}
|
||||
break;
|
||||
}
|
||||
case tt_Switch: {
|
||||
@@ -3186,10 +3305,6 @@ path_table::InitTracks() {
|
||||
track->AssignForcedEvents(
|
||||
simulation::Events.FindEvent( trackname + ":forced+" ),
|
||||
simulation::Events.FindEvent( trackname + ":forced-" ) );
|
||||
if( Global.CreateSwitchTrackbeds ) {
|
||||
// when autogenerating trackbeds, try to restore trackbeds for tracks neighbouring double slips
|
||||
track->copy_adjacent_trackbed_material();
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
@@ -3205,6 +3320,25 @@ path_table::InitTracks() {
|
||||
}
|
||||
}
|
||||
|
||||
if( Global.CreateSwitchTrackbeds ) {
|
||||
// do this after all connections are established, otherwise missing switch connections
|
||||
// may prevent us from obtaining texture data for basic track from 'across' a switch
|
||||
for( auto *track : m_items ) {
|
||||
// try to assign missing trackbed materials for switches and tracks neighbouring switches
|
||||
switch( track->eType ) {
|
||||
|
||||
case tt_Normal:
|
||||
case tt_Switch: {
|
||||
track->copy_adjacent_trackbed_material();
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
break;
|
||||
}
|
||||
} // switch
|
||||
}
|
||||
}
|
||||
|
||||
auto *isolated = TIsolated::Root();
|
||||
while( isolated ) {
|
||||
|
||||
@@ -3226,6 +3360,8 @@ path_table::InitTracks() {
|
||||
|
||||
isolated = isolated->Next();
|
||||
}
|
||||
|
||||
TTrack::fetch_default_profiles();
|
||||
}
|
||||
|
||||
// legacy method, sends list of occupied paths over network
|
||||
|
||||
16
Track.h
16
Track.h
@@ -167,8 +167,10 @@ private:
|
||||
float fTexSlope = 0.9f;
|
||||
|
||||
glm::dvec3 m_origin;
|
||||
// TODO: store material names as strings, for lossless serialization and export
|
||||
material_handle m_material1 = 0; // tekstura szyn albo nawierzchni
|
||||
material_handle m_material2 = 0; // tekstura automatycznej podsypki albo pobocza
|
||||
std::pair<std::string, int> m_profile1 {}; // profile of geometry chunks textured with texture 1
|
||||
using geometryhandle_sequence = std::vector<gfx::geometry_handle>;
|
||||
geometryhandle_sequence Geometry1; // geometry chunks textured with texture 1
|
||||
geometryhandle_sequence Geometry2; // geometry chunks textured with texture 2
|
||||
@@ -293,8 +295,13 @@ public:
|
||||
double VelocityGet();
|
||||
void ConnectionsLog();
|
||||
bool DoubleSlip() const;
|
||||
static void fetch_default_profiles();
|
||||
|
||||
private:
|
||||
// types
|
||||
using profiles_array = std::vector<gfx::vertex_array>;
|
||||
using profiles_map = std::unordered_map<std::string, int>;
|
||||
// methods
|
||||
// radius() subclass details, calculates node's bounding radius
|
||||
float radius_();
|
||||
// serialize() subclass details, sends content of the subclass to provided stream
|
||||
@@ -303,6 +310,12 @@ private:
|
||||
void deserialize_( std::istream &Input );
|
||||
// export() subclass details, sends basic content of the class in legacy (text) format to provided stream
|
||||
void export_as_text_( std::ostream &Output ) const;
|
||||
// locates specified profile in the profile database, potentially loading it from a file
|
||||
static std::pair<std::string, int> fetch_track_rail_profile( std::string const &Profile );
|
||||
// loads content of specified file and converts it into a vertex array
|
||||
static gfx::vertex_array deserialize_profile( std::string const &Profile );
|
||||
// provides direct access to vertex data of specified profile
|
||||
static gfx::vertex_array const & track_rail_profile( int const Profile );
|
||||
// returns texture length for specified material
|
||||
float texture_length( material_handle const Material );
|
||||
// creates profile for a part of current path
|
||||
@@ -312,6 +325,9 @@ private:
|
||||
void create_track_bed_profile( gfx::vertex_array &Output, TTrack const *Previous, TTrack const *Next );
|
||||
void create_road_profile( gfx::vertex_array &Output, bool const Forcetransition = false );
|
||||
void create_road_side_profile( gfx::vertex_array &Right, gfx::vertex_array &Left, gfx::vertex_array const &Road, bool const Forcetransition = false );
|
||||
// members
|
||||
static profiles_array m_profiles; // shared database of path element profiles
|
||||
static profiles_map m_profilesmap;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -452,11 +452,11 @@ bool TTrain::Init(TDynamicObject *NewDynamicObject, bool e3d)
|
||||
PyObject *TTrain::GetTrainState() {
|
||||
|
||||
auto const *mover = DynamicObject->MoverParameters;
|
||||
PyEval_AcquireLock();
|
||||
Application.acquire_python_lock();
|
||||
auto *dict = PyDict_New();
|
||||
if( ( dict == nullptr )
|
||||
|| ( mover == nullptr ) ) {
|
||||
PyEval_ReleaseLock();
|
||||
Application.release_python_lock();
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
@@ -586,7 +586,7 @@ PyObject *TTrain::GetTrainState() {
|
||||
PyDict_SetItemString( dict, "seconds", PyGetInt( simulation::Time.second() ) );
|
||||
PyDict_SetItemString( dict, "air_temperature", PyGetInt( Global.AirTemperature ) );
|
||||
|
||||
PyEval_ReleaseLock();
|
||||
Application.release_python_lock();
|
||||
return dict;
|
||||
}
|
||||
|
||||
@@ -6004,7 +6004,7 @@ bool TTrain::Update( double const Deltatime )
|
||||
&& ( false == FreeFlyModeFlag ) ) { // don't bother if we're outside
|
||||
fScreenTimer = 0.f;
|
||||
for( auto const &screen : m_screens ) {
|
||||
Application.request( { screen.first, GetTrainState(), screen.second } );
|
||||
Application.request( { screen.first, GetTrainState(), GfxRenderer.Texture( screen.second ).id } );
|
||||
}
|
||||
}
|
||||
// sounds
|
||||
|
||||
@@ -174,12 +174,27 @@ eu07_application::run() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// issues request for a worker thread to perform specified task. returns: true if task was scheduled
|
||||
bool
|
||||
eu07_application::request( python_taskqueue::task_request const &Task ) {
|
||||
|
||||
return m_taskqueue.insert( Task );
|
||||
}
|
||||
|
||||
// ensures the main thread holds the python gil and can safely execute python calls
|
||||
void
|
||||
eu07_application::acquire_python_lock() {
|
||||
|
||||
m_taskqueue.acquire_lock();
|
||||
}
|
||||
|
||||
// frees the python gil and swaps out the main thread
|
||||
void
|
||||
eu07_application::release_python_lock() {
|
||||
|
||||
m_taskqueue.release_lock();
|
||||
}
|
||||
|
||||
void
|
||||
eu07_application::exit() {
|
||||
|
||||
|
||||
@@ -30,8 +30,15 @@ public:
|
||||
init( int Argc, char *Argv[] );
|
||||
int
|
||||
run();
|
||||
// issues request for a worker thread to perform specified task. returns: true if task was scheduled
|
||||
bool
|
||||
request( python_taskqueue::task_request const &Task );
|
||||
// ensures the main thread holds the python gil and can safely execute python calls
|
||||
void
|
||||
acquire_python_lock();
|
||||
// frees the python gil and swaps out the main thread
|
||||
void
|
||||
release_python_lock();
|
||||
void
|
||||
exit();
|
||||
void
|
||||
|
||||
106
material.cpp
106
material.cpp
@@ -308,62 +308,85 @@ material_manager::create( std::string const &Filename, bool const Loadnow ) {
|
||||
if( filename.find( '|' ) != std::string::npos )
|
||||
filename.erase( filename.find( '|' ) ); // po | może być nazwa kolejnej tekstury
|
||||
|
||||
erase_extension( filename );
|
||||
// discern references to textures generated by a script
|
||||
// TBD: support file: for file resources?
|
||||
auto const isgenerated { filename.find( "make:" ) == 0 };
|
||||
|
||||
if( filename[ 0 ] == '/' ) {
|
||||
// filename can potentially begin with a slash, and we don't need it
|
||||
filename.erase( 0, 1 );
|
||||
// process supplied resource name
|
||||
if( isgenerated ) {
|
||||
// generated resource
|
||||
// scheme:(user@)path?query
|
||||
|
||||
// remove scheme indicator
|
||||
filename.erase( 0, filename.find(':') + 1 );
|
||||
// TBD, TODO: allow shader specification as part of the query?
|
||||
erase_leading_slashes( filename );
|
||||
}
|
||||
else {
|
||||
// regular file resource
|
||||
// (filepath/)filename.extension
|
||||
|
||||
erase_extension( filename );
|
||||
replace_slashes( filename );
|
||||
erase_leading_slashes( filename );
|
||||
}
|
||||
|
||||
// try to locate requested material in the databank
|
||||
auto const databanklookup { find_in_databank( filename ) };
|
||||
auto const databanklookup { find_in_databank( ToLower( filename ) ) };
|
||||
if( databanklookup != null_handle ) {
|
||||
return databanklookup;
|
||||
}
|
||||
// if this fails, try to look for it on disk
|
||||
|
||||
opengl_material material;
|
||||
auto const disklookup { find_on_disk( filename ) };
|
||||
if( false == disklookup.first.empty() ) {
|
||||
cParser materialparser( disklookup.first + disklookup.second, cParser::buffer_FILE );
|
||||
if( false == material.deserialize( materialparser, Loadnow ) ) {
|
||||
// deserialization failed but the .mat file does exist, so we give up at this point
|
||||
return null_handle;
|
||||
material_handle materialhandle { null_handle };
|
||||
|
||||
auto const locator {
|
||||
isgenerated ?
|
||||
std::make_pair( filename, "make:" ) :
|
||||
find_on_disk( filename ) };
|
||||
|
||||
if( ( false == isgenerated )
|
||||
&& ( false == locator.first.empty() ) ) {
|
||||
// try to parse located file resource
|
||||
cParser materialparser( locator.first + locator.second, cParser::buffer_FILE );
|
||||
if( true == material.deserialize( materialparser, Loadnow ) ) {
|
||||
material.name = locator.first;
|
||||
}
|
||||
material.name = disklookup.first;
|
||||
}
|
||||
else {
|
||||
// if there's no .mat file, this could be legacy method of referring just to diffuse texture directly, make a material out of it in such case
|
||||
// if there's no .mat file, this can be either autogenerated texture,
|
||||
// or legacy method of referring just to diffuse texture directly.
|
||||
// wrap basic material around it in either case
|
||||
material.textures[0] = GfxRenderer.Fetch_Texture( Filename, Loadnow );
|
||||
if( material.textures[0] == null_handle ) {
|
||||
// if there's also no texture, give up
|
||||
return null_handle;
|
||||
}
|
||||
if( material.textures[0] != null_handle )
|
||||
{
|
||||
// use texture path and name to tell the newly created materials apart
|
||||
material.name = GfxRenderer.Texture( material.textures[0] ).name;
|
||||
|
||||
// material would attach default shader anyway, but it would spit to error log
|
||||
try
|
||||
{
|
||||
material.shader = GfxRenderer.Fetch_Shader("default_1");
|
||||
// material would attach default shader anyway, but it would spit to error log
|
||||
try
|
||||
{
|
||||
material.shader = GfxRenderer.Fetch_Shader("default_1");
|
||||
}
|
||||
catch (gl::shader_exception const &e)
|
||||
{
|
||||
ErrorLog("invalid shader: " + std::string(e.what()));
|
||||
}
|
||||
}
|
||||
catch (gl::shader_exception const &e)
|
||||
{
|
||||
ErrorLog("invalid shader: " + std::string(e.what()));
|
||||
}
|
||||
|
||||
// use texture path and name to tell the newly created materials apart
|
||||
filename = GfxRenderer.Texture( material.textures[0] ).name;
|
||||
erase_extension( filename );
|
||||
material.name = filename;
|
||||
}
|
||||
|
||||
material.finalize(Loadnow);
|
||||
if( false == material.name.empty() ) {
|
||||
// if we have material name and shader it means resource was processed succesfully
|
||||
material.finalize(Loadnow);
|
||||
materialhandle = m_materials.size();
|
||||
m_materials.emplace_back( std::move(material) );
|
||||
m_materialmappings.emplace( material.name, materialhandle );
|
||||
}
|
||||
else {
|
||||
// otherwise record our failure to process the resource, to speed up subsequent attempts
|
||||
m_materialmappings.emplace( filename, materialhandle );
|
||||
}
|
||||
|
||||
if (!material.shader)
|
||||
return null_handle;
|
||||
|
||||
material_handle handle = m_materials.size();
|
||||
m_materialmappings.emplace(material.name, handle);
|
||||
m_materials.emplace_back( std::move(material) );
|
||||
return handle;
|
||||
return materialhandle;
|
||||
};
|
||||
|
||||
// checks whether specified material is in the material bank. returns handle to the material, or a null handle
|
||||
@@ -390,9 +413,10 @@ material_manager::find_in_databank( std::string const &Materialname ) const {
|
||||
std::pair<std::string, std::string>
|
||||
material_manager::find_on_disk( std::string const &Materialname ) const {
|
||||
|
||||
auto const materialname { ToLower( Materialname ) };
|
||||
return (
|
||||
FileExists(
|
||||
{ Global.asCurrentTexturePath + Materialname, Materialname, szTexturePath + Materialname },
|
||||
{ Global.asCurrentTexturePath + materialname, materialname, szTexturePath + materialname },
|
||||
{ ".mat" } ) );
|
||||
}
|
||||
|
||||
|
||||
2581
renderer.cpp
2581
renderer.cpp
File diff suppressed because it is too large
Load Diff
13
scene.cpp
13
scene.cpp
@@ -1175,16 +1175,21 @@ basic_region::RadioStop( glm::dvec3 const &Location ) {
|
||||
}
|
||||
|
||||
std::vector<std::string> switchtrackbedtextures {
|
||||
"rkpd34r190-tpd1",
|
||||
"rkpd34r190-tpd2",
|
||||
"rkpd34r190-tpd-oil2",
|
||||
"rozkrz8r150-1pods-new",
|
||||
"rozkrz8r150-2pods-new",
|
||||
"rozkrz34r150-tpbps-new2",
|
||||
"rozkrz34r150-tpd1",
|
||||
"rkpd34r190-tpd1",
|
||||
"rkpd34r190-tpd2",
|
||||
"rkpd34r190-tpd-oil2",
|
||||
"rz-1200-185",
|
||||
"zwr41r500",
|
||||
"zwrot-tpd-oil1",
|
||||
"zwrot34r300pods-new" };
|
||||
"zwrot34r300pods",
|
||||
"zwrot34r300pods-new",
|
||||
"zwrot34r300pods-old",
|
||||
"zwrotl65r1200pods-new",
|
||||
"zwrotp65r1200pods-new" };
|
||||
|
||||
void
|
||||
basic_region::insert( shape_node Shape, scratch_data &Scratchpad, bool const Transform ) {
|
||||
|
||||
@@ -476,6 +476,13 @@ sound_source::stop( bool const Skipend ) {
|
||||
void
|
||||
sound_source::update( audio::openal_source &Source ) {
|
||||
|
||||
if( ( m_owner != nullptr )
|
||||
&& ( false == m_owner->bEnabled ) ) {
|
||||
// terminate the sound if the owner is gone
|
||||
// TBD, TODO: replace with a listener pattern to receive vehicle removal and cab change events and such?
|
||||
m_stop = true;
|
||||
}
|
||||
|
||||
if( sound( sound_id::main ).buffer != null_handle ) {
|
||||
// basic variant: single main sound, with optional bookends
|
||||
update_basic( Source );
|
||||
|
||||
@@ -384,6 +384,14 @@ erase_extension( std::string &Filename ) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void
|
||||
erase_leading_slashes( std::string &Filename ) {
|
||||
|
||||
while( Filename[ 0 ] == '/' ) {
|
||||
Filename.erase( 0, 1 );
|
||||
}
|
||||
}
|
||||
|
||||
// potentially replaces backward slashes in provided file path with unix-compatible forward slashes
|
||||
void
|
||||
replace_slashes( std::string &Filename ) {
|
||||
|
||||
@@ -31,6 +31,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
#define szModelPath "models/"
|
||||
#define szDynamicPath "dynamic/"
|
||||
#define szSoundPath "sounds/"
|
||||
#define szDataPath "data/"
|
||||
|
||||
#define MAKE_ID4(a,b,c,d) (((std::uint32_t)(d)<<24)|((std::uint32_t)(c)<<16)|((std::uint32_t)(b)<<8)|(std::uint32_t)(a))
|
||||
|
||||
@@ -189,6 +190,10 @@ std::time_t last_modified( std::string const &Filename );
|
||||
bool
|
||||
erase_extension( std::string &Filename );
|
||||
|
||||
// potentially erase leading slashes from provided file path
|
||||
void
|
||||
erase_leading_slashes( std::string &Filename );
|
||||
|
||||
// potentially replaces backward slashes in provided file path with unix-compatible forward slashes
|
||||
void
|
||||
replace_slashes( std::string &Filename );
|
||||
|
||||
Reference in New Issue
Block a user