#include "FractoriumPch.h" #include "FractoriumEmberController.h" #include "Fractorium.h" #include "GLEmberController.h" /// /// Constructor which initializes the non-templated members contained in this class. /// The renderer, other templated members and GUI setup will be done in the templated derived controller class. /// /// Pointer to the main window. FractoriumEmberControllerBase::FractoriumEmberControllerBase(Fractorium* fractorium) { Timing t; m_Rendering = false; m_Shared = true; m_Platform = 0; m_Device = 0; m_FailedRenders = 0; m_UndoIndex = 0; m_RenderType = CPU_RENDERER; m_OutputTexID = 0; m_SubBatchCount = 1;//Will be ovewritten by the options on first render. m_Fractorium = fractorium; m_RenderTimer = NULL; m_RenderRestartTimer = NULL; m_Rand = QTIsaac(ISAAC_INT(t.Tic()), ISAAC_INT(t.Tic() * 2), ISAAC_INT(t.Tic() * 3));//Ensure a different rand seed on each instance. m_RenderTimer = new QTimer(m_Fractorium); m_RenderTimer->setInterval(0); m_Fractorium->connect(m_RenderTimer, SIGNAL(timeout()), SLOT(IdleTimer())); m_RenderRestartTimer = new QTimer(m_Fractorium); m_Fractorium->connect(m_RenderRestartTimer, SIGNAL(timeout()), SLOT(StartRenderTimer())); } /// /// Destructor which stops rendering and deletes the timers. /// All other memory is cleared automatically through the use of STL. /// FractoriumEmberControllerBase::~FractoriumEmberControllerBase() { StopRenderTimer(true); if (m_RenderTimer) { m_RenderTimer->stop(); delete m_RenderTimer; m_RenderTimer = NULL; } if (m_RenderRestartTimer) { m_RenderRestartTimer->stop(); delete m_RenderRestartTimer; m_RenderRestartTimer = NULL; } } /// /// Constructor which passes the main window parameter to the base, initializes the templated members contained in this class. /// Then sets up the parts of the GUI that require templated Widgets, such as the variations tree and the palette table. /// Note the renderer is not setup here automatically. Instead, it must be manually created by the caller later. /// /// Pointer to the main window. template FractoriumEmberController::FractoriumEmberController(Fractorium* fractorium) : FractoriumEmberControllerBase(fractorium) { m_PreviewRun = false; m_PreviewRunning = false; m_SheepTools = unique_ptr>(new SheepTools("flam3-palettes.xml", new EmberNs::Renderer())); m_GLController = unique_ptr>(new GLEmberController(fractorium, fractorium->ui.GLDisplay, this)); m_PreviewRenderer = unique_ptr>(new EmberNs::Renderer()); SetupVariationTree(); InitPaletteTable("flam3-palettes.xml"); BackgroundChanged(QColor(0, 0, 0));//Default to black. ClearUndo(); m_PreviewRenderer->Callback(NULL); m_PreviewRenderer->NumChannels(4); m_PreviewRenderer->EarlyClip(m_Fractorium->m_Settings->EarlyClip()); m_PreviewRenderer->YAxisUp(m_Fractorium->m_Settings->YAxisUp()); m_PreviewRenderer->SetEmber(m_Ember);//Give it an initial ember, will be updated many times later. //m_PreviewRenderer->ThreadCount(1);//For debugging. m_PreviewRenderFunc = [&](unsigned int start, unsigned int end) { while(m_PreviewRun || m_PreviewRunning) { } m_PreviewRun = true; m_PreviewRunning = true; m_PreviewRenderer->ThreadCount(max(1, Timing::ProcessorCount() - 1));//Leave one processor free so the GUI can breathe. QTreeWidget* tree = m_Fractorium->ui.LibraryTree; if (QTreeWidgetItem* top = tree->topLevelItem(0)) { for (size_t i = start; m_PreviewRun && i < end && i < m_EmberFile.Size(); i++) { Ember ember = m_EmberFile.m_Embers[i]; ember.SyncSize(); ember.SetSizeAndAdjustScale(PREVIEW_SIZE, PREVIEW_SIZE, false, SCALE_WIDTH); ember.m_TemporalSamples = 1; ember.m_Quality = 25; ember.m_Supersample = 1; m_PreviewRenderer->SetEmber(ember); if (m_PreviewRenderer->Run(m_PreviewFinalImage) == RENDER_OK) { if (EmberTreeWidgetItem* treeItem = dynamic_cast*>(top->child(i))) { //It is critical that Qt::BlockingQueuedConnection is passed because this is running on a different thread than the UI. //This ensures the events are processed in order as each preview is updated, and that control does not return here //until the update is complete. QMetaObject::invokeMethod(m_Fractorium, "SetLibraryTreeItemData", Qt::BlockingQueuedConnection, Q_ARG(EmberTreeWidgetItemBase*, (EmberTreeWidgetItemBase*)treeItem), Q_ARG(vector&, m_PreviewFinalImage), Q_ARG(unsigned int, PREVIEW_SIZE), Q_ARG(unsigned int, PREVIEW_SIZE)); //treeItem->SetImage(m_PreviewFinalImage, PREVIEW_SIZE, PREVIEW_SIZE); } } } } m_PreviewRun = false; m_PreviewRunning = false; }; } /// /// Empty destructor that does nothing. /// template FractoriumEmberController::~FractoriumEmberController() { } /// /// Setters for embers, ember files and palettes which convert between float and double types. /// These are used to preserve the current ember/file when switching between renderers. /// Note that some precision will be lost when going from double to float. /// template void FractoriumEmberController::SetEmber(const Ember& ember, bool verbatim) { SetEmberPrivate(ember, verbatim); } template void FractoriumEmberController::CopyEmber(Ember& ember, std::function& ember)> perEmberOperation) { ember = m_Ember; perEmberOperation(ember); } template void FractoriumEmberController::SetEmberFile(const EmberFile& emberFile) { m_EmberFile = emberFile; } template void FractoriumEmberController::CopyEmberFile(EmberFile& emberFile, std::function& ember)> perEmberOperation) { emberFile.m_Filename = m_EmberFile.m_Filename; CopyVec(emberFile.m_Embers, m_EmberFile.m_Embers, perEmberOperation); } template void FractoriumEmberController::SetTempPalette(const Palette& palette) { m_TempPalette = palette; } template void FractoriumEmberController::CopyTempPalette(Palette& palette) { palette = m_TempPalette; } #ifdef DO_DOUBLE template void FractoriumEmberController::SetEmber(const Ember& ember, bool verbatim) { SetEmberPrivate(ember, verbatim); } template void FractoriumEmberController::CopyEmber(Ember& ember, std::function& ember)> perEmberOperation) { ember = m_Ember; perEmberOperation(ember); } template void FractoriumEmberController::SetEmberFile(const EmberFile& emberFile) { m_EmberFile = emberFile; } template void FractoriumEmberController::CopyEmberFile(EmberFile& emberFile, std::function& ember)> perEmberOperation) { emberFile.m_Filename = m_EmberFile.m_Filename; CopyVec(emberFile.m_Embers, m_EmberFile.m_Embers, perEmberOperation); } template void FractoriumEmberController::SetTempPalette(const Palette& palette) { m_TempPalette = palette; } template void FractoriumEmberController::CopyTempPalette(Palette& palette) { palette = m_TempPalette; } #endif template Ember* FractoriumEmberController::CurrentEmber() { return &m_Ember; } template void FractoriumEmberController::ConstrainDimensions(Ember& ember) { ember.m_FinalRasW = std::min(m_Fractorium->ui.GLDisplay->MaxTexSize(), ember.m_FinalRasW); ember.m_FinalRasH = std::min(m_Fractorium->ui.GLDisplay->MaxTexSize(), ember.m_FinalRasH); } /// /// Set the ember at the specified index from the currently opened file as the current Ember. /// Clears the undo state. /// Resets the rendering process. /// /// The index in the file from which to retrieve the ember template void FractoriumEmberController::SetEmber(size_t index) { if (index < m_EmberFile.Size()) { if (QTreeWidgetItem* top = m_Fractorium->ui.LibraryTree->topLevelItem(0)) { for (unsigned int i = 0; i < top->childCount(); i++) { if (EmberTreeWidgetItem* emberItem = dynamic_cast*>(top->child(i))) emberItem->setSelected(i == index); } } ClearUndo(); SetEmber(m_EmberFile.m_Embers[index]); } } /// /// Wrapper to call a function, then optionally add the requested action to the rendering queue. /// /// The function to call /// True to update renderer, else false. Default: false. /// The action to add to the rendering queue. Default: FULL_RENDER. template void FractoriumEmberController::Update(std::function func, bool updateRender, eProcessAction action) { func(); if (updateRender) UpdateRender(action); } /// /// Wrapper to call a function on the current xform, then optionally add the requested action to the rendering queue. /// /// The function to call /// True to update renderer, else false. Default: true. /// The action to add to the rendering queue. Default: FULL_RENDER. template void FractoriumEmberController::UpdateCurrentXform(std::function*)> func, bool updateRender, eProcessAction action) { if (Xform* xform = CurrentXform()) { func(xform); if (updateRender) UpdateRender(action); } } /// /// Set the current ember, but use GUI values for the fields which make sense to /// keep the same between ember selection changes. /// Note the extra template parameter U allows for assigning ember of different types. /// Resets the rendering process. /// /// The ember to set as the current template template void FractoriumEmberController::SetEmberPrivate(const Ember& ember, bool verbatim) { if (ember.m_Name != m_Ember.m_Name) m_LastSaveCurrent = ""; m_Ember = ember; if (!verbatim) { //m_Ember.SetSizeAndAdjustScale(m_Fractorium->ui.GLDisplay->width(), m_Fractorium->ui.GLDisplay->height(), true, SCALE_WIDTH); m_Ember.m_TemporalSamples = 1;//Change once animation is supported. m_Ember.m_Quality = m_Fractorium->m_QualitySpin->value(); m_Ember.m_Supersample = m_Fractorium->m_SupersampleSpin->value(); } m_Fractorium->FillXforms();//Must do this first because the palette setup in FillParamTablesAndPalette() uses the xforms combo. FillParamTablesAndPalette(); }