mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-03-22 15:05:03 +01:00
python interpreter refactored to a task queue
This commit is contained in:
@@ -70,4 +70,6 @@ enum class TCommandType
|
||||
cm_Command // komenda pobierana z komórki
|
||||
};
|
||||
|
||||
using material_handle = int;
|
||||
|
||||
#endif
|
||||
|
||||
817
PyInt.cpp
817
PyInt.cpp
@@ -1,24 +1,56 @@
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "PyInt.h"
|
||||
|
||||
#include "globals.h"
|
||||
#include "parser.h"
|
||||
#include "application.h"
|
||||
#include "renderer.h"
|
||||
#include "Model3d.h"
|
||||
#include "Train.h"
|
||||
#include "Logs.h"
|
||||
|
||||
TPythonInterpreter *TPythonInterpreter::_instance = NULL;
|
||||
void render_task::run() {
|
||||
// call the renderer
|
||||
auto *output { PyObject_CallMethod( m_renderer, "render", "O", m_input ) };
|
||||
Py_DECREF( m_input );
|
||||
|
||||
//#define _PY_INT_MORE_LOG
|
||||
if( output != nullptr ) {
|
||||
auto *outputwidth { PyObject_CallMethod( m_renderer, "get_width", nullptr ) };
|
||||
auto *outputheight { PyObject_CallMethod( m_renderer, "get_height", nullptr ) };
|
||||
// upload texture data
|
||||
if( ( outputwidth != nullptr )
|
||||
&& ( outputheight != nullptr ) ) {
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic ignored "-Wwrite-strings"
|
||||
#endif
|
||||
GfxRenderer.Bind_Material( m_target );
|
||||
// setup texture parameters
|
||||
::glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE );
|
||||
::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
|
||||
::glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
|
||||
::glTexEnvf( GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, -1.0 );
|
||||
// build texture
|
||||
::glTexImage2D(
|
||||
GL_TEXTURE_2D, 0,
|
||||
GL_RGBA8,
|
||||
PyInt_AsLong( outputwidth ), PyInt_AsLong( outputheight ), 0,
|
||||
GL_RGB, GL_UNSIGNED_BYTE, reinterpret_cast<GLubyte const *>( PyString_AsString( output ) ) );
|
||||
}
|
||||
Py_DECREF( outputheight );
|
||||
Py_DECREF( outputwidth );
|
||||
Py_DECREF( output );
|
||||
}
|
||||
// clean up after yourself
|
||||
delete this;
|
||||
}
|
||||
|
||||
// initializes the module. returns true on success
|
||||
auto python_taskqueue::init() -> bool {
|
||||
|
||||
TPythonInterpreter::TPythonInterpreter()
|
||||
{
|
||||
WriteLog("Loading Python ...");
|
||||
#ifdef _WIN32
|
||||
if (sizeof(void*) == 8)
|
||||
Py_SetPythonHome("python64");
|
||||
@@ -31,568 +63,253 @@ TPythonInterpreter::TPythonInterpreter()
|
||||
Py_SetPythonHome("linuxpython");
|
||||
#endif
|
||||
Py_Initialize();
|
||||
_main = PyImport_ImportModule("__main__");
|
||||
if (_main == NULL)
|
||||
{
|
||||
WriteLog("Cannot import Python module __main__");
|
||||
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) {
|
||||
ErrorLog( "Python Interpreter: __main__ module is missing" );
|
||||
goto release_and_exit;
|
||||
}
|
||||
PyObject *cStringModule = PyImport_ImportModule("cStringIO");
|
||||
_stdErr = NULL;
|
||||
if (cStringModule == NULL)
|
||||
return;
|
||||
PyObject *cStringClassName = PyObject_GetAttrString(cStringModule, "StringIO");
|
||||
if (cStringClassName == NULL)
|
||||
return;
|
||||
PyObject *cString = PyObject_CallObject(cStringClassName, NULL);
|
||||
if (cString == NULL)
|
||||
return;
|
||||
if (PySys_SetObject("stderr", cString) != 0)
|
||||
return;
|
||||
_stdErr = cString;
|
||||
|
||||
auto *stringiomodule { PyImport_ImportModule( "cStringIO" ) };
|
||||
auto *stringioclassname { (
|
||||
stringiomodule != nullptr ?
|
||||
PyObject_GetAttrString( stringiomodule, "StringIO" ) :
|
||||
nullptr ) };
|
||||
auto *stringioobject { (
|
||||
stringioclassname != nullptr ?
|
||||
PyObject_CallObject( stringioclassname, nullptr ) :
|
||||
nullptr ) };
|
||||
m_error = { (
|
||||
stringioobject == nullptr ? nullptr :
|
||||
PySys_SetObject( "stderr", stringioobject ) != 0 ? nullptr :
|
||||
stringioobject ) };
|
||||
|
||||
if( m_error == nullptr ) { goto release_and_exit; }
|
||||
|
||||
if( false == run_file( "abstractscreenrenderer" ) ) { goto release_and_exit; }
|
||||
|
||||
// release the lock
|
||||
PyEval_ReleaseLock();
|
||||
|
||||
WriteLog( "Python Interpreter setup complete" );
|
||||
|
||||
// init workers
|
||||
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 ) );
|
||||
|
||||
if( worker == nullptr ) { return false; }
|
||||
}
|
||||
|
||||
return true;
|
||||
|
||||
release_and_exit:
|
||||
PyEval_ReleaseLock();
|
||||
return false;
|
||||
}
|
||||
|
||||
TPythonInterpreter *TPythonInterpreter::getInstance()
|
||||
{
|
||||
if (!_instance)
|
||||
{
|
||||
_instance = new TPythonInterpreter();
|
||||
// shuts down the module
|
||||
void python_taskqueue::exit() {
|
||||
// let the workers know we're done with them
|
||||
m_exit = true;
|
||||
m_condition.notify_all();
|
||||
// let them free up their shit before we proceed
|
||||
for( auto const &worker : m_workers ) {
|
||||
worker->join();
|
||||
}
|
||||
return _instance;
|
||||
// get rid of the leftover tasks
|
||||
// with the workers dead we don't have to worry about concurrent access anymore
|
||||
for( auto *task : m_tasks.data ) {
|
||||
delete task;
|
||||
}
|
||||
// take a bow
|
||||
PyEval_AcquireLock();
|
||||
PyThreadState_Swap( m_mainthread );
|
||||
Py_Finalize();
|
||||
}
|
||||
|
||||
void
|
||||
TPythonInterpreter::killInstance() {
|
||||
// adds specified task along with provided collection of data to the work queue. returns true on success
|
||||
auto python_taskqueue::insert( task_request const &Task ) -> bool {
|
||||
|
||||
delete _instance;
|
||||
}
|
||||
if( ( Task.renderer.empty() )
|
||||
|| ( Task.input == nullptr )
|
||||
|| ( Task.target == null_handle ) ) { return false; }
|
||||
|
||||
bool TPythonInterpreter::loadClassFile( std::string const &lookupPath, std::string const &className )
|
||||
{
|
||||
std::set<std::string>::const_iterator it = _classes.find(className);
|
||||
if (it == _classes.end())
|
||||
auto *renderer { fetch_renderer( Task.renderer ) };
|
||||
if( renderer == nullptr ) { return false; }
|
||||
// acquire a lock on the task queue and add a new task
|
||||
{
|
||||
FILE *sourceFile = _getFile(lookupPath, className);
|
||||
if (sourceFile != nullptr)
|
||||
{
|
||||
fseek(sourceFile, 0, SEEK_END);
|
||||
auto const fsize = ftell(sourceFile);
|
||||
char *buffer = (char *)calloc(fsize + 1, sizeof(char));
|
||||
fseek(sourceFile, 0, SEEK_SET);
|
||||
auto const freaded = fread(buffer, sizeof(char), fsize, sourceFile);
|
||||
buffer[freaded] = 0; // z jakiegos powodu czytamy troche mniej i trzczeba dodac konczace
|
||||
// zero do bufora (mimo ze calloc teoretycznie powiniene zwrocic
|
||||
// wyzerowana pamiec)
|
||||
#ifdef _PY_INT_MORE_LOG
|
||||
char buf[255];
|
||||
sprintf(buf, "readed %d / %d characters for %s", freaded, fsize, className);
|
||||
WriteLog(buf);
|
||||
#endif // _PY_INT_MORE_LOG
|
||||
fclose(sourceFile);
|
||||
if (PyRun_SimpleString(buffer) != 0)
|
||||
{
|
||||
handleError();
|
||||
return false;
|
||||
}
|
||||
_classes.insert( className );
|
||||
/*
|
||||
char *classNameToRemember = (char *)calloc(strlen(className) + 1, sizeof(char));
|
||||
strcpy(classNameToRemember, className);
|
||||
_classes.insert(classNameToRemember);
|
||||
*/
|
||||
free(buffer);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
std::lock_guard<std::mutex> lock( m_tasks.mutex );
|
||||
m_tasks.data.emplace_back( new render_task( renderer, Task.input, Task.target ) );
|
||||
}
|
||||
// potentially wake a worker to handle the new task
|
||||
m_condition.notify_one();
|
||||
// all done
|
||||
return true;
|
||||
}
|
||||
|
||||
PyObject *TPythonInterpreter::newClass( std::string const &className )
|
||||
{
|
||||
return newClass(className, NULL);
|
||||
// executes python script stored in specified file. returns true on success
|
||||
auto python_taskqueue::run_file( std::string const &File, std::string const &Path ) -> bool {
|
||||
|
||||
auto const lookup { FileExists( { Path + File, "python/local/" + File }, { ".py" } ) };
|
||||
if( lookup.first.empty() ) { return false; }
|
||||
|
||||
std::ifstream inputfile { lookup.first + lookup.second };
|
||||
std::string input;
|
||||
input.assign( std::istreambuf_iterator<char>( inputfile ), std::istreambuf_iterator<char>() );
|
||||
|
||||
if( PyRun_SimpleString( input.c_str() ) != 0 ) {
|
||||
error();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
FILE *TPythonInterpreter::_getFile( std::string const &lookupPath, std::string const &className )
|
||||
{
|
||||
if( false == lookupPath.empty() ) {
|
||||
std::string const sourcefilepath = lookupPath + className + ".py";
|
||||
FILE *file = fopen( sourcefilepath.c_str(), "r" );
|
||||
#ifdef _PY_INT_MORE_LOG
|
||||
WriteLog( sourceFilePath );
|
||||
#endif // _PY_INT_MORE_LOG
|
||||
if( nullptr != file ) { return file; }
|
||||
}
|
||||
std::string sourcefilepath = "python/local/" + className + ".py";
|
||||
FILE *file = fopen( sourcefilepath.c_str(), "r" );
|
||||
#ifdef _PY_INT_MORE_LOG
|
||||
WriteLog( sourceFilePath );
|
||||
#endif // _PY_INT_MORE_LOG
|
||||
return file; // either the file, or a nullptr on fail
|
||||
/*
|
||||
char *sourceFilePath;
|
||||
if (lookupPath != NULL)
|
||||
{
|
||||
sourceFilePath = (char *)calloc(strlen(lookupPath) + strlen(className) + 4, sizeof(char));
|
||||
strcat(sourceFilePath, lookupPath);
|
||||
strcat(sourceFilePath, className);
|
||||
strcat(sourceFilePath, ".py");
|
||||
auto python_taskqueue::fetch_renderer( std::string const Renderer ) ->PyObject * {
|
||||
|
||||
FILE *file = fopen(sourceFilePath, "r");
|
||||
#ifdef _PY_INT_MORE_LOG
|
||||
WriteLog(sourceFilePath);
|
||||
#endif // _PY_INT_MORE_LOG
|
||||
free(sourceFilePath);
|
||||
if (file != NULL)
|
||||
{
|
||||
return file;
|
||||
auto const lookup { m_renderers.find( Renderer ) };
|
||||
if( lookup != std::end( m_renderers ) ) {
|
||||
return lookup->second;
|
||||
}
|
||||
// try to load specified renderer class
|
||||
auto const path { substr_path( Renderer ) };
|
||||
auto const file { Renderer.substr( path.size() ) };
|
||||
PyObject *renderer { nullptr };
|
||||
if( m_main == nullptr ) {
|
||||
ErrorLog( "Python Renderer: __main__ module is missing" );
|
||||
goto cache_and_return;
|
||||
}
|
||||
|
||||
PyEval_AcquireLock();
|
||||
{
|
||||
PyObject *rendererarguments{ nullptr };
|
||||
if( false == run_file( file, path ) ) {
|
||||
goto cache_and_return;
|
||||
}
|
||||
auto *renderername{ PyObject_GetAttrString( m_main, file.c_str() ) };
|
||||
if( renderername == nullptr ) {
|
||||
ErrorLog( "Python Renderer: class \"" + file + "\" not defined" );
|
||||
goto cache_and_return;
|
||||
}
|
||||
rendererarguments = Py_BuildValue( "(s)", path.c_str() );
|
||||
if( rendererarguments == nullptr ) {
|
||||
ErrorLog( "Python Renderer: failed to create initialization arguments" );
|
||||
goto cache_and_return;
|
||||
}
|
||||
renderer = PyObject_CallObject( renderername, rendererarguments );
|
||||
|
||||
if( PyErr_Occurred() != nullptr ) {
|
||||
error();
|
||||
renderer = nullptr;
|
||||
}
|
||||
|
||||
cache_and_return:
|
||||
// clean up after yourself
|
||||
if( rendererarguments != nullptr ) {
|
||||
Py_DECREF( rendererarguments );
|
||||
}
|
||||
}
|
||||
char *basePath = "python/local/";
|
||||
sourceFilePath = (char *)calloc(strlen(basePath) + strlen(className) + 4, sizeof(char));
|
||||
strcat(sourceFilePath, basePath);
|
||||
strcat(sourceFilePath, className);
|
||||
strcat(sourceFilePath, ".py");
|
||||
|
||||
FILE *file = fopen(sourceFilePath, "r");
|
||||
#ifdef _PY_INT_MORE_LOG
|
||||
WriteLog(sourceFilePath);
|
||||
#endif // _PY_INT_MORE_LOG
|
||||
free(sourceFilePath);
|
||||
if (file != NULL)
|
||||
{
|
||||
return file;
|
||||
}
|
||||
return NULL;
|
||||
*/
|
||||
PyEval_ReleaseLock();
|
||||
// cache the failures as well so we don't try again on subsequent requests
|
||||
m_renderers.emplace( Renderer, renderer );
|
||||
return renderer;
|
||||
}
|
||||
|
||||
void TPythonInterpreter::handleError()
|
||||
{
|
||||
#ifdef _PY_INT_MORE_LOG
|
||||
WriteLog("Python Error occured");
|
||||
#endif // _PY_INT_MORE_LOG
|
||||
if (_stdErr != NULL)
|
||||
{ // std err pythona jest buforowane
|
||||
void python_taskqueue::run( GLFWwindow *Context, rendertask_sequence &Tasks, threading::condition_variable &Condition, std::atomic<bool> &Exit ) {
|
||||
|
||||
glfwMakeContextCurrent( Context );
|
||||
// create a state object for this thread
|
||||
PyEval_AcquireLock();
|
||||
auto *threadstate { PyThreadState_New( m_mainthread->interp ) };
|
||||
PyEval_ReleaseLock();
|
||||
|
||||
render_task *task { nullptr };
|
||||
|
||||
while( false == Exit.load() ) {
|
||||
// regardless of the reason we woke up prime the spurious wakeup flag for the next time
|
||||
Condition.spurious( true );
|
||||
// keep working as long as there's any scheduled tasks
|
||||
do {
|
||||
task = nullptr;
|
||||
// acquire a lock on the task queue and potentially grab a task from it
|
||||
{
|
||||
std::lock_guard<std::mutex> lock( Tasks.mutex );
|
||||
if( false == Tasks.data.empty() ) {
|
||||
// fifo
|
||||
task = Tasks.data.front();
|
||||
Tasks.data.pop_front();
|
||||
}
|
||||
}
|
||||
if( task != nullptr ) {
|
||||
// swap in my thread state
|
||||
PyEval_AcquireLock();
|
||||
PyThreadState_Swap( threadstate );
|
||||
// execute python code
|
||||
task->run();
|
||||
error();
|
||||
// clear the thread state
|
||||
PyThreadState_Swap( nullptr );
|
||||
PyEval_ReleaseLock();
|
||||
}
|
||||
// TBD, TODO: add some idle time between tasks in case we're on a single thread cpu?
|
||||
} while( task != nullptr );
|
||||
// if there's nothing left to do wait until there is
|
||||
// but check every now and then on your own to minimize potential deadlock situations
|
||||
Condition.wait_for( std::chrono::seconds( 5 ) );
|
||||
}
|
||||
// clean up thread state data
|
||||
PyEval_AcquireLock();
|
||||
PyThreadState_Swap( nullptr );
|
||||
PyThreadState_Clear( threadstate );
|
||||
PyThreadState_Delete( threadstate );
|
||||
PyEval_ReleaseLock();
|
||||
}
|
||||
|
||||
void
|
||||
python_taskqueue::error() {
|
||||
|
||||
if( PyErr_Occurred() == nullptr ) { return; }
|
||||
|
||||
if( m_error != nullptr ) {
|
||||
// std err pythona jest buforowane
|
||||
PyErr_Print();
|
||||
PyObject *bufferContent = PyObject_CallMethod(_stdErr, "getvalue", NULL);
|
||||
PyObject_CallMethod(_stdErr, "truncate", "i", 0); // czyscimy bufor na kolejne bledy
|
||||
WriteLog(PyString_AsString(bufferContent));
|
||||
auto *errortext { PyObject_CallMethod( m_error, "getvalue", nullptr ) };
|
||||
ErrorLog( PyString_AsString( errortext ) );
|
||||
// czyscimy bufor na kolejne bledy
|
||||
PyObject_CallMethod( m_error, "truncate", "i", 0 );
|
||||
}
|
||||
else
|
||||
{ // nie dziala buffor pythona
|
||||
if (PyErr_Occurred() != NULL)
|
||||
{
|
||||
PyObject *ptype, *pvalue, *ptraceback;
|
||||
PyErr_Fetch(&ptype, &pvalue, &ptraceback);
|
||||
if (ptype == NULL)
|
||||
{
|
||||
WriteLog("Don't konw how to handle NULL exception");
|
||||
}
|
||||
PyErr_NormalizeException(&ptype, &pvalue, &ptraceback);
|
||||
if (ptype == NULL)
|
||||
{
|
||||
WriteLog("Don't konw how to handle NULL exception");
|
||||
}
|
||||
PyObject *pStrType = PyObject_Str(ptype);
|
||||
if (pStrType != NULL)
|
||||
{
|
||||
WriteLog(PyString_AsString(pStrType));
|
||||
}
|
||||
WriteLog(PyString_AsString(pvalue));
|
||||
PyObject *pStrTraceback = PyObject_Str(ptraceback);
|
||||
if (pStrTraceback != NULL)
|
||||
{
|
||||
WriteLog(PyString_AsString(pStrTraceback));
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog("Python Traceback cannot be shown");
|
||||
}
|
||||
else {
|
||||
// nie dziala buffor pythona
|
||||
PyObject *type, *value, *traceback;
|
||||
PyErr_Fetch( &type, &value, &traceback );
|
||||
if( type == nullptr ) {
|
||||
ErrorLog( "Python Interpreter: don't know how to handle null exception" );
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef _PY_INT_MORE_LOG
|
||||
WriteLog("Called python error handler when no error occured!");
|
||||
#endif // _PY_INT_MORE_LOG
|
||||
PyErr_NormalizeException( &type, &value, &traceback );
|
||||
if( type == nullptr ) {
|
||||
ErrorLog( "Python Interpreter: don't know how to handle null exception" );
|
||||
}
|
||||
auto *typetext { PyObject_Str( type ) };
|
||||
if( typetext != nullptr ) {
|
||||
ErrorLog( PyString_AsString( typetext ) );
|
||||
}
|
||||
if( value != nullptr ) {
|
||||
ErrorLog( PyString_AsString( value ) );
|
||||
}
|
||||
auto *tracebacktext { PyObject_Str( traceback ) };
|
||||
if( tracebacktext != nullptr ) {
|
||||
WriteLog( PyString_AsString( tracebacktext ) );
|
||||
}
|
||||
else {
|
||||
WriteLog( "Python Interpreter: failed to retrieve the stack traceback" );
|
||||
}
|
||||
}
|
||||
}
|
||||
PyObject *TPythonInterpreter::newClass(std::string const &className, PyObject *argsTuple)
|
||||
{
|
||||
if (_main == NULL)
|
||||
{
|
||||
WriteLog("main turned into null");
|
||||
return NULL;
|
||||
}
|
||||
PyObject *classNameObj = PyObject_GetAttrString(_main, className.c_str());
|
||||
if (classNameObj == NULL)
|
||||
{
|
||||
#ifdef _PY_INT_MORE_LOG
|
||||
char buf[255];
|
||||
sprintf(buf, "Python class %s not defined!", className);
|
||||
WriteLog(buf);
|
||||
#endif // _PY_INT_MORE_LOG
|
||||
return NULL;
|
||||
}
|
||||
PyObject *object = PyObject_CallObject(classNameObj, argsTuple);
|
||||
|
||||
if (PyErr_Occurred() != NULL)
|
||||
{
|
||||
handleError();
|
||||
return NULL;
|
||||
}
|
||||
return object;
|
||||
}
|
||||
|
||||
TPythonScreenRenderer::TPythonScreenRenderer(int textureId, PyObject *renderer)
|
||||
{
|
||||
_textureId = textureId;
|
||||
_pyRenderer = renderer;
|
||||
}
|
||||
|
||||
void TPythonScreenRenderer::updateTexture()
|
||||
{
|
||||
int width, height;
|
||||
if (_pyWidth == NULL || _pyHeight == NULL)
|
||||
{
|
||||
WriteLog("Unknown python texture size!");
|
||||
return;
|
||||
}
|
||||
width = PyInt_AsLong(_pyWidth);
|
||||
height = PyInt_AsLong(_pyHeight);
|
||||
if (_pyTexture != NULL)
|
||||
{
|
||||
char *textureData = PyString_AsString(_pyTexture);
|
||||
if (textureData != NULL)
|
||||
{
|
||||
#ifdef _PY_INT_MORE_LOG
|
||||
char buff[255];
|
||||
sprintf(buff, "Sending texture id: %d w: %d h: %d", _textureId, width, height);
|
||||
WriteLog(buff);
|
||||
#endif // _PY_INT_MORE_LOG
|
||||
/*
|
||||
glPixelStorei( GL_UNPACK_ALIGNMENT, 1 );
|
||||
glPixelStorei( GL_PACK_ALIGNMENT, 1 );
|
||||
*/
|
||||
GfxRenderer.Bind_Material(_textureId);
|
||||
// setup texture parameters
|
||||
glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE );
|
||||
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
|
||||
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
|
||||
glTexEnvf( GL_TEXTURE_FILTER_CONTROL, GL_TEXTURE_LOD_BIAS, -1.0 );
|
||||
// build texture
|
||||
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, textureData );
|
||||
|
||||
#ifdef _PY_INT_MORE_LOG
|
||||
GLenum status = glGetError();
|
||||
switch (status)
|
||||
{
|
||||
case GL_INVALID_ENUM:
|
||||
WriteLog("An unacceptable value is specified for an enumerated argument. The "
|
||||
"offending function is ignored, having no side effect other than to set "
|
||||
"the error flag.");
|
||||
break;
|
||||
case GL_INVALID_VALUE:
|
||||
WriteLog("A numeric argument is out of range. The offending function is ignored, "
|
||||
"having no side effect other than to set the error flag.");
|
||||
break;
|
||||
case GL_INVALID_OPERATION:
|
||||
WriteLog("The specified operation is not allowed in the current state. The "
|
||||
"offending function is ignored, having no side effect other than to set "
|
||||
"the error flag.");
|
||||
break;
|
||||
case GL_NO_ERROR:
|
||||
WriteLog("No error has been recorded. The value of this symbolic constant is "
|
||||
"guaranteed to be zero.");
|
||||
break;
|
||||
case GL_STACK_OVERFLOW:
|
||||
WriteLog("This function would cause a stack overflow. The offending function is "
|
||||
"ignored, having no side effect other than to set the error flag.");
|
||||
break;
|
||||
case GL_STACK_UNDERFLOW:
|
||||
WriteLog("This function would cause a stack underflow. The offending function is "
|
||||
"ignored, having no side effect other than to set the error flag.");
|
||||
break;
|
||||
case GL_OUT_OF_MEMORY:
|
||||
WriteLog("There is not enough memory left to execute the function. The state of "
|
||||
"OpenGL is undefined, except for the state of the error flags, after this "
|
||||
"error is recorded.");
|
||||
break;
|
||||
};
|
||||
#endif // _PY_INT_MORE_LOG
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog("RAW python texture data is NULL!");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
WriteLog("Python texture object is NULL!");
|
||||
}
|
||||
}
|
||||
|
||||
void TPythonScreenRenderer::render(PyObject *trainState)
|
||||
{
|
||||
#ifdef _PY_INT_MORE_LOG
|
||||
WriteLog("Python rendering texture ...");
|
||||
#endif // _PY_INT_MORE_LOG
|
||||
_pyTexture = PyObject_CallMethod(_pyRenderer, "render", "O", trainState);
|
||||
|
||||
if (_pyTexture == NULL)
|
||||
{
|
||||
TPythonInterpreter::getInstance()->handleError();
|
||||
}
|
||||
else
|
||||
{
|
||||
_pyWidth = PyObject_CallMethod(_pyRenderer, "get_width", NULL);
|
||||
if (_pyWidth == NULL)
|
||||
{
|
||||
TPythonInterpreter::getInstance()->handleError();
|
||||
}
|
||||
_pyHeight = PyObject_CallMethod(_pyRenderer, "get_height", NULL);
|
||||
if (_pyHeight == NULL)
|
||||
{
|
||||
TPythonInterpreter::getInstance()->handleError();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef __GNUC__
|
||||
#pragma GCC diagnostic pop
|
||||
#endif
|
||||
|
||||
TPythonScreenRenderer::~TPythonScreenRenderer()
|
||||
{
|
||||
#ifdef _PY_INT_MORE_LOG
|
||||
WriteLog("PythonScreenRenderer descturctor called");
|
||||
#endif // _PY_INT_MORE_LOG
|
||||
if (_pyRenderer != NULL)
|
||||
{
|
||||
Py_CLEAR(_pyRenderer);
|
||||
}
|
||||
cleanup();
|
||||
#ifdef _PY_INT_MORE_LOG
|
||||
WriteLog("PythonScreenRenderer desctructor finished");
|
||||
#endif // _PY_INT_MORE_LOG
|
||||
}
|
||||
|
||||
void TPythonScreenRenderer::cleanup()
|
||||
{
|
||||
if (_pyTexture != NULL)
|
||||
{
|
||||
Py_CLEAR(_pyTexture);
|
||||
_pyTexture = NULL;
|
||||
}
|
||||
if (_pyWidth != NULL)
|
||||
{
|
||||
Py_CLEAR(_pyWidth);
|
||||
_pyWidth = NULL;
|
||||
}
|
||||
if (_pyHeight != NULL)
|
||||
{
|
||||
Py_CLEAR(_pyHeight);
|
||||
_pyHeight = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void TPythonScreens::reset(void *train)
|
||||
{
|
||||
_terminationFlag = true;
|
||||
if (_thread != NULL)
|
||||
{
|
||||
// WriteLog("Awaiting python thread to end");
|
||||
_thread->join();
|
||||
delete _thread;
|
||||
_thread = nullptr;
|
||||
}
|
||||
_terminationFlag = false;
|
||||
_cleanupReadyFlag = false;
|
||||
_renderReadyFlag = false;
|
||||
for (std::vector<TPythonScreenRenderer *>::iterator i = _screens.begin(); i != _screens.end();
|
||||
++i)
|
||||
{
|
||||
delete *i;
|
||||
}
|
||||
#ifdef _PY_INT_MORE_LOG
|
||||
WriteLog("Clearing renderer vector");
|
||||
#endif // _PY_INT_MORE_LOG
|
||||
_screens.clear();
|
||||
_train = train;
|
||||
}
|
||||
|
||||
void TPythonScreens::init(cParser &parser, TModel3d *model, std::string const &name, int const cab)
|
||||
{
|
||||
std::string asSubModelName, asPyClassName;
|
||||
parser.getTokens( 2, false );
|
||||
parser
|
||||
>> asSubModelName
|
||||
>> asPyClassName;
|
||||
std::string subModelName = ToLower( asSubModelName );
|
||||
std::string pyClassName = ToLower( asPyClassName );
|
||||
TSubModel *subModel = model->GetFromName(subModelName);
|
||||
if (subModel == NULL)
|
||||
{
|
||||
WriteLog( "Python Screen: submodel " + subModelName + " not found - Ignoring screen" );
|
||||
return; // nie ma takiego sub modelu w danej kabinie pomijamy
|
||||
}
|
||||
auto textureId = subModel->GetMaterial();
|
||||
if (textureId <= 0)
|
||||
{
|
||||
WriteLog( "Python Screen: invalid texture id " + std::to_string(textureId) + " - Ignoring screen" );
|
||||
return; // sub model nie posiada tekstury lub tekstura wymienna - nie obslugiwana
|
||||
}
|
||||
TPythonInterpreter *python = TPythonInterpreter::getInstance();
|
||||
python->loadClassFile(_lookupPath, pyClassName);
|
||||
PyObject *args = Py_BuildValue("(ssi)", _lookupPath.c_str(), name.c_str(), cab);
|
||||
if (args == NULL)
|
||||
{
|
||||
WriteLog("Python Screen: cannot create __init__ arguments");
|
||||
return;
|
||||
}
|
||||
PyObject *pyRenderer = python->newClass(pyClassName, args);
|
||||
Py_CLEAR(args);
|
||||
if (pyRenderer == NULL)
|
||||
{
|
||||
WriteLog( "Python Screen: null renderer for " + pyClassName + " - Ignoring screen" );
|
||||
return; // nie mozna utworzyc obiektu Pythonowego
|
||||
}
|
||||
m_updaterate = Global.PythonScreenUpdateRate;
|
||||
TPythonScreenRenderer *renderer = new TPythonScreenRenderer(textureId, pyRenderer);
|
||||
_screens.push_back(renderer);
|
||||
WriteLog( "Created python screen " + pyClassName + " on submodel " + subModelName + " (" + std::to_string(textureId) + ")" );
|
||||
}
|
||||
|
||||
void TPythonScreens::update()
|
||||
{
|
||||
if (!_renderReadyFlag)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_renderReadyFlag = false;
|
||||
for (std::vector<TPythonScreenRenderer *>::iterator i = _screens.begin(); i != _screens.end();
|
||||
++i)
|
||||
{
|
||||
(*i)->updateTexture();
|
||||
}
|
||||
_cleanupReadyFlag = true;
|
||||
}
|
||||
|
||||
void TPythonScreens::setLookupPath(std::string const &path)
|
||||
{
|
||||
_lookupPath = path;
|
||||
replace_slashes( _lookupPath );
|
||||
}
|
||||
|
||||
TPythonScreens::TPythonScreens()
|
||||
{
|
||||
TPythonInterpreter::getInstance()->loadClassFile("", "abstractscreenrenderer");
|
||||
}
|
||||
|
||||
TPythonScreens::~TPythonScreens()
|
||||
{
|
||||
#ifdef _PY_INT_MORE_LOG
|
||||
WriteLog("Called python sceeens destructor");
|
||||
#endif // _PY_INT_MORE_LOG
|
||||
reset(NULL);
|
||||
/*
|
||||
if (_lookupPath != NULL)
|
||||
{
|
||||
#ifdef _PY_INT_MORE_LOG
|
||||
WriteLog("Freeing lookup path");
|
||||
#endif // _PY_INT_MORE_LOG
|
||||
free(_lookupPath);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
void TPythonScreens::run()
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
m_updatestopwatch.start();
|
||||
if (_terminationFlag)
|
||||
{
|
||||
return;
|
||||
}
|
||||
TTrain *train = (TTrain *)_train;
|
||||
_trainState = train->GetTrainState();
|
||||
if (_terminationFlag)
|
||||
{
|
||||
_freeTrainState();
|
||||
return;
|
||||
}
|
||||
for (std::vector<TPythonScreenRenderer *>::iterator i = _screens.begin();
|
||||
i != _screens.end(); ++i)
|
||||
{
|
||||
(*i)->render(_trainState);
|
||||
}
|
||||
_freeTrainState();
|
||||
if (_terminationFlag)
|
||||
{
|
||||
_cleanup();
|
||||
return;
|
||||
}
|
||||
_renderReadyFlag = true;
|
||||
m_updatestopwatch.stop();
|
||||
while (!_cleanupReadyFlag && !_terminationFlag)
|
||||
{
|
||||
auto const sleeptime {
|
||||
std::max(
|
||||
100,
|
||||
m_updaterate - static_cast<int>( m_updatestopwatch.average() ) ) };
|
||||
#ifdef _WIN32
|
||||
Sleep( sleeptime );
|
||||
#elif __linux__
|
||||
usleep( sleeptime * 1000 );
|
||||
#endif
|
||||
}
|
||||
if (_terminationFlag)
|
||||
{
|
||||
return;
|
||||
}
|
||||
_cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
void TPythonScreens::finish()
|
||||
{
|
||||
// nothing to do here, proper clean up takes place afterwards
|
||||
}
|
||||
|
||||
void ScreenRendererThread(TPythonScreens* renderer)
|
||||
{
|
||||
renderer->run();
|
||||
renderer->finish();
|
||||
#ifdef _PY_INT_MORE_LOG
|
||||
WriteLog("Python Screen Renderer Thread Ends");
|
||||
#endif // _PY_INT_MORE_LOG
|
||||
}
|
||||
|
||||
void TPythonScreens::start()
|
||||
{
|
||||
if (_screens.size() > 0)
|
||||
{
|
||||
_thread = new std::thread(ScreenRendererThread, this);
|
||||
}
|
||||
}
|
||||
|
||||
void TPythonScreens::_cleanup()
|
||||
{
|
||||
_cleanupReadyFlag = false;
|
||||
for (std::vector<TPythonScreenRenderer *>::iterator i = _screens.begin(); i != _screens.end();
|
||||
++i)
|
||||
{
|
||||
(*i)->cleanup();
|
||||
}
|
||||
}
|
||||
|
||||
void TPythonScreens::_freeTrainState()
|
||||
{
|
||||
if (_trainState != NULL)
|
||||
{
|
||||
PyDict_Clear(_trainState);
|
||||
Py_CLEAR(_trainState);
|
||||
_trainState = NULL;
|
||||
}
|
||||
}
|
||||
137
PyInt.h
137
PyInt.h
@@ -1,13 +1,13 @@
|
||||
#ifndef PyIntH
|
||||
#define PyIntH
|
||||
/*
|
||||
This Source Code Form is subject to the
|
||||
terms of the Mozilla Public License, v.
|
||||
2.0. If a copy of the MPL was not
|
||||
distributed with this file, You can
|
||||
obtain one at
|
||||
http://mozilla.org/MPL/2.0/.
|
||||
*/
|
||||
|
||||
#ifdef _POSIX_C_SOURCE
|
||||
#undef _POSIX_C_SOURCE
|
||||
#endif
|
||||
|
||||
#ifdef _XOPEN_SOURCE
|
||||
#undef _XOPEN_SOURCE
|
||||
#endif
|
||||
#pragma once
|
||||
|
||||
#ifdef _DEBUG
|
||||
#undef _DEBUG // bez tego macra Py_DECREF powoduja problemy przy linkowaniu
|
||||
@@ -17,78 +17,69 @@
|
||||
#include "Python.h"
|
||||
#endif
|
||||
#include "Classes.h"
|
||||
#include "timer.h"
|
||||
|
||||
#define PyGetFloat(param) PyFloat_FromDouble(param >= 0 ? param : -param)
|
||||
#define PyGetFloatS(param) PyFloat_FromDouble(param)
|
||||
#define PyGetInt(param) PyInt_FromLong(param)
|
||||
#define PyGetFloatS(param) PyFloat_FromDouble(param)
|
||||
#define PyGetBool(param) param ? Py_True : Py_False
|
||||
#define PyGetString(param) PyString_FromString(param)
|
||||
|
||||
class TPythonInterpreter
|
||||
{
|
||||
protected:
|
||||
TPythonInterpreter();
|
||||
~TPythonInterpreter() {}
|
||||
static TPythonInterpreter *_instance;
|
||||
std::set<std::string> _classes;
|
||||
PyObject *_main;
|
||||
PyObject *_stdErr;
|
||||
FILE *_getFile( std::string const &lookupPath, std::string const &className );
|
||||
// TODO: extract common base and inherit specialization from it
|
||||
class render_task {
|
||||
|
||||
public:
|
||||
static TPythonInterpreter *getInstance();
|
||||
static void killInstance();
|
||||
bool loadClassFile( std::string const &lookupPath, std::string const &className );
|
||||
PyObject *newClass( std::string const &className );
|
||||
PyObject *newClass( std::string const &className, PyObject *argsTuple );
|
||||
void handleError();
|
||||
};
|
||||
|
||||
class TPythonScreenRenderer
|
||||
{
|
||||
protected:
|
||||
PyObject *_pyRenderer;
|
||||
PyObject *_pyTexture;
|
||||
int _textureId;
|
||||
PyObject *_pyWidth;
|
||||
PyObject *_pyHeight;
|
||||
|
||||
public:
|
||||
TPythonScreenRenderer(int textureId, PyObject *renderer);
|
||||
~TPythonScreenRenderer();
|
||||
void render(PyObject *trainState);
|
||||
void cleanup();
|
||||
void updateTexture();
|
||||
};
|
||||
|
||||
class TPythonScreens
|
||||
{
|
||||
protected:
|
||||
bool _cleanupReadyFlag{ false };
|
||||
bool _renderReadyFlag{ false };
|
||||
bool _terminationFlag{ false };
|
||||
std::thread *_thread{ nullptr };
|
||||
std::vector<TPythonScreenRenderer *> _screens;
|
||||
std::string _lookupPath;
|
||||
void *_train;
|
||||
void _cleanup();
|
||||
void _freeTrainState();
|
||||
PyObject *_trainState;
|
||||
int m_updaterate { 200 };
|
||||
Timer::stopwatch m_updatestopwatch;
|
||||
|
||||
public:
|
||||
void reset(void *train);
|
||||
void setLookupPath(std::string const &path);
|
||||
void init(cParser &parser, TModel3d *model, std::string const &name, int const cab);
|
||||
void update();
|
||||
TPythonScreens();
|
||||
~TPythonScreens();
|
||||
public:
|
||||
// constructors
|
||||
render_task( PyObject *Renderer, PyObject *Input, material_handle Target ) :
|
||||
m_renderer( Renderer ), m_input( Input ), m_target( Target )
|
||||
{}
|
||||
// methods
|
||||
void run();
|
||||
void start();
|
||||
void finish();
|
||||
|
||||
private:
|
||||
// members
|
||||
PyObject *m_renderer {nullptr};
|
||||
PyObject *m_input { nullptr };
|
||||
material_handle m_target { null_handle };
|
||||
};
|
||||
|
||||
#endif // PyIntH
|
||||
class python_taskqueue {
|
||||
|
||||
public:
|
||||
// types
|
||||
struct task_request {
|
||||
|
||||
std::string const &renderer;
|
||||
PyObject *input;
|
||||
material_handle target;
|
||||
};
|
||||
// constructors
|
||||
python_taskqueue() = default;
|
||||
// methods
|
||||
// initializes the module. returns true on success
|
||||
auto init() -> bool;
|
||||
// shuts down the module
|
||||
void exit();
|
||||
// adds specified task along with provided collection of data to the work queue. returns true on success
|
||||
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;
|
||||
|
||||
private:
|
||||
// types
|
||||
static int const WORKERCOUNT { 1 };
|
||||
using worker_array = std::array<std::unique_ptr<std::thread>, WORKERCOUNT >;
|
||||
using rendertask_sequence = threading::lockable< std::deque<render_task *> >;
|
||||
// methods
|
||||
auto fetch_renderer( std::string const Renderer ) -> PyObject *;
|
||||
void run( GLFWwindow *Context, rendertask_sequence &Tasks, threading::condition_variable &Condition, std::atomic<bool> &Exit );
|
||||
void error();
|
||||
// members
|
||||
PyObject *m_main { nullptr };
|
||||
PyObject *m_error { nullptr };
|
||||
PyThreadState *m_mainthread{ nullptr };
|
||||
worker_array m_workers;
|
||||
threading::condition_variable m_condition; // wakes up the workers
|
||||
std::atomic<bool> m_exit { false }; // signals the workers to quit
|
||||
std::unordered_map<std::string, PyObject *> m_renderers; // cache of python classes
|
||||
rendertask_sequence m_tasks;
|
||||
};
|
||||
|
||||
@@ -9,8 +9,6 @@ http://mozilla.org/MPL/2.0/.
|
||||
|
||||
#pragma once
|
||||
|
||||
int const null_handle = 0;
|
||||
|
||||
enum class resource_state {
|
||||
none,
|
||||
loading,
|
||||
|
||||
51
Train.cpp
51
Train.cpp
@@ -28,6 +28,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include "dynobj.h"
|
||||
#include "mtable.h"
|
||||
#include "Console.h"
|
||||
#include "application.h"
|
||||
|
||||
namespace input {
|
||||
|
||||
@@ -449,6 +450,7 @@ PyObject *TTrain::GetTrainState() {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PyDict_SetItemString( dict, "name", PyGetString( DynamicObject->asName.c_str() ) );
|
||||
PyDict_SetItemString( dict, "cab", PyGetInt( mover->ActiveCab ) );
|
||||
// basic systems state data
|
||||
PyDict_SetItemString( dict, "battery", PyGetBool( mvControlled->Battery ) );
|
||||
@@ -469,7 +471,7 @@ PyObject *TTrain::GetTrainState() {
|
||||
PyDict_SetItemString( dict, "dir_brake", PyGetBool( bEP ) );
|
||||
bool bPN;
|
||||
if( ( typeid( *mvControlled->Hamulec ) == typeid( TLSt ) )
|
||||
|| ( typeid( *mvControlled->Hamulec ) == typeid( TEStED ) ) ) {
|
||||
|| ( typeid( *mvControlled->Hamulec ) == typeid( TEStED ) ) ) {
|
||||
|
||||
TBrake* temp_ham = mvControlled->Hamulec.get();
|
||||
bPN = ( static_cast<TLSt*>( temp_ham )->GetEDBCP() > 0.2 );
|
||||
@@ -4682,7 +4684,6 @@ bool TTrain::Update( double const Deltatime )
|
||||
}
|
||||
}
|
||||
|
||||
tor = DynamicObject->GetTrack(); // McZapkie-180203
|
||||
// McZapkie: predkosc wyswietlana na tachometrze brana jest z obrotow kol
|
||||
auto const maxtacho { 3.0 };
|
||||
fTachoVelocity = static_cast<float>( std::min( std::abs(11.31 * mvControlled->WheelDiameter * mvControlled->nrot), mvControlled->Vmax * 1.05) );
|
||||
@@ -5606,7 +5607,6 @@ bool TTrain::Update( double const Deltatime )
|
||||
ggFuelPumpButton.Update();
|
||||
ggOilPumpButton.Update();
|
||||
//------
|
||||
pyScreens.update();
|
||||
}
|
||||
// wyprowadzenie sygnałów dla haslera na PoKeys (zaznaczanie na taśmie)
|
||||
btHaslerBrakes.Turn(DynamicObject->MoverParameters->BrakePress > 0.4); // ciśnienie w cylindrach
|
||||
@@ -5676,8 +5676,8 @@ bool TTrain::Update( double const Deltatime )
|
||||
}
|
||||
}
|
||||
/*
|
||||
// NOTE: disabled while switch state isn't preserved while moving between compartments
|
||||
// check whether we should raise the pantographs, based on volume in pantograph tank
|
||||
// NOTE: disabled while switch state isn't preserved while moving between compartments
|
||||
if( mvControlled->PantPress > (
|
||||
mvControlled->TrainType == dt_EZT ?
|
||||
2.4 :
|
||||
@@ -5692,7 +5692,15 @@ bool TTrain::Update( double const Deltatime )
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
// screens
|
||||
fScreenTimer += Deltatime;
|
||||
if( ( fScreenTimer > Global.PythonScreenUpdateRate * 0.001f )
|
||||
&& ( 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 } );
|
||||
}
|
||||
}
|
||||
// sounds
|
||||
update_sounds( Deltatime );
|
||||
|
||||
@@ -5853,7 +5861,6 @@ TTrain::update_sounds( double const Deltatime ) {
|
||||
dsbSlipAlarm.stop();
|
||||
}
|
||||
}
|
||||
/*
|
||||
// szum w czasie jazdy
|
||||
if( ( false == FreeFlyModeFlag )
|
||||
&& ( false == Global.CabWindowOpen )
|
||||
@@ -5865,7 +5872,6 @@ TTrain::update_sounds( double const Deltatime ) {
|
||||
// don't play the optional ending sound if the listener switches views
|
||||
rsRunningNoise.stop( true == FreeFlyModeFlag );
|
||||
}
|
||||
*/
|
||||
// hunting oscillation noise
|
||||
if( ( false == FreeFlyModeFlag )
|
||||
&& ( false == Global.CabWindowOpen )
|
||||
@@ -6255,6 +6261,8 @@ bool TTrain::LoadMMediaFile(std::string const &asFileName)
|
||||
bool TTrain::InitializeCab(int NewCabNo, std::string const &asFileName)
|
||||
{
|
||||
m_controlmapper.clear();
|
||||
// clear python screens
|
||||
m_screens.clear();
|
||||
// reset sound positions and owner
|
||||
auto const nullvector { glm::vec3() };
|
||||
std::vector<sound_source *> sounds = {
|
||||
@@ -6272,9 +6280,6 @@ bool TTrain::InitializeCab(int NewCabNo, std::string const &asFileName)
|
||||
pMechViewAngle = { 0.0, 0.0 };
|
||||
Global.pCamera.Pitch = pMechViewAngle.x;
|
||||
Global.pCamera.Yaw = pMechViewAngle.y;
|
||||
|
||||
pyScreens.reset(this);
|
||||
pyScreens.setLookupPath(DynamicObject->asBaseDir);
|
||||
bool parse = false;
|
||||
int cabindex = 0;
|
||||
DynamicObject->mdKabina = NULL; // likwidacja wskaźnika na dotychczasową kabinę
|
||||
@@ -6469,9 +6474,32 @@ bool TTrain::InitializeCab(int NewCabNo, std::string const &asFileName)
|
||||
// matched the token, grab the next one
|
||||
continue;
|
||||
}
|
||||
// TODO: add "pydestination:"
|
||||
else if (token == "pyscreen:")
|
||||
{
|
||||
pyScreens.init(parser, DynamicObject->mdKabina, DynamicObject->name(), NewCabNo);
|
||||
std::string submodelname, renderername;
|
||||
parser.getTokens( 2 );
|
||||
parser
|
||||
>> submodelname
|
||||
>> renderername;
|
||||
|
||||
auto const *submodel { DynamicObject->mdKabina->GetFromName( submodelname ) };
|
||||
if( submodel == nullptr ) {
|
||||
WriteLog( "Python Screen: submodel " + submodelname + " not found - Ignoring screen" );
|
||||
continue;
|
||||
}
|
||||
auto const material { submodel->GetMaterial() };
|
||||
if( material <= 0 ) {
|
||||
// sub model nie posiada tekstury lub tekstura wymienna - nie obslugiwana
|
||||
WriteLog( "Python Screen: invalid texture id " + std::to_string( material ) + " - Ignoring screen" );
|
||||
continue;
|
||||
}
|
||||
// record renderer and material binding for future update requests
|
||||
m_screens.emplace_back(
|
||||
( substr_path(renderername).empty() ? // supply vehicle folder as path if none is provided
|
||||
DynamicObject->asBaseDir + renderername :
|
||||
renderername ),
|
||||
material );
|
||||
}
|
||||
// btLampkaUnknown.Init("unknown",mdKabina,false);
|
||||
} while (token != "");
|
||||
@@ -6480,7 +6508,6 @@ bool TTrain::InitializeCab(int NewCabNo, std::string const &asFileName)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
pyScreens.start();
|
||||
if (DynamicObject->mdKabina)
|
||||
{
|
||||
// configure placement of sound emitters which aren't bound with any device model, and weren't placed manually
|
||||
|
||||
8
Train.h
8
Train.h
@@ -628,12 +628,10 @@ private:
|
||||
float fConverterTimer; // hunter-261211: dla przekaznika
|
||||
float fMainRelayTimer; // hunter-141211: zalaczanie WSa z opoznieniem
|
||||
float fCzuwakTestTimer; // hunter-091012: do testu czuwaka
|
||||
float fLightsTimer; // yB 150617: timer do swiatel
|
||||
float fScreenTimer { 0.f };
|
||||
|
||||
bool CAflag { false }; // hunter-131211: dla osobnego zbijania CA i SHP
|
||||
|
||||
double fPoslizgTimer;
|
||||
TTrack *tor;
|
||||
// McZapkie-240302 - przyda sie do tachometru
|
||||
float fTachoVelocity{ 0.0f };
|
||||
float fTachoVelocityJump{ 0.0f }; // ze skakaniem
|
||||
@@ -659,10 +657,8 @@ private:
|
||||
bool bHeat[8]; // grzanie
|
||||
// McZapkie: do syczenia
|
||||
float fPPress, fNPress;
|
||||
// float fSPPress, fSNPress;
|
||||
int iSekunda; // Ra: sekunda aktualizacji pr?dko?ci
|
||||
int iRadioChannel { 1 }; // numer aktualnego kana?u radiowego
|
||||
TPythonScreens pyScreens;
|
||||
std::vector<std::pair<std::string, material_handle>> m_screens;
|
||||
|
||||
public:
|
||||
float fPress[20][3]; // cisnienia dla wszystkich czlonow
|
||||
|
||||
@@ -16,7 +16,6 @@ http://mozilla.org/MPL/2.0/.
|
||||
#include "globals.h"
|
||||
#include "simulation.h"
|
||||
#include "train.h"
|
||||
#include "pyint.h"
|
||||
#include "sceneeditor.h"
|
||||
#include "renderer.h"
|
||||
#include "uilayer.h"
|
||||
@@ -129,6 +128,7 @@ eu07_application::init( int Argc, char *Argv[] ) {
|
||||
if( ( result = init_audio() ) != 0 ) {
|
||||
return result;
|
||||
}
|
||||
m_taskqueue.init();
|
||||
if( ( result = init_modes() ) != 0 ) {
|
||||
return result;
|
||||
}
|
||||
@@ -140,7 +140,7 @@ int
|
||||
eu07_application::run() {
|
||||
|
||||
// main application loop
|
||||
while( ( false == glfwWindowShouldClose( m_window ) )
|
||||
while( ( false == glfwWindowShouldClose( m_windows.front() ) )
|
||||
&& ( false == m_modestack.empty() )
|
||||
&& ( true == m_modes[ m_modestack.top() ]->update() )
|
||||
&& ( true == GfxRenderer.Render() ) ) {
|
||||
@@ -151,6 +151,12 @@ eu07_application::run() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool
|
||||
eu07_application::request( python_taskqueue::task_request const &Task ) {
|
||||
|
||||
return m_taskqueue.insert( Task );
|
||||
}
|
||||
|
||||
void
|
||||
eu07_application::exit() {
|
||||
|
||||
@@ -159,10 +165,11 @@ eu07_application::exit() {
|
||||
|
||||
ui_layer::shutdown();
|
||||
|
||||
glfwDestroyWindow( m_window );
|
||||
for( auto *window : m_windows ) {
|
||||
glfwDestroyWindow( window );
|
||||
}
|
||||
glfwTerminate();
|
||||
|
||||
TPythonInterpreter::killInstance();
|
||||
m_taskqueue.exit();
|
||||
}
|
||||
|
||||
void
|
||||
@@ -197,7 +204,7 @@ eu07_application::push_mode( eu07_application::mode const Mode ) {
|
||||
void
|
||||
eu07_application::set_title( std::string const &Title ) {
|
||||
|
||||
glfwSetWindowTitle( m_window, Title.c_str() );
|
||||
glfwSetWindowTitle( m_windows.front(), Title.c_str() );
|
||||
}
|
||||
|
||||
void
|
||||
@@ -217,17 +224,13 @@ eu07_application::set_cursor( int const Mode ) {
|
||||
void
|
||||
eu07_application::set_cursor_pos( double const Horizontal, double const Vertical ) {
|
||||
|
||||
if( m_window != nullptr ) {
|
||||
glfwSetCursorPos( m_window, Horizontal, Vertical );
|
||||
}
|
||||
glfwSetCursorPos( m_windows.front(), Horizontal, Vertical );
|
||||
}
|
||||
|
||||
void
|
||||
eu07_application::get_cursor_pos( double &Horizontal, double &Vertical ) const {
|
||||
|
||||
if( m_window != nullptr ) {
|
||||
glfwGetCursorPos( m_window, &Horizontal, &Vertical );
|
||||
}
|
||||
glfwGetCursorPos( m_windows.front(), &Horizontal, &Vertical );
|
||||
}
|
||||
|
||||
void
|
||||
@@ -262,6 +265,24 @@ eu07_application::on_scroll( double const Xoffset, double const Yoffset ) {
|
||||
m_modes[ m_modestack.top() ]->on_scroll( Xoffset, Yoffset );
|
||||
}
|
||||
|
||||
GLFWwindow *
|
||||
eu07_application::window( int const Windowindex ) {
|
||||
|
||||
if( Windowindex >= 0 ) {
|
||||
return (
|
||||
Windowindex < m_windows.size() ?
|
||||
m_windows[ Windowindex ] :
|
||||
nullptr );
|
||||
}
|
||||
// for index -1 create a new child window
|
||||
glfwWindowHint( GLFW_VISIBLE, GL_FALSE );
|
||||
auto *childwindow = glfwCreateWindow( 1, 1, "eu07helper", nullptr, m_windows.front() );
|
||||
if( childwindow != nullptr ) {
|
||||
m_windows.emplace_back( childwindow );
|
||||
}
|
||||
return childwindow;
|
||||
}
|
||||
|
||||
// private:
|
||||
|
||||
void
|
||||
@@ -433,7 +454,7 @@ eu07_application::init_glfw() {
|
||||
// switch off the topmost flag
|
||||
::SetWindowPos( Hwnd, HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE );
|
||||
#endif
|
||||
m_window = window;
|
||||
m_windows.emplace_back( window );
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -441,16 +462,17 @@ eu07_application::init_glfw() {
|
||||
void
|
||||
eu07_application::init_callbacks() {
|
||||
|
||||
glfwSetFramebufferSizeCallback( m_window, window_resize_callback );
|
||||
glfwSetCursorPosCallback( m_window, cursor_pos_callback );
|
||||
glfwSetMouseButtonCallback( m_window, mouse_button_callback );
|
||||
glfwSetKeyCallback( m_window, key_callback );
|
||||
glfwSetScrollCallback( m_window, scroll_callback );
|
||||
glfwSetWindowFocusCallback( m_window, focus_callback );
|
||||
auto *window { m_windows.front() };
|
||||
glfwSetFramebufferSizeCallback( window, window_resize_callback );
|
||||
glfwSetCursorPosCallback( window, cursor_pos_callback );
|
||||
glfwSetMouseButtonCallback( window, mouse_button_callback );
|
||||
glfwSetKeyCallback( window, key_callback );
|
||||
glfwSetScrollCallback( window, scroll_callback );
|
||||
glfwSetWindowFocusCallback( window, focus_callback );
|
||||
{
|
||||
int width, height;
|
||||
glfwGetFramebufferSize( m_window, &width, &height );
|
||||
window_resize_callback( m_window, width, height );
|
||||
glfwGetFramebufferSize( window, &width, &height );
|
||||
window_resize_callback( window, width, height );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -462,8 +484,8 @@ eu07_application::init_gfx() {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if( ( false == GfxRenderer.Init( m_window ) )
|
||||
|| ( false == ui_layer::init( m_window ) ) ) {
|
||||
if( ( false == GfxRenderer.Init( m_windows.front() ) )
|
||||
|| ( false == ui_layer::init( m_windows.front() ) ) ) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ http://mozilla.org/MPL/2.0/.
|
||||
#pragma once
|
||||
|
||||
#include "applicationmode.h"
|
||||
#include "pyint.h"
|
||||
|
||||
class eu07_application {
|
||||
|
||||
@@ -29,6 +30,8 @@ public:
|
||||
init( int Argc, char *Argv[] );
|
||||
int
|
||||
run();
|
||||
bool
|
||||
request( python_taskqueue::task_request const &Task );
|
||||
void
|
||||
exit();
|
||||
void
|
||||
@@ -57,10 +60,9 @@ public:
|
||||
on_mouse_button( int const Button, int const Action, int const Mods );
|
||||
void
|
||||
on_scroll( double const Xoffset, double const Yoffset );
|
||||
inline
|
||||
// gives access to specified window, creates a new window if index == -1
|
||||
GLFWwindow *
|
||||
window() {
|
||||
return m_window; }
|
||||
window( int const Windowindex = 0 );
|
||||
|
||||
private:
|
||||
// types
|
||||
@@ -77,9 +79,10 @@ private:
|
||||
int init_audio();
|
||||
int init_modes();
|
||||
// members
|
||||
GLFWwindow * m_window { nullptr };
|
||||
modeptr_array m_modes { nullptr }; // collection of available application behaviour modes
|
||||
mode_stack m_modestack; // current behaviour mode
|
||||
python_taskqueue m_taskqueue;
|
||||
std::vector<GLFWwindow *> m_windows;
|
||||
};
|
||||
|
||||
extern eu07_application Application;
|
||||
|
||||
2
stdafx.h
2
stdafx.h
@@ -103,6 +103,8 @@
|
||||
#include <glm/gtx/rotate_vector.hpp>
|
||||
#include <glm/gtx/norm.hpp>
|
||||
|
||||
int const null_handle = 0;
|
||||
|
||||
#include "openglmatrixstack.h"
|
||||
#include "openglcolor.h"
|
||||
|
||||
|
||||
57
utilities.h
57
utilities.h
@@ -313,4 +313,61 @@ glm::dvec3 LoadPoint( class cParser &Input );
|
||||
std::string
|
||||
deserialize_random_set( cParser &Input, char const *Break = "\n\r\t ;" );
|
||||
|
||||
namespace threading {
|
||||
|
||||
// simple POD pairing of a data item and a mutex
|
||||
// NOTE: doesn't do any locking itself, it's merely for cleaner argument arrangement and passing
|
||||
template <typename Type_>
|
||||
struct lockable {
|
||||
|
||||
Type_ data;
|
||||
std::mutex mutex;
|
||||
};
|
||||
|
||||
// basic wrapper simplifying use of std::condition_variable for most typical cases.
|
||||
// has its own mutex and secondary variable to ignore spurious wakeups
|
||||
class condition_variable {
|
||||
|
||||
public:
|
||||
// methods
|
||||
void
|
||||
wait() {
|
||||
std::unique_lock<std::mutex> lock( m_mutex );
|
||||
m_condition.wait(
|
||||
lock,
|
||||
[ this ]() {
|
||||
return m_spurious == false; } ); }
|
||||
template< class Rep_, class Period_ >
|
||||
void
|
||||
wait_for( const std::chrono::duration<Rep_, Period_> &Time ) {
|
||||
std::unique_lock<std::mutex> lock( m_mutex );
|
||||
m_condition.wait_for(
|
||||
lock,
|
||||
Time,
|
||||
[ this ]() {
|
||||
return m_spurious == false; } ); }
|
||||
void
|
||||
notify_one() {
|
||||
spurious( false );
|
||||
m_condition.notify_one();
|
||||
}
|
||||
void
|
||||
notify_all() {
|
||||
spurious( false );
|
||||
m_condition.notify_all();
|
||||
}
|
||||
void
|
||||
spurious( bool const Spurious ) {
|
||||
std::lock_guard<std::mutex> lock( m_mutex );
|
||||
m_spurious = Spurious; }
|
||||
|
||||
private:
|
||||
// members
|
||||
mutable std::mutex m_mutex;
|
||||
std::condition_variable m_condition;
|
||||
bool m_spurious { true };
|
||||
};
|
||||
|
||||
} // threading
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user