00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023 #include <boost/bind.hpp>
00024 #include <boost/filesystem/path.hpp>
00025 #include <boost/filesystem/operations.hpp>
00026 #include <boost/thread.hpp>
00027 #include <boost/cast.hpp>
00028
00029 #include <sstream>
00030 #include <clocale>
00031
00032 #include "lux.h"
00033 #include "api.h"
00034 #include "error.h"
00035
00036 #include "wx/app.h"
00037 #include "wx/filedlg.h"
00038 #include "wx/filename.h"
00039 #include "wx/dcbuffer.h"
00040 #include "wx/splash.h"
00041 #include <boost/date_time/posix_time/posix_time.hpp>
00042
00043 #include "wxluxgui.h"
00044 #include "wxglviewer.h"
00045 #include "wximages.h"
00046
00047 using namespace lux;
00048
00049
00050
00051 DEFINE_EVENT_TYPE(lux::wxEVT_LUX_ERROR)
00052 DEFINE_EVENT_TYPE(lux::wxEVT_LUX_PARSEERROR)
00053 DEFINE_EVENT_TYPE(lux::wxEVT_LUX_FINISHED)
00054 DEFINE_EVENT_TYPE(lux::wxEVT_LUX_TONEMAPPED)
00055
00056 BEGIN_EVENT_TABLE(LuxGui, wxFrame)
00057 EVT_LUX_ERROR (wxID_ANY, LuxGui::OnError)
00058 EVT_TIMER (wxID_ANY, LuxGui::OnTimer)
00059 EVT_SPINCTRL (wxID_ANY, LuxGui::OnSpin)
00060 EVT_COMMAND (wxID_ANY, lux::wxEVT_LUX_TONEMAPPED, LuxGui::OnCommand)
00061 EVT_COMMAND (wxID_ANY, lux::wxEVT_LUX_PARSEERROR, LuxGui::OnCommand)
00062 EVT_COMMAND (wxID_ANY, lux::wxEVT_LUX_FINISHED, LuxGui::OnCommand)
00063 EVT_ICONIZE (LuxGui::OnIconize)
00064 END_EVENT_TABLE()
00065
00066 LuxGui::LuxGui(wxWindow* parent, bool opengl):LuxMainFrame(parent), m_opengl(opengl) {
00067
00068 LoadImages();
00069
00070
00071 if(m_opengl)
00072 m_renderOutput = new LuxGLViewer(m_renderPage);
00073 else
00074 m_renderOutput = new LuxOutputWin(m_renderPage);
00075 m_renderPage->GetSizer()->Add(m_renderOutput, 1, wxALL | wxEXPAND, 5);
00076 m_renderPage->Layout();
00077
00078
00079
00080 SetSize(GetSize());
00081 m_renderOutput->Update();
00082
00083
00084 m_renderTimer = new wxTimer(this, ID_RENDERUPDATE);
00085 m_statsTimer = new wxTimer(this, ID_STATSUPDATE);
00086 m_loadTimer = new wxTimer(this, ID_LOADUPDATE);
00087
00088 m_numThreads = 0;
00089 m_engineThread = NULL;
00090 m_updateThread = NULL;
00091
00092 luxErrorHandler(&LuxGuiErrorHandler);
00093
00094 ChangeRenderState(WAITING);
00095 m_guiWindowState = SHOWN;
00096 }
00097
00098 void LuxGui::ChangeRenderState(LuxGuiRenderState state) {
00099 switch(state) {
00100 case WAITING:
00101
00102 m_file->Enable(wxID_OPEN, true);
00103 m_render->Enable(ID_RESUMEITEM, false);
00104 m_render->Enable(ID_STOPITEM, false);
00105 m_renderToolBar->EnableTool(ID_RESUMETOOL, false);
00106 m_renderToolBar->EnableTool(ID_STOPTOOL, false);
00107 m_threadSpinCtrl->Disable();
00108 break;
00109 case RENDERING:
00110
00111 m_file->Enable(wxID_OPEN, false);
00112 m_render->Enable(ID_RESUMEITEM, false);
00113 m_render->Enable(ID_STOPITEM, true);
00114 m_renderToolBar->EnableTool(ID_RESUMETOOL, false);
00115 m_renderToolBar->EnableTool(ID_STOPTOOL, true);
00116 m_threadSpinCtrl->Enable();
00117 break;
00118 case IDLE:
00119
00120 m_file->Enable(wxID_OPEN, false);
00121 m_render->Enable(ID_RESUMEITEM, true);
00122 m_render->Enable(ID_STOPITEM, false);
00123 m_renderToolBar->EnableTool(ID_RESUMETOOL, true);
00124 m_renderToolBar->EnableTool(ID_STOPTOOL, false);
00125 m_threadSpinCtrl->Enable();
00126 break;
00127 case FINISHED:
00128
00129 m_file->Enable(wxID_OPEN, false);
00130 m_render->Enable(ID_RESUMEITEM, false);
00131 m_render->Enable(ID_STOPITEM, false);
00132 m_renderToolBar->EnableTool(ID_RESUMETOOL, false);
00133 m_renderToolBar->EnableTool(ID_STOPTOOL, false);
00134 m_threadSpinCtrl->Disable();
00135 break;
00136 }
00137 m_guiRenderState = state;
00138 }
00139
00140 void LuxGui::LoadImages() {
00141 wxImage::AddHandler(new wxPNGHandler());
00142
00143
00144 #ifndef __WXMSW__
00145 wxIcon appIcon;
00146 appIcon.CopyFromBitmap(wxMEMORY_BITMAP(luxicon_png));
00147 SetIcon(appIcon);
00148 #endif
00149
00150
00151
00152 wxToolBarToolBase *rendertool = m_renderToolBar->RemoveTool(ID_RESUMETOOL);
00153 rendertool->SetNormalBitmap(wxMEMORY_BITMAP(resume_png));
00154 m_renderToolBar->InsertTool(0, rendertool);
00155
00156
00157 wxToolBarToolBase *stoptool = m_renderToolBar->RemoveTool(ID_STOPTOOL);
00158 stoptool->SetNormalBitmap(wxMEMORY_BITMAP(stop_png));
00159 m_renderToolBar->InsertTool(1, stoptool);
00160 m_renderToolBar->Realize();
00161
00162
00163 #ifndef __WXMSW__
00164
00165
00166 wxMenuItem *renderitem = m_render->Remove(ID_RESUMEITEM);
00167 renderitem->SetBitmap(wxMEMORY_BITMAP(resume_png));
00168 m_render->Insert(0,renderitem);
00169
00170 wxMenuItem *stopitem = m_render->Remove(ID_STOPITEM);
00171 stopitem->SetBitmap(wxMEMORY_BITMAP(stop_png));
00172 m_render->Insert(1,stopitem);
00173 #endif
00174
00175 m_auinotebook->SetPageBitmap(0, wxMEMORY_BITMAP(render_png));
00176 m_auinotebook->SetPageBitmap(1, wxMEMORY_BITMAP(info_png));
00177 m_auinotebook->SetPageBitmap(2, wxMEMORY_BITMAP(output_png));
00178
00179 m_splashbmp = wxMEMORY_BITMAP(splash_png);
00180 }
00181
00182 void LuxGui::OnMenu(wxCommandEvent& event) {
00183 switch (event.GetId()) {
00184 case ID_RESUMEITEM:
00185 case ID_RESUMETOOL:
00186 if(m_guiRenderState != RENDERING) {
00187
00188 m_renderOutput->Refresh();
00189 m_renderTimer->Start(1000*luxStatistics("displayInterval"), wxTIMER_CONTINUOUS);
00190 m_statsTimer->Start(1000, wxTIMER_CONTINUOUS);
00191 if(m_guiRenderState == IDLE)
00192 luxStart();
00193 ChangeRenderState(RENDERING);
00194 }
00195 break;
00196 case ID_STOPITEM:
00197 case ID_STOPTOOL:
00198 if(m_guiRenderState != IDLE) {
00199
00200 m_renderTimer->Stop();
00201 m_statsTimer->Stop();
00202 if(m_guiRenderState == RENDERING)
00203 luxPause();
00204 ChangeRenderState(IDLE);
00205 }
00206 break;
00207 case wxID_ABOUT:
00208 new wxSplashScreen(m_splashbmp, wxSPLASH_CENTRE_ON_PARENT, 0, this, -1);
00209 break;
00210 case wxID_EXIT:
00211 Close(false);
00212 break;
00213 default:
00214 break;
00215 }
00216 }
00217
00218 void LuxGui::OnOpen(wxCommandEvent& event) {
00219 wxFileDialog filedlg(this,
00220 _("Choose a file to open"),
00221 wxEmptyString,
00222 wxEmptyString,
00223 _("LuxRender scene files (*.lxs)|*.lxs|All files (*.*)|*.*"),
00224 wxFD_OPEN | wxFD_FILE_MUST_EXIST);
00225
00226 if (filedlg.ShowModal() == wxID_OK)
00227 RenderScenefile(filedlg.GetPath());
00228 }
00229
00230 void LuxGui::OnExit(wxCloseEvent& event) {
00231
00232 if(m_guiRenderState != WAITING) {
00233 if(m_updateThread)
00234 m_updateThread->join();
00235
00236 luxExit();
00237
00238 if(m_engineThread)
00239 m_engineThread->join();
00240
00241 luxError(LUX_NOERROR, LUX_INFO, "Freeing resources.");
00242 luxCleanup();
00243 }
00244
00245 Destroy();
00246 }
00247
00248 void LuxGui::OnError(wxLuxErrorEvent &event) {
00249 std::stringstream ss("");
00250 ss << boost::posix_time::second_clock::local_time() << ' ';
00251 switch(event.GetError()->GetSeverity()) {
00252 case LUX_INFO:
00253 ss << "Info: "; break;
00254 case LUX_WARNING:
00255 ss << "Warning: "; break;
00256 case LUX_ERROR:
00257 ss << "Error: "; break;
00258 case LUX_SEVERE:
00259 ss << "Severe error: "; break;
00260 }
00261 ss << "(" << event.GetError()->GetCode() << ") ";
00262 ss << event.GetError()->GetMessage() << std::endl;
00263 m_logTextCtrl->AppendText(wxString::FromAscii(ss.str().c_str()));
00264 m_logTextCtrl->ShowPosition(m_logTextCtrl->GetLastPosition());
00265 }
00266
00267 void LuxGui::OnTimer(wxTimerEvent& event) {
00268 switch (event.GetId()) {
00269 case ID_RENDERUPDATE:
00270 if(m_updateThread == NULL && luxStatistics("sceneIsReady") &&
00271 (m_guiWindowState == SHOWN || m_guiRenderState == FINISHED)) {
00272 luxError(LUX_NOERROR, LUX_INFO, "GUI: Updating framebuffer...");
00273 m_statusBar->SetStatusText(wxT("Tonemapping..."), 0);
00274 m_updateThread = new boost::thread(boost::bind(&LuxGui::UpdateThread, this));
00275 }
00276 break;
00277 case ID_STATSUPDATE:
00278 if(luxStatistics("sceneIsReady"))
00279 UpdateStatistics();
00280 break;
00281 case ID_LOADUPDATE:
00282 m_progDialog->Pulse();
00283 if(luxStatistics("sceneIsReady") || m_guiRenderState == FINISHED) {
00284 m_progDialog->Destroy();
00285 m_loadTimer->Stop();
00286
00287 if(luxStatistics("sceneIsReady")) {
00288
00289
00290 int curThreads = 1;
00291 while(curThreads < m_numThreads) {
00292 luxAddThread();
00293 curThreads++;
00294 }
00295
00296
00297 wxCommandEvent startEvent(wxEVT_COMMAND_MENU_SELECTED, ID_RESUMEITEM);
00298 GetEventHandler()->AddPendingEvent(startEvent);
00299 }
00300 }
00301 break;
00302 }
00303 }
00304
00305 void LuxGui::OnSpin(wxSpinEvent& event) {
00306 SetRenderThreads(event.GetPosition());
00307 }
00308
00309 void LuxGui::OnCommand(wxCommandEvent &event) {
00310 if(event.GetEventType() == wxEVT_LUX_TONEMAPPED) {
00311
00312 m_updateThread->join();
00313 delete m_updateThread;
00314 m_updateThread = NULL;
00315 m_statusBar->SetStatusText(wxT(""), 0);
00316 m_renderOutput->Refresh();
00317
00318 } else if(event.GetEventType() == wxEVT_LUX_PARSEERROR) {
00319 wxMessageBox(wxT("Scene file parse error.\nSee log for details."), wxT("Error"), wxOK | wxICON_ERROR, this);
00320 ChangeRenderState(FINISHED);
00321 } else if(event.GetEventType() == wxEVT_LUX_FINISHED) {
00322
00323 ChangeRenderState(FINISHED);
00324
00325 m_renderTimer->Stop();
00326 wxTimerEvent rendUpdEvent(ID_RENDERUPDATE, GetId());
00327 GetEventHandler()->AddPendingEvent(rendUpdEvent);
00328 m_statsTimer->Stop();
00329 wxTimerEvent statUpdEvent(ID_STATSUPDATE, GetId());
00330 GetEventHandler()->AddPendingEvent(statUpdEvent);
00331 }
00332 }
00333
00334 void lux::LuxGui::OnIconize( wxIconizeEvent& event )
00335 {
00336 if(!event.Iconized())
00337 m_guiWindowState = SHOWN;
00338 else
00339 m_guiWindowState = HIDDEN;
00340 }
00341
00342 void LuxGui::RenderScenefile(wxString filename) {
00343 wxFileName fn(filename);
00344 SetTitle(wxT("LuxRender - ")+fn.GetName());
00345
00346
00347 m_engineThread = new boost::thread(boost::bind(&LuxGui::EngineThread, this, filename));
00348
00349 m_progDialog = new wxProgressDialog(wxT("Loading..."), wxT(""), 100, NULL, wxSTAY_ON_TOP);
00350 m_progDialog->Pulse();
00351 m_loadTimer->Start(1000, wxTIMER_CONTINUOUS);
00352 }
00353
00354 void LuxGui::EngineThread(wxString filename) {
00355 boost::filesystem::path fullPath(boost::filesystem::initial_path());
00356 fullPath = boost::filesystem::system_complete(boost::filesystem::path(filename.fn_str(), boost::filesystem::native));
00357
00358 chdir(fullPath.branch_path().string().c_str());
00359
00360 ParseFile(fullPath.leaf().c_str());
00361
00362 if(!luxStatistics("sceneIsReady")) {
00363 wxCommandEvent errorEvent(wxEVT_LUX_PARSEERROR, GetId());
00364 GetEventHandler()->AddPendingEvent(errorEvent);
00365
00366 luxWait();
00367 } else {
00368 luxWait();
00369
00370 luxError(LUX_NOERROR, LUX_INFO, "Rendering done.");
00371 wxCommandEvent endEvent(wxEVT_LUX_FINISHED, GetId());
00372 GetEventHandler()->AddPendingEvent(endEvent);
00373 }
00374 }
00375
00376 void LuxGui::UpdateThread() {
00377 luxUpdateFramebuffer();
00378 wxCommandEvent endEvent(wxEVT_LUX_TONEMAPPED, GetId());
00379 GetEventHandler()->AddPendingEvent(endEvent);
00380 }
00381
00382 void LuxGui::SetRenderThreads(int num) {
00383 if(luxStatistics("sceneIsReady")) {
00384 if(num > m_numThreads) {
00385 for(; num > m_numThreads; m_numThreads++)
00386 luxAddThread();
00387 } else {
00388 for(; num < m_numThreads; m_numThreads--)
00389 luxRemoveThread();
00390 }
00391 } else {
00392 m_numThreads = num;
00393 }
00394 m_threadSpinCtrl->SetValue(m_numThreads);
00395 }
00396
00397 void LuxGui::UpdateStatistics() {
00398 int samplesSec = Floor2Int(luxStatistics("samplesSec"));
00399 int samplesTotSec = Floor2Int(luxStatistics("samplesTotSec"));
00400 int secElapsed = Floor2Int(luxStatistics("secElapsed"));
00401 double samplesPx = luxStatistics("samplesPx");
00402 int efficiency = Floor2Int(luxStatistics("efficiency"));
00403
00404 int secs = (secElapsed) % 60;
00405 int mins = (secElapsed / 60) % 60;
00406 int hours = (secElapsed / 3600);
00407
00408 wxString stats;
00409 stats.Printf(wxT("%02d:%02d:%02d - %d S/s - %d TotS/s - %.2f S/px - %i%% eff"),
00410 hours, mins, secs, samplesSec, samplesTotSec, samplesPx, efficiency);
00411 m_statusBar->SetStatusText(stats, 1);
00412 }
00413
00414
00415
00416 BEGIN_EVENT_TABLE(LuxOutputWin, wxWindow)
00417 EVT_PAINT (LuxOutputWin::OnPaint)
00418 END_EVENT_TABLE()
00419
00420 LuxOutputWin::LuxOutputWin(wxWindow *parent)
00421 : wxScrolledWindow(parent, wxID_ANY, wxDefaultPosition, wxSize(-1, -1)) {
00422 }
00423
00424 void LuxOutputWin::OnDraw(wxDC &dc) {
00425 if (luxStatistics("sceneIsReady")) {
00426 int w = luxStatistics("filmXres"), h = luxStatistics("filmYres");
00427 SetVirtualSize(w, h);
00428 SetScrollRate(1,1);
00429 unsigned char* fb = luxFramebuffer();
00430 dc.DrawBitmap(wxBitmap(wxImage(w, h, fb, true)), 0, 0, false);
00431 }
00432 }
00433
00434
00435
00436 void lux::LuxGuiErrorHandler(int code, int severity, const char *msg) {
00437 boost::shared_ptr<LuxError> error(new LuxError(code, severity, msg));
00438 wxLuxErrorEvent errorEvent(error, wxEVT_LUX_ERROR);
00439 wxTheApp->GetTopWindow()->GetEventHandler()->AddPendingEvent(errorEvent);
00440 }
00441