mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-03-22 15:05:03 +01:00
basic texture generation support, python multithreading fixes
This commit is contained in:
@@ -453,7 +453,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 ))
|
||||
{
|
||||
|
||||
60
PyInt.cpp
60
PyInt.cpp
@@ -27,7 +27,7 @@ void render_task::run() {
|
||||
if( ( outputwidth != nullptr )
|
||||
&& ( outputheight != nullptr ) ) {
|
||||
|
||||
::glBindTexture( GL_TEXTURE_2D, GfxRenderer.Texture( m_target ).id );
|
||||
::glBindTexture( GL_TEXTURE_2D, m_target );
|
||||
// setup texture parameters
|
||||
::glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE );
|
||||
::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
|
||||
@@ -74,8 +74,6 @@ auto python_taskqueue::init() -> bool {
|
||||
#endif
|
||||
Py_Initialize();
|
||||
PyEval_InitThreads();
|
||||
// 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) {
|
||||
@@ -101,8 +99,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" );
|
||||
|
||||
@@ -110,12 +108,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;
|
||||
@@ -131,8 +128,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 ) {
|
||||
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
|
||||
@@ -140,8 +137,7 @@ void python_taskqueue::exit() {
|
||||
task->cancel();
|
||||
}
|
||||
// take a bow
|
||||
PyEval_AcquireLock();
|
||||
PyThreadState_Swap( m_mainthread );
|
||||
acquire_lock();
|
||||
Py_Finalize();
|
||||
}
|
||||
|
||||
@@ -150,7 +146,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; }
|
||||
@@ -164,11 +160,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;
|
||||
@@ -202,6 +198,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 ) };
|
||||
@@ -213,7 +221,7 @@ auto python_taskqueue::fetch_renderer( std::string const Renderer ) ->PyObject *
|
||||
auto const file { Renderer.substr( path.size() ) };
|
||||
PyObject *renderer { nullptr };
|
||||
PyObject *rendererarguments { nullptr };
|
||||
PyEval_AcquireLock();
|
||||
acquire_lock();
|
||||
{
|
||||
if( m_main == nullptr ) {
|
||||
ErrorLog( "Python Renderer: __main__ module is missing" );
|
||||
@@ -246,7 +254,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;
|
||||
@@ -279,14 +287,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
@@ -29,7 +29,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
|
||||
@@ -41,9 +41,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:
|
||||
@@ -52,7 +54,7 @@ public:
|
||||
|
||||
std::string const &renderer;
|
||||
PyObject *input;
|
||||
texture_handle target;
|
||||
GLuint target;
|
||||
};
|
||||
// constructors
|
||||
python_taskqueue() = default;
|
||||
@@ -65,11 +67,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 *;
|
||||
|
||||
219
Texture.cpp
219
Texture.cpp
@@ -19,6 +19,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include <ddraw.h>
|
||||
#include "GL/glew.h"
|
||||
|
||||
#include "application.h"
|
||||
#include "utilities.h"
|
||||
#include "Globals.h"
|
||||
#include "Logs.h"
|
||||
@@ -37,18 +38,23 @@ texture_manager::texture_manager() {
|
||||
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 == "bmp" ) { load_BMP(); }
|
||||
else if( extension == "tex" ) { load_TEX(); }
|
||||
if( type == ".dds" ) { load_DDS(); }
|
||||
else if( type == ".tga" ) { load_TGA(); }
|
||||
else if( type == ".bmp" ) { load_BMP(); }
|
||||
else if( type == ".tex" ) { load_TEX(); }
|
||||
else { goto fail; }
|
||||
}
|
||||
|
||||
@@ -67,16 +73,54 @@ 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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
@@ -198,7 +242,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
|
||||
|
||||
@@ -256,12 +300,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;
|
||||
}
|
||||
@@ -316,7 +360,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 );
|
||||
@@ -363,7 +407,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 ];
|
||||
@@ -604,6 +648,12 @@ opengl_texture::create() {
|
||||
data = std::vector<char>();
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -620,30 +670,36 @@ 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( GL_TEXTURE_2D, id );
|
||||
GLint datasize {};
|
||||
GLint iscompressed {};
|
||||
::glGetTexLevelParameteriv( GL_TEXTURE_2D, 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( GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &data_format );
|
||||
::glGetTexLevelParameteriv( GL_TEXTURE_2D, 0, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &datasize );
|
||||
data.resize( datasize );
|
||||
// ...fetch the data...
|
||||
::glGetCompressedTexImage( GL_TEXTURE_2D, 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.resize( data_width * data_height * 4 );
|
||||
// ...fetch the data...
|
||||
::glGetTexImage( GL_TEXTURE_2D, 0, data_format, GL_UNSIGNED_BYTE, &data[ 0 ] );
|
||||
::glBindTexture( GL_TEXTURE_2D, id );
|
||||
GLint datasize{};
|
||||
GLint iscompressed{};
|
||||
::glGetTexLevelParameteriv( GL_TEXTURE_2D, 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( GL_TEXTURE_2D, 0, GL_TEXTURE_INTERNAL_FORMAT, &data_format );
|
||||
::glGetTexLevelParameteriv( GL_TEXTURE_2D, 0, GL_TEXTURE_COMPRESSED_IMAGE_SIZE, &datasize );
|
||||
data.resize( datasize );
|
||||
// ...fetch the data...
|
||||
::glGetCompressedTexImage( GL_TEXTURE_2D, 0, &data[ 0 ] );
|
||||
}
|
||||
else {
|
||||
// for whatever reason texture didn't get compressed during upload
|
||||
// fallback on plain rgba storage...
|
||||
data_format = GL_RGBA;
|
||||
data.resize( data_width * data_height * 4 );
|
||||
// ...fetch the data...
|
||||
::glGetTexImage( GL_TEXTURE_2D, 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 );
|
||||
@@ -697,7 +753,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 );
|
||||
@@ -754,48 +810,79 @@ texture_manager::create( std::string Filename, bool const Loadnow ) {
|
||||
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 );
|
||||
}
|
||||
|
||||
erase_extension( Filename );
|
||||
replace_slashes( Filename );
|
||||
if( Filename[ 0 ] == '/' ) {
|
||||
// filename can potentially begin with a slash, and we don't need it
|
||||
Filename.erase( 0, 1 );
|
||||
}
|
||||
// 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
|
||||
replace_slashes( Filename );
|
||||
erase_leading_slashes( Filename );
|
||||
}
|
||||
else {
|
||||
// regular file resource
|
||||
// (filepath/)(#)filename.extension(:traits)
|
||||
|
||||
// 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
|
||||
replace_slashes( Filename );
|
||||
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;
|
||||
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 ) {
|
||||
|
||||
|
||||
@@ -47,10 +47,13 @@ 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
|
||||
|
||||
private:
|
||||
// methods
|
||||
void make_stub();
|
||||
void make_request();
|
||||
void load_BMP();
|
||||
void load_DDS();
|
||||
void load_TEX();
|
||||
|
||||
@@ -448,11 +448,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;
|
||||
}
|
||||
|
||||
@@ -582,7 +582,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;
|
||||
}
|
||||
|
||||
@@ -5993,7 +5993,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
|
||||
|
||||
@@ -151,12 +151,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
|
||||
|
||||
87
material.cpp
87
material.cpp
@@ -117,47 +117,75 @@ 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 );
|
||||
replace_slashes( filename );
|
||||
if( filename[ 0 ] == '/' ) {
|
||||
// filename can potentially begin with a slash, and we don't need it
|
||||
filename.erase( 0, 1 );
|
||||
// discern references to 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 );
|
||||
// TBD, TODO: allow shader specification as part of the query?
|
||||
replace_slashes( filename );
|
||||
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.texture1 = GfxRenderer.Fetch_Texture( Filename, Loadnow );
|
||||
if( material.texture1 == null_handle ) {
|
||||
// if there's also no texture, give up
|
||||
return null_handle;
|
||||
if( material.texture1 != null_handle ) {
|
||||
// use texture path and name to tell the newly created materials apart
|
||||
material.name = GfxRenderer.Texture( material.texture1 ).name;
|
||||
material.has_alpha = GfxRenderer.Texture( material.texture1 ).has_alpha;
|
||||
}
|
||||
// use texture path and name to tell the newly created materials apart
|
||||
filename = GfxRenderer.Texture( material.texture1 ).name;
|
||||
erase_extension( filename );
|
||||
material.name = filename;
|
||||
material.has_alpha = GfxRenderer.Texture( material.texture1 ).has_alpha;
|
||||
}
|
||||
|
||||
material_handle handle = m_materials.size();
|
||||
m_materials.emplace_back( material );
|
||||
m_materialmappings.emplace( material.name, handle );
|
||||
return handle;
|
||||
if( false == material.name.empty() ) {
|
||||
// if we have material name it means resource was processed succesfully
|
||||
materialhandle = m_materials.size();
|
||||
m_materials.emplace_back( 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 );
|
||||
}
|
||||
|
||||
return materialhandle;
|
||||
};
|
||||
|
||||
// checks whether specified material is in the material bank. returns handle to the material, or a null handle
|
||||
@@ -184,9 +212,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" } ) );
|
||||
}
|
||||
|
||||
|
||||
@@ -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 ) {
|
||||
|
||||
@@ -30,6 +30,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))
|
||||
|
||||
@@ -188,6 +189,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