2017-09-22 19:08:02 +02:00
# include "lc_global.h"
# include "lc_renderdialog.h"
# include "ui_lc_renderdialog.h"
# include "project.h"
# include "lc_application.h"
2017-11-03 01:35:12 +01:00
# include "lc_profile.h"
2023-06-02 00:02:53 +02:00
# include "lc_blenderpreferences.h"
# include "lc_mainwindow.h"
# include "lc_model.h"
# ifdef Q_OS_WIN
# include <TlHelp32.h>
# endif
2017-09-22 19:08:02 +02:00
2017-11-05 02:54:12 +01:00
# define LC_POVRAY_PREVIEW_WIDTH 768
# define LC_POVRAY_PREVIEW_HEIGHT 432
2018-01-15 06:45:50 +01:00
# if defined(Q_OS_WIN) || defined(Q_OS_MACOS)
# define LC_POVRAY_MEMORY_MAPPED_FILE 1
# endif
2017-11-05 02:54:12 +01:00
2023-06-02 00:02:53 +02:00
static inline QString ElapsedTime ( const qint64 & Duration )
{
qint64 Elapsed = Duration ;
int Milliseconds = int ( Elapsed % 1000 ) ;
Elapsed / = 1000 ;
int Seconds = int ( Elapsed % 60 ) ;
Elapsed / = 60 ;
int Minutes = int ( Elapsed % 60 ) ;
Elapsed / = 60 ;
int Hours = int ( Elapsed % 24 ) ;
return QObject : : tr ( " Elapsed time: %1%2%3 " )
. arg ( Hours > 0 ? QString ( " %1 %2 " ) . arg ( Hours ) . arg ( Hours > 1 ? QObject : : tr ( " hours " ) : QObject : : tr ( " hour " ) ) : QString ( ) )
. arg ( Minutes > 0 ? QString ( " %1 %2 " ) . arg ( Minutes ) . arg ( Minutes > 1 ? QObject : : tr ( " minutes " ) : QObject : : tr ( " minute " ) ) : QString ( ) )
. arg ( QString ( " %1.%2 %3 " ) . arg ( Seconds ) . arg ( Milliseconds , 3 , 10 , QLatin1Char ( ' 0 ' ) ) . arg ( Seconds > 1 ? QObject : : tr ( " seconds " ) : QObject : : tr ( " second " ) ) ) ;
}
2019-09-14 21:05:30 +02:00
void lcRenderPreviewWidget : : resizeEvent ( QResizeEvent * Event )
{
mScaledImage = QImage ( ) ;
QWidget : : resizeEvent ( Event ) ;
}
void lcRenderPreviewWidget : : paintEvent ( QPaintEvent * PaintEvent )
{
Q_UNUSED ( PaintEvent ) ;
QPainter Painter ( this ) ;
if ( ! mImage . isNull ( ) )
{
QSize Size = size ( ) ;
if ( mScaledImage . isNull ( ) )
mScaledImage = mImage . scaled ( Size , Qt : : KeepAspectRatio , Qt : : SmoothTransformation ) ;
Painter . drawImage ( ( Size . width ( ) - mScaledImage . width ( ) ) / 2 , ( Size . height ( ) - mScaledImage . height ( ) ) / 2 , mScaledImage ) ;
}
else
Painter . fillRect ( rect ( ) , Qt : : white ) ;
}
2023-06-02 00:02:53 +02:00
lcRenderDialog : : lcRenderDialog ( QWidget * Parent , int Command )
2017-09-22 19:08:02 +02:00
: QDialog ( Parent ) ,
2023-06-02 00:02:53 +02:00
mCommand ( Command ) ,
2019-03-19 23:08:03 +01:00
ui ( new Ui : : lcRenderDialog )
2017-09-22 19:08:02 +02:00
{
2017-12-07 07:08:56 +01:00
# ifndef QT_NO_PROCESS
2017-09-22 19:08:02 +02:00
mProcess = nullptr ;
2017-12-07 07:08:56 +01:00
# endif
2018-01-15 19:48:34 +01:00
mOutputBuffer = nullptr ;
2017-09-22 19:08:02 +02:00
ui - > setupUi ( this ) ;
2017-11-04 00:01:30 +01:00
2023-06-02 00:02:53 +02:00
mWidth = lcGetProfileInt ( LC_PROFILE_RENDER_WIDTH ) ;
mHeight = lcGetProfileInt ( LC_PROFILE_RENDER_HEIGHT ) ;
mScale = 1.0f ;
ui - > WidthEdit - > setText ( QString : : number ( mWidth ) ) ;
2017-11-04 00:01:30 +01:00
ui - > WidthEdit - > setValidator ( new QIntValidator ( 16 , INT_MAX ) ) ;
2023-06-02 00:02:53 +02:00
ui - > HeightEdit - > setText ( QString : : number ( mHeight ) ) ;
2017-11-04 00:01:30 +01:00
ui - > HeightEdit - > setValidator ( new QIntValidator ( 16 , INT_MAX ) ) ;
2018-04-15 03:27:16 +02:00
ui - > OutputEdit - > setText ( lcGetActiveProject ( ) - > GetImageFileName ( false ) ) ;
2017-11-04 00:01:30 +01:00
2023-06-02 00:02:53 +02:00
lcModel * Model = lcGetActiveProject ( ) - > GetActiveModel ( ) ;
QString LabelMessage = tr ( " Render image: " ) ;
if ( Model )
LabelMessage = tr ( " Render <b>STEP %1</b> image: " ) . arg ( Model - > GetCurrentStep ( ) ) ;
ui - > renderLabel - > setText ( LabelMessage ) ;
ui - > RenderOutputButton - > setEnabled ( false ) ;
if ( mCommand = = POVRAY_RENDER )
{
setWindowTitle ( tr ( " POV-Ray Image Render " ) ) ;
ui - > RenderButton - > setToolTip ( tr ( " Render LDraw model " ) ) ;
QImage Image ( LC_POVRAY_PREVIEW_WIDTH , LC_POVRAY_PREVIEW_HEIGHT , QImage : : Format_RGB32 ) ;
Image . fill ( QColor ( 255 , 255 , 255 ) ) ;
ui - > preview - > SetImage ( Image ) ;
ui - > RenderSettingsButton - > hide ( ) ;
}
else
{
setWindowTitle ( tr ( " Blender %1 " ) . arg ( mCommand = = OPEN_IN_BLENDER ? tr ( " LDraw Import " ) : tr ( " Image Render " ) ) ) ;
bool BlenderConfigured = ! lcGetProfileString ( LC_PROFILE_BLENDER_IMPORT_MODULE ) . isEmpty ( ) ;
QStringList const & DataPathList = QStandardPaths : : standardLocations ( QStandardPaths : : DataLocation ) ;
if ( ! QDir ( QString ( " %1/Blender/addons/%2 " ) . arg ( DataPathList . first ( ) ) . arg ( LC_BLENDER_ADDON_FOLDER_STR ) ) . isReadable ( ) )
{
BlenderConfigured = false ;
lcSetProfileString ( LC_PROFILE_BLENDER_IMPORT_MODULE , QString ( ) ) ;
}
if ( BlenderConfigured )
mImportModule = lcGetProfileString ( LC_PROFILE_BLENDER_IMPORT_MODULE ) = = QLatin1String ( " TN " )
? tr ( " LDraw Import TN " )
: tr ( " LDraw Import MM " ) ;
ui - > RenderButton - > setToolTip ( BlenderConfigured
? tr ( " Render LDraw Model " )
: tr ( " Blender not configured. Use Settings... to configure. " ) ) ;
ui - > RenderButton - > setEnabled ( BlenderConfigured ) ;
ui - > RenderSettingsButton - > setToolTip ( tr ( " Blender render settings " ) ) ;
ui - > qualityLabel - > hide ( ) ;
ui - > QualityComboBox - > hide ( ) ;
if ( mCommand = = OPEN_IN_BLENDER )
{
LabelMessage = tr ( " Open%1 in Blender using %2: " )
. arg ( Model ? tr ( " <b>STEP %1</b> " ) . arg ( Model - > GetCurrentStep ( ) ) : " " ) . arg ( mImportModule ) ;
ui - > RenderSettingsButton - > setToolTip ( tr ( " Blender import settings " ) ) ;
ui - > RenderButton - > setText ( tr ( " Open in Blender " ) ) ;
ui - > RenderButton - > setFixedWidth ( ui - > RenderButton - > sizeHint ( ) . width ( ) + 20 ) ;
if ( BlenderConfigured )
ui - > RenderButton - > setToolTip ( tr ( " Import and open LDraw model in Blender " ) ) ;
ui - > renderLabel - > setText ( LabelMessage ) ;
ui - > renderLabel - > setAlignment ( Qt : : AlignTrailing | Qt : : AlignVCenter ) ;
ui - > outputLabel - > hide ( ) ;
ui - > OutputEdit - > hide ( ) ;
ui - > RenderProgress - > hide ( ) ;
ui - > OutputBrowseButton - > hide ( ) ;
ui - > RenderOutputButton - > hide ( ) ;
adjustSize ( ) ;
setMinimumWidth ( ui - > preview - > geometry ( ) . width ( ) ) ;
ui - > preview - > hide ( ) ;
}
else
{
QImage Image ( QPixmap ( " :/resources/file_render_blender_logo_1280x720.png " ) . toImage ( ) ) ;
Image = Image . convertToFormat ( QImage : : Format_ARGB32_Premultiplied ) ;
ui - > preview - > SetImage ( Image ) ;
}
connect ( & mUpdateTimer , SIGNAL ( timeout ( ) ) , this , SLOT ( UpdateElapsedTime ( ) ) ) ;
}
2017-11-05 02:54:12 +01:00
2017-09-22 19:08:02 +02:00
connect ( & mUpdateTimer , SIGNAL ( timeout ( ) ) , this , SLOT ( Update ( ) ) ) ;
2023-06-02 00:02:53 +02:00
2017-12-10 01:27:56 +01:00
mUpdateTimer . start ( 500 ) ;
2023-06-02 00:02:53 +02:00
setSizeGripEnabled ( true ) ;
2017-09-22 19:08:02 +02:00
}
lcRenderDialog : : ~ lcRenderDialog ( )
{
delete ui ;
}
2023-06-02 00:02:53 +02:00
QString lcRenderDialog : : GetOutputFileName ( int StdErr ) const
2018-01-15 06:45:50 +01:00
{
2023-06-02 00:02:53 +02:00
QString LogFile = mCommand = = POVRAY_RENDER
? StdErr ? QLatin1String ( " leocad-povray-render.err " ) : QLatin1String ( " leocad-povray-render.out " )
: StdErr ? QLatin1String ( " leocad-blender-render.err " ) : QLatin1String ( " leocad-blender-render.out " ) ;
return QDir ( QDir : : tempPath ( ) ) . absoluteFilePath ( LogFile ) ;
2018-01-15 06:45:50 +01:00
}
2017-11-03 01:35:12 +01:00
QString lcRenderDialog : : GetPOVFileName ( ) const
{
return QDir ( QDir : : tempPath ( ) ) . absoluteFilePath ( " leocad-render.pov " ) ;
}
2023-06-02 00:02:53 +02:00
void lcRenderDialog : : UpdateElapsedTime ( ) const
{
if ( mProcess & & mCommand = = BLENDER_RENDER )
{
QString const RenderType = lcGetProfileString ( LC_PROFILE_BLENDER_VERSION ) . startsWith ( " v3 " )
? QLatin1String ( " Samples " )
: QLatin1String ( " Tiles " ) ;
ui - > renderLabel - > setText ( tr ( " %1: %2/%3, %4 " )
. arg ( RenderType )
. arg ( mBlendProgValue )
. arg ( mBlendProgMax )
. arg ( ElapsedTime ( mRenderTime . elapsed ( ) ) ) ) ;
}
}
2017-11-05 02:54:12 +01:00
void lcRenderDialog : : CloseProcess ( )
2017-11-04 00:01:30 +01:00
{
2017-12-07 07:08:56 +01:00
# ifndef QT_NO_PROCESS
2017-11-05 02:54:12 +01:00
delete mProcess ;
mProcess = nullptr ;
2017-12-07 07:08:56 +01:00
# endif
2018-01-15 06:45:50 +01:00
2023-06-02 00:02:53 +02:00
if ( mCommand = = POVRAY_RENDER )
{
2018-01-15 06:45:50 +01:00
# if LC_POVRAY_MEMORY_MAPPED_FILE
2023-06-02 00:02:53 +02:00
mOutputFile . unmap ( ( uchar * ) mOutputBuffer ) ;
mOutputBuffer = nullptr ;
mOutputFile . close ( ) ;
2018-01-15 06:45:50 +01:00
2023-06-02 00:02:53 +02:00
QFile : : remove ( GetOutputFileName ( ) ) ;
2018-01-15 06:45:50 +01:00
# endif
2023-06-02 00:02:53 +02:00
QFile : : remove ( GetPOVFileName ( ) ) ;
}
2017-11-05 02:54:12 +01:00
2023-06-02 00:02:53 +02:00
if ( mCommand ! = OPEN_IN_BLENDER )
ui - > RenderButton - > setText ( tr ( " Render " ) ) ;
2017-11-04 00:01:30 +01:00
}
2017-11-05 02:54:12 +01:00
bool lcRenderDialog : : PromptCancel ( )
2017-09-22 19:08:02 +02:00
{
2017-12-07 07:08:56 +01:00
# ifndef QT_NO_PROCESS
2017-09-22 19:08:02 +02:00
if ( mProcess )
{
2017-11-05 02:54:12 +01:00
if ( QMessageBox : : question ( this , tr ( " Cancel Render " ) , tr ( " Are you sure you want to cancel the current render? " ) , QMessageBox : : Yes | QMessageBox : : No ) = = QMessageBox : : Yes )
{
2023-06-02 00:02:53 +02:00
if ( mCommand = = POVRAY_RENDER )
gMainWindow - > mActions [ LC_FILE_RENDER_POVRAY ] - > setEnabled ( true ) ;
else if ( mCommand = = BLENDER_RENDER )
gMainWindow - > mActions [ LC_FILE_RENDER_BLENDER ] - > setEnabled ( true ) ;
# ifdef Q_OS_WIN
TerminateChildProcess ( mProcess - > processId ( ) ,
QCoreApplication : : applicationPid ( ) ) ;
# endif
mProcess - > kill ( ) ;
CloseProcess ( ) ;
if ( mStdOutList . size ( ) )
2017-11-05 02:54:12 +01:00
{
2023-06-02 00:02:53 +02:00
WriteStdOut ( ) ;
ui - > RenderOutputButton - > setEnabled ( true ) ;
2017-11-05 02:54:12 +01:00
}
2023-06-02 00:02:53 +02:00
if ( mCommand = = BLENDER_RENDER )
ui - > renderLabel - > setText ( tr ( " Tiles: %1/%2, Render Cancelled. " )
. arg ( mBlendProgValue )
. arg ( mBlendProgMax ) ) ;
2017-11-05 02:54:12 +01:00
}
else
return false ;
2017-09-22 19:08:02 +02:00
}
2017-12-07 07:08:56 +01:00
# endif
2023-06-02 00:02:53 +02:00
2017-11-05 02:54:12 +01:00
return true ;
}
void lcRenderDialog : : reject ( )
{
if ( PromptCancel ( ) )
QDialog : : reject ( ) ;
}
2023-06-02 00:02:53 +02:00
void lcRenderDialog : : on_RenderSettingsButton_clicked ( )
{
if ( mCommand = = POVRAY_RENDER )
return ;
lcBlenderPreferencesDialog : : GetBlenderPreferences ( mWidth , mHeight , mScale , this ) ;
if ( lcGetProfileString ( LC_PROFILE_BLENDER_IMPORT_MODULE ) . isEmpty ( ) )
ui - > RenderButton - > setToolTip ( tr ( " Blender not configured. Use Settings... to configure. " ) ) ;
else
ui - > RenderButton - > setEnabled ( true ) ;
}
2017-11-05 02:54:12 +01:00
void lcRenderDialog : : on_RenderButton_clicked ( )
{
2017-12-07 07:08:56 +01:00
# ifndef QT_NO_PROCESS
2017-12-10 01:27:56 +01:00
if ( mProcess )
{
PromptCancel ( ) ;
2017-11-05 02:54:12 +01:00
return ;
2017-12-10 01:27:56 +01:00
}
2017-11-05 02:54:12 +01:00
2023-06-02 00:02:53 +02:00
ui - > RenderOutputButton - > setEnabled ( false ) ;
2017-09-22 19:08:02 +02:00
2023-06-02 00:02:53 +02:00
mPreviewWidth = ui - > preview - > width ( ) ;
mPreviewHeight = ui - > preview - > height ( ) ;
mRenderTime . start ( ) ;
if ( mCommand = = POVRAY_RENDER )
{
gMainWindow - > mActions [ LC_FILE_RENDER_POVRAY ] - > setEnabled ( false ) ;
QString FileName = GetPOVFileName ( ) ;
QImage Image ( mPreviewWidth , mPreviewHeight , QImage : : Format_RGB32 ) ;
Image . fill ( QColor ( 255 , 255 , 255 ) ) ;
ui - > preview - > SetImage ( Image ) ;
if ( ! lcGetActiveProject ( ) - > ExportPOVRay ( FileName ) )
return ;
2017-09-22 19:08:02 +02:00
2023-06-02 00:02:53 +02:00
QStringList Arguments ;
2017-09-22 19:08:02 +02:00
2023-06-02 00:02:53 +02:00
Arguments . append ( QString : : fromLatin1 ( " +I \" %1 \" " ) . arg ( FileName ) ) ;
Arguments . append ( QString : : fromLatin1 ( " +W%1 " ) . arg ( ui - > WidthEdit - > text ( ) ) ) ;
Arguments . append ( QString : : fromLatin1 ( " +H%1 " ) . arg ( ui - > HeightEdit - > text ( ) ) ) ;
Arguments . append ( " -O- " ) ;
2017-09-22 19:08:02 +02:00
2018-01-15 06:45:50 +01:00
# if LC_POVRAY_MEMORY_MAPPED_FILE
2023-06-02 00:02:53 +02:00
Arguments . append ( QString : : fromLatin1 ( " +SM \" %1 \" " ) . arg ( GetOutputFileName ( ) ) ) ;
2018-01-15 06:45:50 +01:00
# endif
2023-06-02 00:02:53 +02:00
int Quality = ui - > QualityComboBox - > currentIndex ( ) ;
2017-12-27 22:55:37 +01:00
2023-06-02 00:02:53 +02:00
switch ( Quality )
{
case 0 :
Arguments . append ( " +Q11 " ) ;
Arguments . append ( " +R3 " ) ;
Arguments . append ( " +A0.1 " ) ;
Arguments . append ( " +J0.5 " ) ;
break ;
case 1 :
Arguments . append ( " +Q5 " ) ;
Arguments . append ( " +A0.1 " ) ;
break ;
case 2 :
break ;
}
2017-09-22 19:08:02 +02:00
2023-06-02 00:02:53 +02:00
/*
if ( ! LGEOPath . isEmpty ( ) )
{
Arguments . append ( QString : : fromLatin1 ( " +L%1lg/ " ) . arg ( LGEOPath ) ) ;
Arguments . append ( QString : : fromLatin1 ( " +L%1ar/ " ) . arg ( LGEOPath ) ) ;
}
*/
2017-09-22 19:08:02 +02:00
2023-06-02 00:02:53 +02:00
QString POVRayPath ;
2017-11-03 01:35:12 +01:00
# ifdef Q_OS_WIN
2023-06-02 00:02:53 +02:00
POVRayPath = QDir : : cleanPath ( QCoreApplication : : applicationDirPath ( ) + QLatin1String ( " /povconsole32-sse2.exe " ) ) ;
2017-11-03 01:35:12 +01:00
# endif
# ifdef Q_OS_LINUX
2023-06-02 00:02:53 +02:00
POVRayPath = lcGetProfileString ( LC_PROFILE_POVRAY_PATH ) ;
Arguments . append ( " +FN " ) ;
Arguments . append ( " -D " ) ;
2017-11-03 01:35:12 +01:00
# endif
# ifdef Q_OS_MACOS
2023-06-02 00:02:53 +02:00
POVRayPath = QDir : : cleanPath ( QCoreApplication : : applicationDirPath ( ) + QLatin1String ( " /povray " ) ) ;
2017-11-03 01:35:12 +01:00
# endif
2017-09-22 19:08:02 +02:00
2023-06-02 00:02:53 +02:00
mProcess = new RenderProcess ( this ) ;
2019-03-19 23:08:03 +01:00
# ifdef Q_OS_LINUX
2023-06-02 00:02:53 +02:00
connect ( mProcess , SIGNAL ( readyReadStandardError ( ) ) , this , SLOT ( ReadStdErr ( ) ) ) ;
# endif
int const StdErr = 1 ;
QStringList POVEnv = QProcess : : systemEnvironment ( ) ;
POVEnv . prepend ( " POV_IGNORE_SYSCONF_MSG=1 " ) ;
mProcess - > setEnvironment ( POVEnv ) ;
mProcess - > setStandardErrorFile ( GetOutputFileName ( StdErr ) ) ;
mProcess - > start ( POVRayPath , Arguments ) ;
mImage = QImage ( ui - > WidthEdit - > text ( ) . toInt ( ) , ui - > HeightEdit - > text ( ) . toInt ( ) , QImage : : Format_ARGB32 ) ;
mImage . fill ( QColor ( 255 , 255 , 255 ) ) ;
ui - > preview - > SetImage ( mImage ) ;
if ( mProcess - > waitForStarted ( ) )
{
ui - > RenderButton - > setText ( tr ( " Cancel " ) ) ;
ui - > RenderProgress - > setValue ( ui - > RenderProgress - > minimum ( ) ) ;
mStdErrList . clear ( ) ;
}
else
{
gMainWindow - > mActions [ LC_FILE_RENDER_POVRAY ] - > setEnabled ( true ) ;
QMessageBox : : warning ( this , tr ( " Error " ) , tr ( " Error starting POV-Ray. " ) ) ;
CloseProcess ( ) ;
}
}
else
{
QString const BlenderLDrawConfigFile = lcGetProfileString ( LC_PROFILE_BLENDER_LDRAW_CONFIG_PATH ) ;
QString const BlenderImportModule = lcGetProfileString ( LC_PROFILE_BLENDER_IMPORT_MODULE ) ;
if ( ! QFileInfo ( BlenderLDrawConfigFile ) . isReadable ( ) & & ! BlenderImportModule . isEmpty ( ) )
lcBlenderPreferences : : SaveSettings ( ) ;
QString const Option = mCommand = = OPEN_IN_BLENDER ? tr ( " import " ) : tr ( " render " ) ;
ui - > renderLabel - > setText ( tr ( " Saving Blender %1 model... " ) . arg ( Option ) ) ;
QApplication : : processEvents ( ) ;
mBlendProgValue = 0 ;
mBlendProgMax = 0 ;
QStringList const & DataPathList = QStandardPaths : : standardLocations ( QStandardPaths : : DataLocation ) ;
mDataPath = DataPathList . first ( ) ;
QString const DefaultBlendFile = QString ( " %1/blender/config/%2 " ) . arg ( mDataPath ) . arg ( LC_BLENDER_ADDON_BLEND_FILE ) ;
lcModel * Model = lcGetActiveProject ( ) - > GetActiveModel ( ) ;
QString const ModelFileName = QFileInfo ( QDir ( lcGetProfileString ( LC_PROFILE_PROJECTS_PATH ) ) , QString ( " %1_Step_%2.ldr " ) . arg ( QFileInfo ( Model - > GetProperties ( ) . mFileName ) . baseName ( ) ) . arg ( Model - > GetCurrentStep ( ) ) ) . absoluteFilePath ( ) ;
lcGetActiveProject ( ) - > ExportCurrentStep ( ModelFileName ) ;
if ( ! QFileInfo ( ModelFileName ) . isReadable ( ) )
return ;
bool SearchCustomDir = true ;
QString Message ;
QStringList Arguments ;
QString PythonExpression = QString ( " \" import bpy; bpy.ops.render_scene.lpub3d_render_ldraw( "
" 'EXEC_DEFAULT', "
" resolution_width=%1, resolution_height=%2, "
" render_percentage=%3, model_file=r'%4', "
" image_file=r'%5', preferences_file=r'%6' " )
. arg ( mWidth ) . arg ( mHeight )
. arg ( mScale * 100 )
. arg ( QDir : : toNativeSeparators ( ModelFileName ) . replace ( " \\ " , " \\ \\ " ) )
. arg ( QDir : : toNativeSeparators ( ui - > OutputEdit - > text ( ) ) . replace ( " \\ " , " \\ \\ " ) )
. arg ( QDir : : toNativeSeparators ( BlenderLDrawConfigFile ) . replace ( " \\ " , " \\ \\ " ) ) ;
if ( BlenderImportModule = = QLatin1String ( " MM " ) )
PythonExpression . append ( " , use_ldraw_import_mm=True " ) ;
if ( SearchCustomDir )
PythonExpression . append ( " , search_additional_paths=True " ) ;
if ( mCommand = = OPEN_IN_BLENDER )
{
PythonExpression . append ( " , import_only=True " ) ;
Arguments < < QLatin1String ( " --window-geometry " ) ;
Arguments < < QLatin1String ( " 200 100 1440 900 " ) ;
}
else
{
Arguments < < QLatin1String ( " --background " ) ;
gMainWindow - > mActions [ LC_FILE_RENDER_BLENDER ] - > setEnabled ( false ) ;
}
PythonExpression . append ( " , cli_render=True) \" " ) ;
if ( QFileInfo ( DefaultBlendFile ) . exists ( ) )
Arguments < < QDir : : toNativeSeparators ( DefaultBlendFile ) ;
Arguments < < QString ( " --python-expr " ) ;
Arguments < < PythonExpression ;
QString ScriptName , ScriptCommand , ShellProgram ;
# ifdef Q_OS_WIN
ScriptName = QLatin1String ( " render_ldraw_model.bat " ) ;
# else
ScriptName = QLatin1String ( " render_ldraw_model.sh " ) ;
# endif
ScriptCommand = QString ( " %1 %2 " ) . arg ( lcGetProfileString ( LC_PROFILE_BLENDER_PATH ) ) . arg ( Arguments . join ( " " ) ) ;
if ( mCommand = = OPEN_IN_BLENDER )
ScriptCommand . append ( QString ( " > %1 " ) . arg ( QDir : : toNativeSeparators ( GetOutputFileName ( ) ) ) ) ;
const QLatin1String LineEnding ( " \r \n " ) ;
QFile Script ( QString ( " %1/%2 " ) . arg ( QDir : : tempPath ( ) ) . arg ( ScriptName ) ) ;
if ( Script . open ( QIODevice : : WriteOnly | QIODevice : : Text ) )
{
QTextStream Stream ( & Script ) ;
# ifdef Q_OS_WIN
Stream < < QLatin1String ( " @ECHO OFF &SETLOCAL " ) < < LineEnding ;
# else
Stream < < QLatin1String ( " #!/bin/bash " ) < < LineEnding ;
# endif
Stream < < ScriptCommand < < LineEnding ;
Script . close ( ) ;
}
else
{
QMessageBox : : warning ( this , tr ( " Error " ) , tr ( " Cannot write Blender render script file [%1] %2. " )
. arg ( Script . fileName ( ) )
. arg ( Script . errorString ( ) ) ) ;
gMainWindow - > mActions [ LC_FILE_RENDER_BLENDER ] - > setEnabled ( true ) ;
return ;
}
QThread : : sleep ( 2 ) ;
# ifdef Q_OS_WIN
ShellProgram = QLatin1String ( LC_WINDOWS_SHELL ) ;
# else
ShellProgram = QLatin1String ( LC_UNIX_SHELL ) ;
2019-03-19 23:08:03 +01:00
# endif
2017-11-03 01:35:12 +01:00
2023-06-02 00:02:53 +02:00
mProcess = new RenderProcess ( this ) ;
connect ( mProcess , SIGNAL ( readyReadStandardOutput ( ) ) , this , SLOT ( ReadStdOut ( ) ) ) ;
QString const LDrawLibPath = QFileInfo ( lcGetProfileString ( LC_PROFILE_PARTS_LIBRARY ) ) . absolutePath ( ) ;
QStringList SystemEnvironment = QProcess : : systemEnvironment ( ) ;
SystemEnvironment . prepend ( " LDRAW_DIRECTORY= " + LDrawLibPath ) ;
int const StdErr = 1 ;
mProcess - > setEnvironment ( SystemEnvironment ) ;
mProcess - > setWorkingDirectory ( QDir : : toNativeSeparators ( QString ( " %1/blender " ) . arg ( mDataPath ) ) ) ;
mProcess - > setStandardErrorFile ( GetOutputFileName ( StdErr ) ) ;
2019-09-14 21:05:30 +02:00
2023-06-02 00:02:53 +02:00
if ( mCommand = = OPEN_IN_BLENDER )
{
QFileInfo Info ( GetOutputFileName ( ) ) ;
if ( Info . exists ( ) )
QFile : : remove ( Info . absoluteFilePath ( ) ) ;
# ifdef Q_OS_WIN
mProcess - > startDetached ( ShellProgram , QStringList ( ) < < " /C " < < Script . fileName ( ) ) ;
# else
mProcess - > startDetached ( ShellProgram , QStringList ( ) < < Script . fileName ( ) ) ;
# endif
if ( mProcess )
{
mProcess - > kill ( ) ;
CloseProcess ( ) ;
if ( mStdOutList . size ( ) )
WriteStdOut ( ) ;
if ( Info . exists ( ) )
{
QFile Log ( Info . absoluteFilePath ( ) ) ;
QTime Wait = QTime : : currentTime ( ) . addSecs ( 3 ) ;
while ( ! Log . size ( ) | | QTime : : currentTime ( ) < Wait )
QCoreApplication : : processEvents ( QEventLoop : : AllEvents , 100 ) ;
if ( Log . size ( ) ) {
if ( Log . open ( QFile : : ReadOnly | QFile : : Text ) )
{
QByteArray Ba = Log . readAll ( ) ;
bool const Error = QString ( Ba ) . contains ( QRegExp ( " (?: \\ w)*ERROR: " , Qt : : CaseInsensitive ) ) ;
bool const Warning = QString ( Ba ) . contains ( QRegExp ( " (?: \\ w)*WARNING: " , Qt : : CaseInsensitive ) ) ;
if ( Error | | Warning )
{
QMessageBox : : Icon Icon = Error ? QMessageBox : : Critical : QMessageBox : : Warning ;
QString const & Items = Error ? tr ( " errors%1 " ) . arg ( Warning ? tr ( " and warnings " ) : " " ) : Warning ? tr ( " warnings " ) : " " ;
QString const & Title = tr ( " Open in Blender output " ) ;
QString const & Body = tr ( " Open in Blender encountered %1. See Show Details... " ) . arg ( Items ) ;
lcBlenderPreferences : : ShowMessage ( Body , Title , QString ( ) , QString ( Ba ) , 0 , Icon ) ;
}
}
}
}
close ( ) ;
return ;
}
}
else
{
# ifdef Q_OS_WIN
mProcess - > start ( ShellProgram , QStringList ( ) < < " /C " < < Script . fileName ( ) ) ;
# else
mProcess - > start ( ShellProgram , QStringList ( ) < < Script . fileName ( ) ) ;
# endif
}
if ( mProcess - > waitForStarted ( ) )
{
ui - > RenderButton - > setText ( tr ( " Cancel " ) ) ;
ui - > RenderProgress - > setValue ( ui - > RenderProgress - > minimum ( ) ) ;
ui - > renderLabel - > setText ( tr ( " Loading LDraw model... %1 " )
. arg ( ElapsedTime ( mRenderTime . elapsed ( ) ) ) ) ;
QApplication : : processEvents ( ) ;
}
else
{
gMainWindow - > mActions [ LC_FILE_RENDER_BLENDER ] - > setEnabled ( true ) ;
Message = tr ( " Error starting Blender render process " ) ;
QMessageBox : : warning ( this , tr ( " Error " ) , Message ) ;
CloseProcess ( ) ;
}
} // BLENDER_RENDER
# endif
}
# ifdef Q_OS_WIN
int lcRenderDialog : : TerminateChildProcess ( const qint64 Pid , const qint64 Ppid )
{
DWORD pID = DWORD ( Pid ) ;
DWORD ppID = DWORD ( Ppid ) ;
HANDLE hSnapshot = INVALID_HANDLE_VALUE , hProcess = INVALID_HANDLE_VALUE ;
PROCESSENTRY32 pe32 ;
if ( ( hSnapshot = CreateToolhelp32Snapshot ( TH32CS_SNAPPROCESS , ppID ) ) = = INVALID_HANDLE_VALUE )
2019-03-08 12:42:36 +01:00
{
2023-06-02 00:02:53 +02:00
QMessageBox : : warning ( this , tr ( " Error " ) , QString ( " %1 failed: %1 " ) . arg ( " CreateToolhelp32Snapshot " ) . arg ( GetLastError ( ) ) ) ;
return - 1 ;
2019-03-08 12:42:36 +01:00
}
2023-06-02 00:02:53 +02:00
pe32 . dwSize = sizeof ( PROCESSENTRY32 ) ;
if ( Process32First ( hSnapshot , & pe32 ) = = FALSE )
2017-11-03 01:35:12 +01:00
{
2023-06-02 00:02:53 +02:00
QMessageBox : : warning ( this , tr ( " Error " ) , QString ( " %1 failed: %2 " ) . arg ( " Process32First " ) . arg ( GetLastError ( ) ) ) ;
CloseHandle ( hSnapshot ) ;
return - 2 ;
}
do
{
if ( QString : : fromWCharArray ( pe32 . szExeFile ) . contains ( QRegExp ( " ^(?:cmd \\ .exe|conhost \\ .exe|blender \\ .exe)$ " , Qt : : CaseInsensitive ) ) )
{
if ( ( pe32 . th32ProcessID = = pID & & pe32 . th32ParentProcessID = = ppID ) | | pe32 . th32ParentProcessID = = pID )
{
if ( ( hProcess = OpenProcess ( PROCESS_TERMINATE , FALSE , pe32 . th32ProcessID ) ) = = INVALID_HANDLE_VALUE )
{
QMessageBox : : warning ( this , tr ( " Error " ) , tr ( " %1 failed: %2 " ) . arg ( " OpenProcess " ) . arg ( GetLastError ( ) ) ) ;
return - 3 ;
}
else
{
TerminateProcess ( hProcess , 9 ) ;
CloseHandle ( hProcess ) ;
}
}
}
2017-11-03 01:35:12 +01:00
}
2023-06-02 00:02:53 +02:00
while ( Process32Next ( hSnapshot , & pe32 ) ) ;
CloseHandle ( hSnapshot ) ;
return 0 ;
}
2017-12-07 07:08:56 +01:00
# endif
2023-06-02 00:02:53 +02:00
void lcRenderDialog : : ReadStdOut ( )
{
if ( mCommand = = POVRAY_RENDER )
return ;
QString StdOut = QString ( mProcess - > readAllStandardOutput ( ) ) ;
mStdOutList . append ( StdOut ) ;
QRegExp RxRenderProgress ;
RxRenderProgress . setCaseSensitivity ( Qt : : CaseInsensitive ) ;
bool BlenderVersion3 = lcGetProfileString ( LC_PROFILE_BLENDER_VERSION ) . startsWith ( " v3 " ) ;
if ( BlenderVersion3 )
RxRenderProgress . setPattern ( " Sample ( \\ d+) \\ /( \\ d+) " ) ;
else
RxRenderProgress . setPattern ( " ( \\ d+) \\ /( \\ d+) Tiles " ) ;
if ( StdOut . contains ( RxRenderProgress ) )
{
mBlendProgValue = RxRenderProgress . cap ( 1 ) . toInt ( ) ;
mBlendProgMax = RxRenderProgress . cap ( 2 ) . toInt ( ) ;
ui - > RenderProgress - > setMaximum ( mBlendProgMax ) ;
ui - > RenderProgress - > setValue ( mBlendProgValue ) ;
}
2017-09-22 19:08:02 +02:00
}
2023-06-02 00:02:53 +02:00
QString lcRenderDialog : : ReadStdErr ( bool & HasError ) const
2019-03-08 12:42:36 +01:00
{
2023-06-02 00:02:53 +02:00
HasError = mCommand = = BLENDER_RENDER ? false : true ;
QFile File ;
QStringList returnLines ;
int const StdErr = 1 ;
File . setFileName ( GetOutputFileName ( StdErr ) ) ;
if ( ! File . open ( QFile : : ReadOnly | QFile : : Text ) )
2019-03-08 12:42:36 +01:00
{
2023-06-02 00:02:53 +02:00
QString message = tr ( " Failed to open log file: %1: \n %2 " )
. arg ( File . fileName ( ) )
. arg ( File . errorString ( ) ) ;
return message ;
}
QTextStream In ( & File ) ;
while ( ! In . atEnd ( ) )
{
QString Line = In . readLine ( 0 ) ;
returnLines < < Line . trimmed ( ) + " <br> " ;
if ( mCommand = = POVRAY_RENDER )
{
if ( Line . contains ( QRegExp ( " ^POV-Ray finished$ " , Qt : : CaseSensitive ) ) )
HasError = false ;
}
else if ( mCommand = = BLENDER_RENDER )
{
if ( ! HasError & & ! Line . isEmpty ( ) )
HasError = true ;
}
}
return returnLines . join ( " " ) ;
}
void lcRenderDialog : : WriteStdOut ( )
{
QFile File ( GetOutputFileName ( ) ) ;
if ( File . open ( QFile : : WriteOnly | QIODevice : : Truncate | QFile : : Text ) )
{
QTextStream Out ( & File ) ;
for ( const QString & Line : mStdOutList )
Out < < Line ;
File . close ( ) ;
ui - > RenderOutputButton - > setEnabled ( true ) ;
2019-03-08 12:42:36 +01:00
}
}
2017-09-22 19:08:02 +02:00
void lcRenderDialog : : Update ( )
{
2017-12-07 07:08:56 +01:00
# ifndef QT_NO_PROCESS
2017-12-10 01:27:56 +01:00
if ( ! mProcess )
return ;
if ( mProcess - > state ( ) = = QProcess : : NotRunning )
2017-09-22 19:08:02 +02:00
{
2017-11-03 01:35:12 +01:00
# ifdef Q_OS_LINUX
2017-12-10 01:27:56 +01:00
QByteArray Output = mProcess - > readAllStandardOutput ( ) ;
2019-03-08 12:42:36 +01:00
mImage = QImage : : fromData ( Output ) ;
2017-11-03 01:35:12 +01:00
# endif
2019-03-08 12:42:36 +01:00
2019-09-08 21:19:00 +02:00
ShowResult ( ) ;
2017-12-10 01:27:56 +01:00
CloseProcess ( ) ;
2017-09-22 19:08:02 +02:00
}
2017-12-07 07:08:56 +01:00
# endif
2017-11-03 01:35:12 +01:00
2023-06-02 00:02:53 +02:00
if ( mCommand = = POVRAY_RENDER )
2018-01-15 06:45:50 +01:00
{
2023-06-02 00:02:53 +02:00
# if LC_POVRAY_MEMORY_MAPPED_FILE
if ( ! mOutputBuffer )
{
mOutputFile . setFileName ( GetOutputFileName ( ) ) ;
2017-09-22 19:08:02 +02:00
2023-06-02 00:02:53 +02:00
if ( ! mOutputFile . open ( QFile : : ReadWrite ) )
return ;
2017-09-22 19:08:02 +02:00
2023-06-02 00:02:53 +02:00
mOutputBuffer = mOutputFile . map ( 0 , mOutputFile . size ( ) ) ;
2018-01-15 06:45:50 +01:00
2023-06-02 00:02:53 +02:00
if ( ! mOutputBuffer )
{
mOutputFile . close ( ) ;
return ;
}
2018-01-15 06:45:50 +01:00
}
2017-09-22 19:08:02 +02:00
2023-06-02 00:02:53 +02:00
struct lcSharedMemoryHeader
{
quint32 Version ;
quint32 Width ;
quint32 Height ;
quint32 PixelsWritten ;
quint32 PixelsRead ;
} ;
2017-09-22 19:08:02 +02:00
2023-06-02 00:02:53 +02:00
lcSharedMemoryHeader * Header = ( lcSharedMemoryHeader * ) mOutputBuffer ;
2017-09-22 19:08:02 +02:00
2023-06-02 00:02:53 +02:00
if ( Header - > PixelsWritten = = Header - > PixelsRead )
return ;
2017-12-10 01:27:56 +01:00
2023-06-02 00:02:53 +02:00
int Width = Header - > Width ;
int Height = Header - > Height ;
int PixelsWritten = Header - > PixelsWritten ;
2017-09-22 19:08:02 +02:00
2023-06-02 00:02:53 +02:00
if ( ! Header - > PixelsRead )
mImage = QImage ( Width , Height , QImage : : Format_ARGB32 ) ;
2017-09-22 19:08:02 +02:00
2023-06-02 00:02:53 +02:00
quint8 * Pixels = ( quint8 * ) ( Header + 1 ) ;
for ( int y = 0 ; y < Height ; y + + )
2017-09-22 19:08:02 +02:00
{
2023-06-02 00:02:53 +02:00
for ( int x = 0 ; x < Width ; x + + )
{
mImage . setPixel ( x , y , qRgba ( Pixels [ 0 ] , Pixels [ 1 ] , Pixels [ 2 ] , Pixels [ 3 ] ) ) ;
Pixels + = 4 ;
}
2017-09-22 19:08:02 +02:00
}
2023-06-02 00:02:53 +02:00
Header - > PixelsRead = PixelsWritten ;
ui - > RenderProgress - > setMaximum ( mImage . width ( ) * mImage . height ( ) ) ;
ui - > RenderProgress - > setValue ( int ( Header - > PixelsRead ) ) ;
2017-09-22 19:08:02 +02:00
2023-06-02 00:02:53 +02:00
if ( PixelsWritten = = Width * Height )
ui - > RenderProgress - > setValue ( ui - > RenderProgress - > maximum ( ) ) ;
2019-03-19 23:08:03 +01:00
2023-06-02 00:02:53 +02:00
ui - > preview - > SetImage ( mImage . scaled ( mPreviewWidth , mPreviewHeight , Qt : : KeepAspectRatio , Qt : : SmoothTransformation ) ) ;
/*ui->preview->SetImage(mImage);*/
2019-03-08 12:42:36 +01:00
# endif
2023-06-02 00:02:53 +02:00
}
2019-03-08 12:42:36 +01:00
}
2017-12-27 22:55:37 +01:00
2019-03-10 01:38:54 +01:00
void lcRenderDialog : : ShowResult ( )
{
2023-06-02 00:02:53 +02:00
# ifndef QT_NO_PROCESS
bool Error ;
const QString StdErrLog = ReadStdErr ( Error ) ;
2019-03-08 12:42:36 +01:00
2023-06-02 00:02:53 +02:00
const QString RenderLabel = mCommand = = BLENDER_RENDER ? tr ( " Blender Render " ) : tr ( " POV-Ray Render " ) ;
if ( mProcess - > exitStatus ( ) ! = QProcess : : NormalExit | | mProcess - > exitCode ( ) ! = 0 | | Error )
2019-03-10 01:38:54 +01:00
{
2023-06-02 00:02:53 +02:00
ui - > renderLabel - > setText ( tr ( " Image generation failed. " ) ) ;
ui - > RenderProgress - > setRange ( 0 , 1 ) ;
ui - > RenderProgress - > setValue ( 0 ) ;
QString const & Title = mCommand = = BLENDER_RENDER ? tr ( " Blender Render " ) : tr ( " POV-Ray Render " ) ;
QString const & Body = tr ( " An error occurred while rendering. See Show Details... " ) ;
lcBlenderPreferences : : ShowMessage ( Body , Title , QString ( ) , StdErrLog , 0 , QMessageBox : : Warning ) ;
2019-03-08 12:42:36 +01:00
return ;
}
2023-06-02 00:02:53 +02:00
else
{
ui - > RenderProgress - > setValue ( ui - > RenderProgress - > maximum ( ) ) ;
}
# endif
2017-12-27 22:55:37 +01:00
2023-06-02 00:02:53 +02:00
bool Success = false ;
2017-12-27 22:55:37 +01:00
2019-03-08 12:42:36 +01:00
QString FileName = ui - > OutputEdit - > text ( ) ;
2023-06-02 00:02:53 +02:00
if ( mCommand = = POVRAY_RENDER )
2019-03-08 12:42:36 +01:00
{
2023-06-02 00:02:53 +02:00
gMainWindow - > mActions [ LC_FILE_RENDER_POVRAY ] - > setEnabled ( true ) ;
2019-03-08 12:42:36 +01:00
2023-06-02 00:02:53 +02:00
ui - > preview - > SetImage ( mImage . scaled ( mPreviewWidth , mPreviewHeight , Qt : : KeepAspectRatio , Qt : : SmoothTransformation ) ) ;
2019-03-19 23:08:03 +01:00
2023-06-02 00:02:53 +02:00
if ( ! FileName . isEmpty ( ) )
{
QImageWriter Writer ( FileName ) ;
2019-03-19 23:08:03 +01:00
2023-06-02 00:02:53 +02:00
Success = Writer . write ( mImage ) ;
2019-03-19 23:22:47 +01:00
2023-06-02 00:02:53 +02:00
if ( ! Success )
QMessageBox : : warning ( this , tr ( " Error " ) , tr ( " Error writing to file '%1': \n %2 " ) . arg ( FileName , Writer . errorString ( ) ) ) ;
}
2019-03-19 23:22:47 +01:00
2023-06-02 00:02:53 +02:00
}
else if ( mCommand = = BLENDER_RENDER )
2019-03-19 23:22:47 +01:00
{
2023-06-02 00:02:53 +02:00
gMainWindow - > mActions [ LC_FILE_RENDER_BLENDER ] - > setEnabled ( true ) ;
Success = QFileInfo ( FileName ) . exists ( ) ;
if ( Success )
{
setMinimumSize ( 100 , 100 ) ;
QImageReader Reader ( FileName ) ;
mImage = Reader . read ( ) ;
mImage = mImage . convertToFormat ( QImage : : Format_ARGB32_Premultiplied ) ;
mPreviewWidth = mImage . width ( ) ;
mPreviewHeight = mImage . height ( ) ;
ui - > preview - > SetImage ( mImage . scaled ( mPreviewWidth , mPreviewHeight , Qt : : KeepAspectRatio , Qt : : SmoothTransformation ) ) ;
}
2019-03-19 23:22:47 +01:00
}
2023-06-02 00:02:53 +02:00
if ( ! Success )
2019-03-19 23:22:47 +01:00
{
2023-06-02 00:02:53 +02:00
ui - > renderLabel - > setStyleSheet ( " QLabel { color : red; } " ) ;
ui - > renderLabel - > setText ( tr ( " Image render failed. " ) ) ;
QString const Message = QString ( " %1 %2. %3 " ) . arg ( RenderLabel ) . arg ( tr ( " failed (unknown reason) " ) )
. arg ( ElapsedTime ( mRenderTime . elapsed ( ) ) ) ;
QMessageBox : : warning ( this , tr ( " Error " ) , Message ) ;
2019-03-19 23:08:03 +01:00
}
2023-06-02 00:02:53 +02:00
WriteStdOut ( ) ;
2017-09-22 19:08:02 +02:00
}
2017-12-27 22:55:37 +01:00
void lcRenderDialog : : on_OutputBrowseButton_clicked ( )
{
QString Result = QFileDialog : : getSaveFileName ( this , tr ( " Select Output File " ) , ui - > OutputEdit - > text ( ) , tr ( " Supported Image Files (*.bmp *.png *.jpg);;BMP Files (*.bmp);;PNG Files (*.png);;JPEG Files (*.jpg);;All Files (*.*) " ) ) ;
if ( ! Result . isEmpty ( ) )
ui - > OutputEdit - > setText ( QDir : : toNativeSeparators ( Result ) ) ;
2023-06-02 00:02:53 +02:00
}
2017-12-27 22:55:37 +01:00
2023-06-02 00:02:53 +02:00
void lcRenderDialog : : on_RenderOutputButton_clicked ( )
{
QString RenderType = mCommand = = POVRAY_RENDER ? QLatin1String ( " POV-Ray " ) : QLatin1String ( " Blender " ) ;
QFileInfo FileInfo ( GetOutputFileName ( ) ) ;
if ( ! FileInfo . exists ( ) )
{
QMessageBox : : warning ( this , tr ( " Error " ) , tr ( " %1 Standard output file not found: %2. " )
. arg ( RenderType ) . arg ( FileInfo . absoluteFilePath ( ) ) ) ;
return ;
}
QDesktopServices : : openUrl ( QUrl ( " file:/// " + FileInfo . absoluteFilePath ( ) , QUrl : : TolerantMode ) ) ;
}
RenderProcess : : ~ RenderProcess ( )
{
if ( state ( ) = = QProcess : : Running | | state ( ) = = QProcess : : Starting )
{
terminate ( ) ;
waitForFinished ( ) ;
}
2017-12-27 22:55:37 +01:00
}