basic texture generation support, python multithreading fixes

This commit is contained in:
tmj-fstate
2018-10-21 01:29:27 +02:00
parent b51d5c3d2b
commit b36ed8db6c
11 changed files with 298 additions and 130 deletions

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

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

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

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

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