16
0
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:
milek7
2018-10-25 13:37:57 +02:00
26 changed files with 2056 additions and 1669 deletions

View File

@@ -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)

View File

@@ -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

View File

@@ -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 != "" );

View File

@@ -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 };

View File

@@ -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;
}

View File

@@ -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,

View File

@@ -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 ) {

View File

@@ -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

View File

@@ -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
View File

@@ -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 *;

View File

@@ -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;

View File

@@ -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();

View File

@@ -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 ) {

View File

@@ -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
View File

@@ -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
View File

@@ -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;
};

View File

@@ -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

View File

@@ -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() {

View File

@@ -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

View File

@@ -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" } ) );
}

File diff suppressed because it is too large Load Diff

View File

@@ -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 ) {

View File

@@ -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 );

View File

@@ -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 ) {

View File

@@ -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 );

View File

@@ -1 +1 @@
#define VERSION_INFO "M7 (GL3) 14.10.2018, based on milek-87d95b2d, tmj-81c9238"
#define VERSION_INFO "M7 (GL3) 25.10.2018, based on milek-98b167a, tmj-b36ed8d"