mirror of
				https://bitbucket.org/mfeemster/fractorium.git
				synced 2025-10-30 17:00:24 -04:00 
			
		
		
		
	 018ba26b5f
			
		
	
	018ba26b5f
	
	
	
		
			
			-Add support for multiple GPU devices.
  --These options are present in the command line and in Fractorium.
 -Change scheme of specifying devices from platform,device to just total device index.
  --Single number on the command line.
  --Change from combo boxes for device selection to a table of all devices in Fractorium.
 -Temporal samples defaults to 100 instead of 1000 which was needless overkill.
--Bug fixes
 -EmberAnimate, EmberRender, FractoriumSettings, FinalRenderDialog: Fix wrong order of arguments to Clamp() when assigning thread priority.
 -VariationsDC.h: Fix NVidia OpenCL compilation error in DCTriangleVariation.
 -FractoriumXformsColor.cpp: Checking for null pixmap pointer is not enough, must also check if the underlying buffer is null via call to QPixmap::isNull().
--Code changes
 -Ember.h: Add case for FLAME_MOTION_NONE and default in ApplyFlameMotion().
 -EmberMotion.h: Call base constructor.
 -EmberPch.h: #pragma once only on Windows.
 -EmberToXml.h:
  --Handle different types of exceptions.
  --Add default cases to ToString().
 -Isaac.h: Remove unused variable in constructor.
 -Point.h: Call base constructor in Color().
 -Renderer.h/cpp:
  --Add bool to Alloc() to only allocate memory for the histogram. Needed for multi-GPU.
  --Make CoordMap() return a const ref, not a pointer.
 -SheepTools.h:
  --Use 64-bit types like the rest of the code already does.
  --Fix some comment misspellings.
 -Timing.h: Make BeginTime(), EndTime(), ElapsedTime() and Format() be const functions.
 -Utils.h:
  --Add new functions Equal() and Split().
  --Handle more exception types in ReadFile().
  --Get rid of most legacy blending of C and C++ argument parsing.
 -XmlToEmber.h:
  --Get rid of most legacy blending of C and C++ code from flam3.
  --Remove some unused variables.
 -EmberAnimate:
  --Support multi-GPU processing that alternates full frames between devices.
  --Use OpenCLInfo instead of OpenCLWrapper for --openclinfo option.
  --Remove bucketT template parameter, and hard code float in its place.
  --If a render fails, exit since there is no point in continuing an animation with a missing frame.
  --Pass variables to threaded save better, which most likely fixes a very subtle bug that existed before.
  --Remove some unused variables.
 -EmberGenome, EmberRender:
  --Support multi-GPU processing that alternates full frames between devices.
  --Use OpenCLInfo instead of OpenCLWrapper for --openclinfo option.
  --Remove bucketT template parameter, and hard code float in its place.
 -EmberRender:
  --Support multi-GPU processing that alternates full frames between devices.
  --Use OpenCLInfo instead of OpenCLWrapper for --openclinfo option.
  --Remove bucketT template parameter, and hard code float in its place.
  --Only print values when not rendering with OpenCL, since they're always 0 in that case.
 -EmberCLPch.h:
  --#pragma once only on Windows.
  --#include <atomic>.
 -IterOpenCLKernelCreator.h: Add new kernel for summing two histograms. This is needed for multi-GPU.
 -OpenCLWrapper.h:
  --Move all OpenCL info related code into its own class OpenCLInfo.
  --Add members to cache the values of global memory size and max allocation size.
 -RendererCL.h/cpp:
  --Redesign to accomodate multi-GPU.
  --Constructor now takes a vector of devices.
  --Remove DumpErrorReport() function, it's handled in the base.
  --ClearBuffer(), ReadPoints(), WritePoints(), ReadHist() and WriteHist() now optionally take a device index as a parameter.
  --MakeDmap() override and m_DmapCL member removed because it no longer applies since the histogram is always float since the last commit.
  --Add new function SumDeviceHist() to sum histograms from two devices by first copying to a temporary on the host, then a temporary on the device, then summing.
  --m_Calls member removed, as it's now per-device.
  --OpenCLWrapper removed.
  --m_Seeds member is now a vector of vector of seeds, to accomodate a separate and different array of seeds for each device.
  --Added member m_Devices, a vector of unique_ptr of RendererCLDevice.
 -EmberCommon.h
  --Added Devices() function to convert from a vector of device indices to a vector of platform,device indices.
  --Changed CreateRenderer() to accept a vector of devices to create a single RendererCL which will split work across multiple devices.
  --Added CreateRenderers() function to accept a vector of devices to create multiple RendererCL, each which will render on a single device.
  --Add more comments to some existing functions.
 -EmberCommonPch.h: #pragma once only on Windows.
 -EmberOptions.h:
  --Remove --platform option, it's just sequential device number now with the --device option.
  --Make --out be OPT_USE_RENDER instead of OPT_RENDER_ANIM since it's an error condition when animating. It makes no sense to write all frames to a single image.
  --Add Devices() function to parse comma separated --device option string and return a vector of device indices.
  --Make int and uint types be 64-bit, so intmax_t and size_t.
  --Make better use of macros.
 -JpegUtils.h: Make string parameters to WriteJpeg() and WritePng() be const ref.
 -All project files: Turn off buffer security check option in Visual Studio (/Gs-)
 -deployment.pri: Remove the line OTHER_FILES +=, it's pointless and was causing problems.
 -Ember.pro, EmberCL.pro: Add CONFIG += plugin, otherwise it wouldn't link.
 -EmberCL.pro: Add new files for multi-GPU support.
 -build_all.sh: use -j4 and QMAKE=${QMAKE:/usr/bin/qmake}
 -shared_settings.pri:
  -Add version string.
  -Remove old DESTDIR definitions.
  -Add the following lines or else nothing would build:
   CONFIG(release, debug|release) {
    CONFIG += warn_off
    DESTDIR = ../../../Bin/release
   }
   CONFIG(debug, debug|release) {
    DESTDIR = ../../../Bin/debug
   }
   QMAKE_POST_LINK += $$quote(cp --update ../../../Data/flam3-palettes.xml $${DESTDIR}$$escape_expand(\n\t))
   LIBS += -L/usr/lib -lpthread
 -AboutDialog.ui: Another futile attempt to make it look correct on Linux.
 -FinalRenderDialog.h/cpp:
  --Add support for multi-GPU.
  --Change from combo boxes for device selection to a table of all devices.
  --Ensure device selection makes sense.
  --Remove "FinalRender" prefix of various function names, it's implied given the context.
 -FinalRenderEmberController.h/cpp:
  --Add support for multi-GPU.
  --Change m_FinishedImageCount to be atomic.
  --Move CancelRender() from the base to FinalRenderEmberController<T>.
  --Refactor RenderComplete() to omit any progress related functionality or image saving since it can be potentially ran in a thread.
  --Consolidate setting various renderer fields into SyncGuiToRenderer().
 -Fractorium.cpp: Allow for resizing of the options dialog to show the entire device table.
 -FractoriumCommon.h: Add various functions to handle a table showing the available OpenCL devices on the system.
 -FractoriumEmberController.h/cpp: Remove m_FinalImageIndex, it's no longer needed.
 -FractoriumRender.cpp: Scale the interactive sub batch count and quality by the number of devices used.
 -FractoriumSettings.h/cpp:
  --Temporal samples defaults to 100 instead of 1000 which was needless overkill.
  --Add multi-GPU support, remove old device,platform pair.
 -FractoriumToolbar.cpp: Disable OpenCL toolbar button if there are no devices present on the system.
 -FractoriumOptionsDialog.h/cpp:
  --Add support for multi-GPU.
  --Consolidate more assignments in DataToGui().
  --Enable/disable CPU/OpenCL items in response to OpenCL checkbox event.
 -Misc: Convert almost everything to size_t for unsigned, intmax_t for signed.
		
	
		
			
				
	
	
		
			880 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			880 lines
		
	
	
		
			29 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| #include "FractoriumPch.h"
 | |
| #include "Fractorium.h"
 | |
| 
 | |
| /// <summary>
 | |
| /// Initialize the menus UI.
 | |
| /// </summary>
 | |
| void Fractorium::InitMenusUI()
 | |
| {
 | |
| 	//File menu.
 | |
| 	connect(ui.ActionNewFlock,					  SIGNAL(triggered(bool)), this, SLOT(OnActionNewFlock(bool)),					  Qt::QueuedConnection);
 | |
| 	connect(ui.ActionNewEmptyFlameInCurrentFile,  SIGNAL(triggered(bool)), this, SLOT(OnActionNewEmptyFlameInCurrentFile(bool)),  Qt::QueuedConnection);
 | |
| 	connect(ui.ActionNewRandomFlameInCurrentFile, SIGNAL(triggered(bool)), this, SLOT(OnActionNewRandomFlameInCurrentFile(bool)), Qt::QueuedConnection);
 | |
| 	connect(ui.ActionCopyFlameInCurrentFile,	  SIGNAL(triggered(bool)), this, SLOT(OnActionCopyFlameInCurrentFile(bool)),	  Qt::QueuedConnection);
 | |
| 	connect(ui.ActionOpen,						  SIGNAL(triggered(bool)), this, SLOT(OnActionOpen(bool)),						  Qt::QueuedConnection);
 | |
| 	connect(ui.ActionSaveCurrentAsXml,			  SIGNAL(triggered(bool)), this, SLOT(OnActionSaveCurrentAsXml(bool)),			  Qt::QueuedConnection);
 | |
| 	connect(ui.ActionSaveEntireFileAsXml,		  SIGNAL(triggered(bool)), this, SLOT(OnActionSaveEntireFileAsXml(bool)),		  Qt::QueuedConnection);
 | |
| 	connect(ui.ActionSaveCurrentToOpenedFile,	  SIGNAL(triggered(bool)), this, SLOT(OnActionSaveCurrentToOpenedFile(bool)),	  Qt::QueuedConnection);
 | |
| 	connect(ui.ActionSaveCurrentScreen,			  SIGNAL(triggered(bool)), this, SLOT(OnActionSaveCurrentScreen(bool)),			  Qt::QueuedConnection);
 | |
| 	connect(ui.ActionExit,						  SIGNAL(triggered(bool)), this, SLOT(OnActionExit(bool)),						  Qt::QueuedConnection);
 | |
| 
 | |
| 	//Edit menu.
 | |
| 	connect(ui.ActionUndo,				  SIGNAL(triggered(bool)), this, SLOT(OnActionUndo(bool)),				  Qt::QueuedConnection);
 | |
| 	connect(ui.ActionRedo,				  SIGNAL(triggered(bool)), this, SLOT(OnActionRedo(bool)),				  Qt::QueuedConnection);
 | |
| 	connect(ui.ActionCopyXml,			  SIGNAL(triggered(bool)), this, SLOT(OnActionCopyXml(bool)),			  Qt::QueuedConnection);
 | |
| 	connect(ui.ActionCopyAllXml,		  SIGNAL(triggered(bool)), this, SLOT(OnActionCopyAllXml(bool)),		  Qt::QueuedConnection);
 | |
| 	connect(ui.ActionPasteXmlAppend,	  SIGNAL(triggered(bool)), this, SLOT(OnActionPasteXmlAppend(bool)),	  Qt::QueuedConnection);
 | |
| 	connect(ui.ActionPasteXmlOver,		  SIGNAL(triggered(bool)), this, SLOT(OnActionPasteXmlOver(bool)),		  Qt::QueuedConnection);
 | |
| 	connect(ui.ActionCopySelectedXforms,  SIGNAL(triggered(bool)), this, SLOT(OnActionCopySelectedXforms(bool)),  Qt::QueuedConnection);
 | |
| 	connect(ui.ActionPasteSelectedXforms, SIGNAL(triggered(bool)), this, SLOT(OnActionPasteSelectedXforms(bool)), Qt::QueuedConnection);
 | |
| 	ui.ActionPasteSelectedXforms->setEnabled(false);
 | |
| 
 | |
| 	//View menu.
 | |
| 	connect(ui.ActionResetWorkspace, SIGNAL(triggered(bool)), this, SLOT(OnActionResetWorkspace(bool)), Qt::QueuedConnection);
 | |
| 
 | |
| 	//Tools menu.
 | |
| 	connect(ui.ActionAddReflectiveSymmetry, SIGNAL(triggered(bool)), this, SLOT(OnActionAddReflectiveSymmetry(bool)), Qt::QueuedConnection);
 | |
| 	connect(ui.ActionAddRotationalSymmetry, SIGNAL(triggered(bool)), this, SLOT(OnActionAddRotationalSymmetry(bool)), Qt::QueuedConnection);
 | |
| 	connect(ui.ActionAddBothSymmetry,		SIGNAL(triggered(bool)), this, SLOT(OnActionAddBothSymmetry(bool)),		  Qt::QueuedConnection);
 | |
| 	connect(ui.ActionClearFlame,			SIGNAL(triggered(bool)), this, SLOT(OnActionClearFlame(bool)),			  Qt::QueuedConnection);
 | |
| 	connect(ui.ActionFlatten,			    SIGNAL(triggered(bool)), this, SLOT(OnActionFlatten(bool)),			      Qt::QueuedConnection);
 | |
| 	connect(ui.ActionUnflatten,			    SIGNAL(triggered(bool)), this, SLOT(OnActionUnflatten(bool)),			  Qt::QueuedConnection);
 | |
| 	connect(ui.ActionStopRenderingPreviews,	SIGNAL(triggered(bool)), this, SLOT(OnActionStopRenderingPreviews(bool)), Qt::QueuedConnection);
 | |
| 	connect(ui.ActionRenderPreviews,		SIGNAL(triggered(bool)), this, SLOT(OnActionRenderPreviews(bool)),		  Qt::QueuedConnection);
 | |
| 	connect(ui.ActionFinalRender,			SIGNAL(triggered(bool)), this, SLOT(OnActionFinalRender(bool)),			  Qt::QueuedConnection);
 | |
| 	connect(m_FinalRenderDialog,			SIGNAL(finished(int)),   this, SLOT(OnFinalRenderClose(int)),			  Qt::QueuedConnection);
 | |
| 	connect(ui.ActionOptions,				SIGNAL(triggered(bool)), this, SLOT(OnActionOptions(bool)),				  Qt::QueuedConnection);
 | |
| 
 | |
| 	//Help menu.
 | |
| 	connect(ui.ActionAbout, SIGNAL(triggered(bool)), this, SLOT(OnActionAbout(bool)), Qt::QueuedConnection);
 | |
| }
 | |
| 
 | |
| /// <summary>
 | |
| /// Create a new flock of random embers, with the specified length.
 | |
| /// </summary>
 | |
| /// <param name="count">The number of embers to include in the flock</param>
 | |
| template <typename T>
 | |
| void FractoriumEmberController<T>::NewFlock(size_t count)
 | |
| {
 | |
| 	Ember<T> ember;
 | |
| 
 | |
| 	StopPreviewRender();
 | |
| 	m_EmberFile.Clear();
 | |
| 	m_EmberFile.m_Embers.reserve(count);
 | |
| 	m_EmberFile.m_Filename = EmberFile<T>::DefaultFilename();
 | |
| 
 | |
| 	for (size_t i = 0; i < count; i++)
 | |
| 	{
 | |
| 		m_SheepTools->Random(ember, m_FilteredVariations, static_cast<intmax_t>(QTIsaac<ISAAC_SIZE, ISAAC_INT>::GlobalRand->Frand<T>(-2, 2)), 0);
 | |
| 		ParamsToEmber(ember);
 | |
| 		ember.m_Index = i;
 | |
| 		ember.m_Name = m_EmberFile.m_Filename.toStdString() + "-" + ToString(i + 1ULL).toStdString();
 | |
| 		m_EmberFile.m_Embers.push_back(ember);
 | |
| 	}
 | |
| 
 | |
| 	m_LastSaveAll = "";
 | |
| 	FillLibraryTree();
 | |
| }
 | |
| 
 | |
| /// <summary>
 | |
| /// Create a new flock and assign the first ember as the current one.
 | |
| /// </summary>
 | |
| /// <param name="checked">Ignored</param>
 | |
| void Fractorium::OnActionNewFlock(bool checked)
 | |
| {
 | |
| 	m_Controller->NewFlock(10);
 | |
| 	m_Controller->SetEmber(0);
 | |
| }
 | |
| 
 | |
| /// <summary>
 | |
| /// Create and add a new empty ember in the currently opened file
 | |
| /// and set it as the current one.
 | |
| /// It will have one empty xform in it.
 | |
| /// Resets the rendering process.
 | |
| /// </summary>
 | |
| template <typename T>
 | |
| void FractoriumEmberController<T>::NewEmptyFlameInCurrentFile()
 | |
| {
 | |
| 	Ember<T> ember;
 | |
| 	Xform<T> xform;
 | |
| 	QDateTime local(QDateTime::currentDateTime());
 | |
| 
 | |
| 	StopPreviewRender();
 | |
| 	ParamsToEmber(ember);
 | |
| 	xform.m_Weight = T(0.25);
 | |
| 	xform.m_ColorX = m_Rand.Frand01<T>();
 | |
| 	ember.AddXform(xform);
 | |
| 	ember.m_Palette = *m_PaletteList.GetRandomPalette();
 | |
| 	ember.m_Name = EmberFile<T>::DefaultEmberName(m_EmberFile.Size() + 1).toStdString();
 | |
| 	ember.m_Index = m_EmberFile.Size();
 | |
| 	m_EmberFile.m_Embers.push_back(ember);//Will invalidate the pointers contained in the EmberTreeWidgetItems, UpdateLibraryTree() will resync.
 | |
| 	m_EmberFile.MakeNamesUnique();
 | |
| 	UpdateLibraryTree();
 | |
| 	SetEmber(m_EmberFile.Size() - 1);
 | |
| }
 | |
| 
 | |
| void Fractorium::OnActionNewEmptyFlameInCurrentFile(bool checked) { m_Controller->NewEmptyFlameInCurrentFile(); }
 | |
| 
 | |
| /// <summary>
 | |
| /// Create and add a new random ember in the currently opened file
 | |
| /// and set it as the current one.
 | |
| /// Resets the rendering process.
 | |
| /// </summary>
 | |
| template <typename T>
 | |
| void FractoriumEmberController<T>::NewRandomFlameInCurrentFile()
 | |
| {
 | |
| 	Ember<T> ember;
 | |
| 
 | |
| 	StopPreviewRender();
 | |
| 	m_SheepTools->Random(ember, m_FilteredVariations, static_cast<int>(QTIsaac<ISAAC_SIZE, ISAAC_INT>::GlobalRand->Frand<T>(-2, 2)), 0);
 | |
| 	ParamsToEmber(ember);
 | |
| 	ember.m_Name = EmberFile<T>::DefaultEmberName(m_EmberFile.Size() + 1).toStdString();
 | |
| 	ember.m_Index = m_EmberFile.Size();
 | |
| 	m_EmberFile.m_Embers.push_back(ember);//Will invalidate the pointers contained in the EmberTreeWidgetItems, UpdateLibraryTree() will resync.
 | |
| 	m_EmberFile.MakeNamesUnique();
 | |
| 	UpdateLibraryTree();
 | |
| 	SetEmber(m_EmberFile.Size() - 1);
 | |
| }
 | |
| 
 | |
| void Fractorium::OnActionNewRandomFlameInCurrentFile(bool checked) { m_Controller->NewRandomFlameInCurrentFile(); }
 | |
| 
 | |
| /// <summary>
 | |
| /// Create and add a a copy of the current ember in the currently opened file
 | |
| /// and set it as the current one.
 | |
| /// Resets the rendering process.
 | |
| /// </summary>
 | |
| template <typename T>
 | |
| void FractoriumEmberController<T>::CopyFlameInCurrentFile()
 | |
| {
 | |
| 	Ember<T> ember = m_Ember;
 | |
| 
 | |
| 	StopPreviewRender();
 | |
| 	ember.m_Name = EmberFile<T>::DefaultEmberName(m_EmberFile.Size() + 1).toStdString();
 | |
| 	ember.m_Index = m_EmberFile.Size();
 | |
| 	m_EmberFile.m_Embers.push_back(ember);//Will invalidate the pointers contained in the EmberTreeWidgetItems, UpdateLibraryTree() will resync.
 | |
| 	m_EmberFile.MakeNamesUnique();
 | |
| 	UpdateLibraryTree();
 | |
| 	SetEmber(m_EmberFile.Size() - 1);
 | |
| }
 | |
| 
 | |
| void Fractorium::OnActionCopyFlameInCurrentFile(bool checked) { m_Controller->CopyFlameInCurrentFile(); }
 | |
| 
 | |
| /// <summary>
 | |
| /// Open a list of ember Xml files, apply various values from the GUI widgets.
 | |
| /// Either append these newly read embers to the existing open embers,
 | |
| /// or clear the current ember file first.
 | |
| /// When appending, add the new embers the the end of library tree.
 | |
| /// When not appending, clear and populate the library tree with the new embers.
 | |
| /// Set the current ember to the first one in the newly opened list.
 | |
| /// Clears the undo state.
 | |
| /// Resets the rendering process.
 | |
| /// </summary>
 | |
| /// <param name="filenames">A list of full paths and filenames</param>
 | |
| /// <param name="append">True to append the embers in the new files to the end of the currently open embers, false to clear and replace them</param>
 | |
| template <typename T>
 | |
| void FractoriumEmberController<T>::OpenAndPrepFiles(const QStringList& filenames, bool append)
 | |
| {
 | |
| 	if (!filenames.empty())
 | |
| 	{
 | |
| 		size_t i;
 | |
| 		EmberFile<T> emberFile;
 | |
| 		XmlToEmber<T> parser;
 | |
| 		vector<Ember<T>> embers;
 | |
| 		uint previousSize = append ? m_EmberFile.Size() : 0;
 | |
| 
 | |
| 		StopPreviewRender();
 | |
| 		emberFile.m_Filename = filenames[0];
 | |
| 
 | |
| 		foreach(const QString& filename, filenames)
 | |
| 		{
 | |
| 			embers.clear();
 | |
| 
 | |
| 			if (parser.Parse(filename.toStdString().c_str(), embers) && !embers.empty())
 | |
| 			{
 | |
| 				for (i = 0; i < embers.size(); i++)
 | |
| 				{
 | |
| 					ConstrainDimensions(embers[i]);//Do not exceed the max texture size.
 | |
| 
 | |
| 					//Also ensure it has a name.
 | |
| 					if (embers[i].m_Name == "" || embers[i].m_Name == "No name")
 | |
| 						embers[i].m_Name = ToString<qulonglong>(i).toStdString();
 | |
| 
 | |
| 					embers[i].m_Quality = m_Fractorium->m_QualitySpin->value();
 | |
| 					embers[i].m_Supersample = m_Fractorium->m_SupersampleSpin->value();
 | |
| 				}
 | |
| 
 | |
| 				m_LastSaveAll = "";
 | |
| 				emberFile.m_Embers.insert(emberFile.m_Embers.end(), embers.begin(), embers.end());
 | |
| 			}
 | |
| 			else
 | |
| 			{
 | |
| 				vector<string> errors = parser.ErrorReport();
 | |
| 
 | |
| 				m_Fractorium->ErrorReportToQTextEdit(errors, m_Fractorium->ui.InfoFileOpeningTextEdit);
 | |
| 				m_Fractorium->ShowCritical("Open Failed", "Could not open file, see info tab for details.");
 | |
| 			}
 | |
| 		}
 | |
| 		
 | |
| 		if (append)
 | |
| 		{
 | |
| 			if (m_EmberFile.m_Filename == "")
 | |
| 				m_EmberFile.m_Filename = filenames[0];
 | |
| 
 | |
| 			m_EmberFile.m_Embers.insert(m_EmberFile.m_Embers.end(), emberFile.m_Embers.begin(), emberFile.m_Embers.end());
 | |
| 		}
 | |
| 		else
 | |
| 			m_EmberFile = emberFile;
 | |
| 
 | |
| 		//Resync indices and names.
 | |
| 		for (i = 0; i < m_EmberFile.Size(); i++)
 | |
| 			m_EmberFile.m_Embers[i].m_Index = i;
 | |
| 
 | |
| 		m_EmberFile.MakeNamesUnique();
 | |
| 
 | |
| 		if (append)
 | |
| 			UpdateLibraryTree();
 | |
| 		else
 | |
| 			FillLibraryTree(append ? previousSize - 1 : 0);
 | |
| 
 | |
| 		ClearUndo();
 | |
| 		SetEmber(previousSize);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /// <summary>
 | |
| /// Show a file open dialog to open ember Xml files.
 | |
| /// </summary>
 | |
| /// <param name="checked">Ignored</param>
 | |
| void Fractorium::OnActionOpen(bool checked) { m_Controller->OpenAndPrepFiles(SetupOpenXmlDialog(), false); }
 | |
| 
 | |
| /// <summary>
 | |
| /// Save current ember as Xml, using the Xml saving template values from the options.
 | |
| /// This will first save the current ember back to the opened file in memory before
 | |
| /// saving it to disk.
 | |
| /// </summary>
 | |
| template <typename T>
 | |
| void FractoriumEmberController<T>::SaveCurrentAsXml()
 | |
| {
 | |
| 	QString filename;
 | |
| 	FractoriumSettings* s = m_Fractorium->m_Settings;
 | |
| 
 | |
| 	if (s->SaveAutoUnique() && m_LastSaveCurrent != "")
 | |
| 	{
 | |
| 		filename = EmberFile<T>::UniqueFilename(m_LastSaveCurrent);
 | |
| 	}
 | |
| 	else if (QFile::exists(m_LastSaveCurrent))
 | |
| 	{
 | |
| 		filename = m_LastSaveCurrent;
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		if (m_EmberFile.Size() == 1)
 | |
| 			filename = m_Fractorium->SetupSaveXmlDialog(m_EmberFile.m_Filename);//If only one ember present, just use parent filename.
 | |
| 		else
 | |
| 			filename = m_Fractorium->SetupSaveXmlDialog(QString::fromStdString(m_Ember.m_Name));//More than one ember present, use individual ember name.
 | |
| 	}
 | |
| 	
 | |
| 	if (filename != "")
 | |
| 	{
 | |
| 		Ember<T> ember = m_Ember;
 | |
| 		EmberToXml<T> writer;
 | |
| 		QFileInfo fileInfo(filename);
 | |
| 		xmlDocPtr tempEdit = ember.m_Edits;
 | |
| 
 | |
| 		SaveCurrentToOpenedFile();//Save the current ember back to the opened file before writing to disk.
 | |
| 		ApplyXmlSavingTemplate(ember);
 | |
| 		ember.m_Edits = writer.CreateNewEditdoc(&ember, nullptr, "edit", s->Nick().toStdString(), s->Url().toStdString(), s->Id().toStdString(), "", 0, 0);
 | |
| 
 | |
| 		if (tempEdit != nullptr)
 | |
| 			xmlFreeDoc(tempEdit);
 | |
| 
 | |
| 		if (writer.Save(filename.toStdString().c_str(), ember, 0, true, false, true))
 | |
| 		{
 | |
| 			s->SaveFolder(fileInfo.canonicalPath());
 | |
| 
 | |
| 			if (!s->SaveAutoUnique() || m_LastSaveCurrent == "")//Only save filename on first time through when doing auto unique names.
 | |
| 				m_LastSaveCurrent = filename;
 | |
| 		}
 | |
| 		else
 | |
| 			m_Fractorium->ShowCritical("Save Failed", "Could not save file, try saving to a different folder.");
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void Fractorium::OnActionSaveCurrentAsXml(bool checked) { m_Controller->SaveCurrentAsXml(); }
 | |
| 
 | |
| /// <summary>
 | |
| /// Save entire opened file Xml, using the Xml saving template values from the options on each ember.
 | |
| /// This will first save the current ember back to the opened file in memory before
 | |
| /// saving all to disk.
 | |
| /// </summary>
 | |
| template <typename T>
 | |
| void FractoriumEmberController<T>::SaveEntireFileAsXml()
 | |
| {
 | |
| 	QString filename;
 | |
| 	FractoriumSettings* s = m_Fractorium->m_Settings;
 | |
| 
 | |
| 	if (s->SaveAutoUnique() && m_LastSaveAll != "")
 | |
| 		filename = EmberFile<T>::UniqueFilename(m_LastSaveAll);
 | |
| 	else if (QFile::exists(m_LastSaveAll))
 | |
| 		filename = m_LastSaveAll;
 | |
| 	else
 | |
| 		filename = m_Fractorium->SetupSaveXmlDialog(m_EmberFile.m_Filename);
 | |
| 	
 | |
| 	if (filename != "")
 | |
| 	{
 | |
| 		EmberFile<T> emberFile;
 | |
| 		EmberToXml<T> writer;
 | |
| 		QFileInfo fileInfo(filename);
 | |
| 
 | |
| 		SaveCurrentToOpenedFile();//Save the current ember back to the opened file before writing to disk.
 | |
| 		emberFile = m_EmberFile;
 | |
| 
 | |
| 		for (auto& ember : emberFile.m_Embers)
 | |
| 			ApplyXmlSavingTemplate(ember);
 | |
| 
 | |
| 		if (writer.Save(filename.toStdString().c_str(), emberFile.m_Embers, 0, true, false, true))
 | |
| 		{
 | |
| 			if (!s->SaveAutoUnique() || m_LastSaveAll == "")//Only save filename on first time through when doing auto unique names.
 | |
| 				m_LastSaveAll = filename;
 | |
| 
 | |
| 			s->SaveFolder(fileInfo.canonicalPath());
 | |
| 		}
 | |
| 		else
 | |
| 			m_Fractorium->ShowCritical("Save Failed", "Could not save file, try saving to a different folder.");
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void Fractorium::OnActionSaveEntireFileAsXml(bool checked) { m_Controller->SaveEntireFileAsXml(); }
 | |
| 
 | |
| /// <summary>
 | |
| /// Show a file save dialog and save what is currently shown in the render window to disk as an image.
 | |
| /// </summary>
 | |
| /// <param name="checked">Ignored</param>
 | |
| void Fractorium::OnActionSaveCurrentScreen(bool checked)
 | |
| {
 | |
| 	QString filename = SetupSaveImageDialog(m_Controller->Name());
 | |
| 	auto renderer = m_Controller->Renderer();
 | |
| 	auto& pixels = *m_Controller->FinalImage();
 | |
| 	RendererCLBase* rendererCL = dynamic_cast<RendererCLBase*>(m_Controller->Renderer());
 | |
| 	auto stats = m_Controller->Stats();
 | |
| 	EmberImageComments comments = renderer->ImageComments(stats, 0, false, true);
 | |
| 
 | |
| 	if (rendererCL && renderer->PrepFinalAccumVector(pixels))
 | |
| 	{
 | |
| 		if (!rendererCL->ReadFinal(pixels.data()))
 | |
| 		{
 | |
| 			ShowCritical("GPU Read Error", "Could not read image from the GPU, aborting image save.", false);
 | |
| 			return;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	m_Controller->SaveCurrentRender(filename, comments, pixels, renderer->FinalRasW(), renderer->FinalRasH(), renderer->NumChannels(), renderer->BytesPerChannel());
 | |
| }
 | |
| 
 | |
| /// <summary>
 | |
| /// Save the current ember back to its position in the opened file.
 | |
| /// This does not save to disk.
 | |
| /// </summary>
 | |
| template <typename T>
 | |
| void FractoriumEmberController<T>::SaveCurrentToOpenedFile()
 | |
| {
 | |
| 	size_t i;
 | |
| 	bool fileFound = false;
 | |
| 
 | |
| 	for (i = 0; i < m_EmberFile.Size(); i++)
 | |
| 	{
 | |
| 		if ((m_Ember.m_Name == m_EmberFile.m_Embers[i].m_Name) &&//Check both to be extra sure.
 | |
| 			(m_Ember.m_Index == m_EmberFile.m_Embers[i].m_Index))
 | |
| 		{
 | |
| 			m_EmberFile.m_Embers[i] = m_Ember;
 | |
| 			fileFound = true;
 | |
| 			break;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	if (!fileFound)
 | |
| 	{
 | |
| 		StopPreviewRender();
 | |
| 		m_EmberFile.m_Embers.push_back(m_Ember);
 | |
| 		m_EmberFile.MakeNamesUnique();
 | |
| 		UpdateLibraryTree();
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		RenderPreviews(i, i + 1);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void Fractorium::OnActionSaveCurrentToOpenedFile(bool checked) { m_Controller->SaveCurrentToOpenedFile(); }
 | |
| 
 | |
| /// <summary>
 | |
| /// Exit the application.
 | |
| /// </summary>
 | |
| /// <param name="checked">Ignore.</param>
 | |
| void Fractorium::OnActionExit(bool checked)
 | |
| {
 | |
| 	closeEvent(nullptr);
 | |
| 	QApplication::exit();
 | |
| }
 | |
| 
 | |
| /// <summary>
 | |
| /// Undoes this instance.
 | |
| /// </summary>
 | |
| template <typename T>
 | |
| void FractoriumEmberController<T>::Undo()
 | |
| {
 | |
| 	if (m_UndoList.size() > 1 && m_UndoIndex > 0)
 | |
| 	{
 | |
| 		int index = m_Ember.GetTotalXformIndex(CurrentXform());
 | |
| 
 | |
| 		m_LastEditWasUndoRedo = true;
 | |
| 		m_UndoIndex = std::max(0u, m_UndoIndex - 1u);
 | |
| 		SetEmber(m_UndoList[m_UndoIndex], true);
 | |
| 		m_EditState = UNDO_REDO;
 | |
| 		
 | |
| 		if (index >= 0)
 | |
| 			m_Fractorium->CurrentXform(index);
 | |
| 
 | |
| 		m_Fractorium->ui.ActionUndo->setEnabled(m_UndoList.size() > 1 && (m_UndoIndex > 0));
 | |
| 		m_Fractorium->ui.ActionRedo->setEnabled(m_UndoList.size() > 1 && !(m_UndoIndex == m_UndoList.size() - 1));
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void Fractorium::OnActionUndo(bool checked) { m_Controller->Undo(); }
 | |
| 
 | |
| /// <summary>
 | |
| /// Redoes this instance.
 | |
| /// </summary>
 | |
| template <typename T>
 | |
| void FractoriumEmberController<T>::Redo()
 | |
| {
 | |
| 	if (m_UndoList.size() > 1 && m_UndoIndex < m_UndoList.size() - 1)
 | |
| 	{
 | |
| 		int index = m_Ember.GetTotalXformIndex(CurrentXform());
 | |
| 
 | |
| 		m_LastEditWasUndoRedo = true;
 | |
| 		m_UndoIndex = std::min<uint>(m_UndoIndex + 1, m_UndoList.size() - 1);
 | |
| 		SetEmber(m_UndoList[m_UndoIndex], true);
 | |
| 		m_EditState = UNDO_REDO;
 | |
| 		
 | |
| 		if (index >= 0)
 | |
| 			m_Fractorium->CurrentXform(index);
 | |
| 
 | |
| 		m_Fractorium->ui.ActionUndo->setEnabled(m_UndoList.size() > 1 && (m_UndoIndex > 0));
 | |
| 		m_Fractorium->ui.ActionRedo->setEnabled(m_UndoList.size() > 1 && !(m_UndoIndex == m_UndoList.size() - 1));
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void Fractorium::OnActionRedo(bool checked) { m_Controller->Redo(); }
 | |
| 
 | |
| /// <summary>
 | |
| /// Copy the current ember Xml to the clipboard.
 | |
| /// Apply Xml saving settings from the options first.
 | |
| /// </summary>
 | |
| template <typename T>
 | |
| void FractoriumEmberController<T>::CopyXml()
 | |
| {
 | |
| 	Ember<T> ember = m_Ember;
 | |
| 	EmberToXml<T> emberToXml;
 | |
| 	FractoriumSettings* settings = m_Fractorium->m_Settings;
 | |
| 
 | |
| 	ember.m_Quality         = settings->XmlQuality();
 | |
| 	ember.m_Supersample     = settings->XmlSupersample();
 | |
| 	ember.m_TemporalSamples = settings->XmlTemporalSamples();
 | |
| 	QApplication::clipboard()->setText(QString::fromStdString(emberToXml.ToString(ember, "", 0, false, false, true)));
 | |
| }
 | |
| 
 | |
| void Fractorium::OnActionCopyXml(bool checked) { m_Controller->CopyXml(); }
 | |
| 
 | |
| /// <summary>
 | |
| /// Copy the Xmls for all open embers as a single string to the clipboard, enclosed with the <flames> tag.
 | |
| /// Apply Xml saving settings from the options first for each ember.
 | |
| /// </summary>
 | |
| template <typename T>
 | |
| void FractoriumEmberController<T>::CopyAllXml()
 | |
| {
 | |
| 	ostringstream os;
 | |
| 	EmberToXml<T> emberToXml;
 | |
| 	FractoriumSettings* settings = m_Fractorium->m_Settings;
 | |
| 
 | |
| 	os << "<flames>\n";
 | |
| 
 | |
| 	for (auto& e : m_EmberFile.m_Embers)
 | |
| 	{
 | |
| 		Ember<T> ember = e;
 | |
| 
 | |
| 		ApplyXmlSavingTemplate(ember);
 | |
| 		os << emberToXml.ToString(ember, "", 0, false, false, true);
 | |
| 	}
 | |
| 
 | |
| 	os << "</flames>\n";
 | |
| 	QApplication::clipboard()->setText(QString::fromStdString(os.str()));
 | |
| }
 | |
| 
 | |
| void Fractorium::OnActionCopyAllXml(bool checked) { m_Controller->CopyAllXml(); }
 | |
| 
 | |
| /// <summary>
 | |
| /// Convert the Xml text from the clipboard to an ember, add it to the end
 | |
| /// of the current file and set it as the current ember. If multiple Xmls were
 | |
| /// copied to the clipboard and were enclosed in <flames> tags, then all of them will be added.
 | |
| /// Clears the undo state.
 | |
| /// Resets the rendering process.
 | |
| /// </summary>
 | |
| template <typename T>
 | |
| void FractoriumEmberController<T>::PasteXmlAppend()
 | |
| {
 | |
| 	uint i, previousSize = m_EmberFile.Size();
 | |
| 	string s, errors;
 | |
| 	XmlToEmber<T> parser;
 | |
| 	vector<Ember<T>> embers;
 | |
| 	QTextCodec* codec = QTextCodec::codecForName("UTF-8");
 | |
| 	QByteArray b = codec->fromUnicode(QApplication::clipboard()->text());
 | |
| 
 | |
| 	s.reserve(b.size());
 | |
| 
 | |
| 	for (i = 0; i < b.size(); i++)
 | |
| 	{
 | |
| 		if (uint(b[i]) < 128u)
 | |
| 			s.push_back(b[i]);
 | |
| 	}
 | |
| 
 | |
| 	b.clear();
 | |
| 	StopPreviewRender();
 | |
| 	parser.Parse(reinterpret_cast<byte*>(const_cast<char*>(s.c_str())), "", embers);
 | |
| 	errors = parser.ErrorReportString();
 | |
| 
 | |
| 	if (errors != "")
 | |
| 	{
 | |
| 		m_Fractorium->ShowCritical("Paste Error", QString::fromStdString(errors));
 | |
| 	}
 | |
| 
 | |
| 	if (!embers.empty())
 | |
| 	{
 | |
| 		for (i = 0; i < embers.size(); i++)
 | |
| 		{
 | |
| 			embers[i].m_Index = m_EmberFile.Size();
 | |
| 			ConstrainDimensions(embers[i]);//Do not exceed the max texture size.
 | |
| 		
 | |
| 			//Also ensure it has a name.
 | |
| 			if (embers[i].m_Name == "" || embers[i].m_Name == "No name")
 | |
| 				embers[i].m_Name = ToString<qulonglong>(embers[i].m_Index).toStdString();
 | |
| 
 | |
| 			m_EmberFile.m_Embers.push_back(embers[i]);//Will invalidate the pointers contained in the EmberTreeWidgetItems, UpdateLibraryTree() will resync.
 | |
| 		}
 | |
| 
 | |
| 		m_EmberFile.MakeNamesUnique();
 | |
| 		UpdateLibraryTree();
 | |
| 		SetEmber(previousSize);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void Fractorium::OnActionPasteXmlAppend(bool checked) { m_Controller->PasteXmlAppend(); }
 | |
| 
 | |
| /// <summary>
 | |
| /// Convert the Xml text from the clipboard to an ember, overwrite the
 | |
| /// current file and set the first as the current ember. If multiple Xmls were
 | |
| /// copied to the clipboard and were enclosed in <flames> tags, then the current file will contain all of them.
 | |
| /// </summary>
 | |
| template <typename T>
 | |
| void FractoriumEmberController<T>::PasteXmlOver()
 | |
| {
 | |
| 	uint i;
 | |
| 	string s, errors;
 | |
| 	XmlToEmber<T> parser;
 | |
| 	Ember<T> backupEmber = m_EmberFile.m_Embers[0];
 | |
| 	QTextCodec* codec = QTextCodec::codecForName("UTF-8");
 | |
| 	QByteArray b = codec->fromUnicode(QApplication::clipboard()->text());
 | |
| 	
 | |
| 	s.reserve(b.size());
 | |
| 	
 | |
| 	for (i = 0; i < b.size(); i++)
 | |
| 	{
 | |
| 		if (uint(b[i]) < 128u)
 | |
| 			s.push_back(b[i]);
 | |
| 	}
 | |
| 
 | |
| 	b.clear();
 | |
| 	StopPreviewRender();
 | |
| 	m_EmberFile.m_Embers.clear();//Will invalidate the pointers contained in the EmberTreeWidgetItems, UpdateLibraryTree() will resync.
 | |
| 	parser.Parse(reinterpret_cast<byte*>(const_cast<char*>(s.c_str())), "", m_EmberFile.m_Embers);
 | |
| 	errors = parser.ErrorReportString();
 | |
| 
 | |
| 	if (errors != "")
 | |
| 	{
 | |
| 		m_Fractorium->ShowCritical("Paste Error", QString::fromStdString(errors));
 | |
| 	}
 | |
| 
 | |
| 	if (m_EmberFile.Size())
 | |
| 	{
 | |
| 		for (i = 0; i < m_EmberFile.Size(); i++)
 | |
| 		{
 | |
| 			m_EmberFile.m_Embers[i].m_Index = i;
 | |
| 			ConstrainDimensions(m_EmberFile.m_Embers[i]);//Do not exceed the max texture size.
 | |
| 		
 | |
| 			//Also ensure it has a name.
 | |
| 			if (m_EmberFile.m_Embers[i].m_Name == "" || m_EmberFile.m_Embers[i].m_Name == "No name")
 | |
| 				m_EmberFile.m_Embers[i].m_Name = ToString<qulonglong>(m_EmberFile.m_Embers[i].m_Index).toStdString();
 | |
| 		}
 | |
| 	}
 | |
| 	else
 | |
| 	{
 | |
| 		backupEmber.m_Index = 0;
 | |
| 		m_EmberFile.m_Embers.push_back(backupEmber);
 | |
| 	}
 | |
| 
 | |
| 	m_EmberFile.MakeNamesUnique();
 | |
| 	FillLibraryTree();
 | |
| 	SetEmber(0);
 | |
| }
 | |
| 
 | |
| void Fractorium::OnActionPasteXmlOver(bool checked) { m_Controller->PasteXmlOver(); }
 | |
| 
 | |
| /// <summary>
 | |
| /// Copy the selected xforms.
 | |
| /// Note this will also copy final if selected.
 | |
| /// If none selected, just copy current.
 | |
| /// </summary>
 | |
| template <typename T>
 | |
| void FractoriumEmberController<T>::CopySelectedXforms()
 | |
| {
 | |
| 	m_CopiedXforms.clear();
 | |
| 	m_CopiedFinalXform.Clear();
 | |
| 
 | |
| 	UpdateXform([&](Xform<T>* xform)
 | |
| 	{
 | |
| 		if (m_Ember.IsFinalXform(xform))
 | |
| 			m_CopiedFinalXform = *xform;
 | |
| 		else
 | |
| 			m_CopiedXforms.push_back(*xform);
 | |
| 	}, UPDATE_SELECTED, false);
 | |
| 	m_Fractorium->ui.ActionPasteSelectedXforms->setEnabled(true);
 | |
| }
 | |
| 
 | |
| void Fractorium::OnActionCopySelectedXforms(bool checked)
 | |
| {
 | |
| 	m_Controller->CopySelectedXforms();
 | |
| }
 | |
| 
 | |
| /// <summary>
 | |
| /// Paste the selected xforms.
 | |
| /// Note this will also paste/overwrite final if previously copied.
 | |
| /// Resets the rendering process.
 | |
| /// </summary>
 | |
| template <typename T>
 | |
| void FractoriumEmberController<T>::PasteSelectedXforms()
 | |
| {
 | |
| 	Update([&]()
 | |
| 	{
 | |
| 		for (auto& it : m_CopiedXforms)
 | |
| 			m_Ember.AddXform(it);
 | |
| 
 | |
| 		if (!m_CopiedFinalXform.Empty())
 | |
| 			m_Ember.SetFinalXform(m_CopiedFinalXform);
 | |
| 
 | |
| 		FillXforms();
 | |
| 	});
 | |
| }
 | |
| 
 | |
| void Fractorium::OnActionPasteSelectedXforms(bool checked)
 | |
| {
 | |
| 	m_Controller->PasteSelectedXforms();
 | |
| }
 | |
| 
 | |
| /// <summary>
 | |
| /// Reset dock widgets and tabs to their default position.
 | |
| /// Note that there is a bug in Qt, where it will only move them all to the left side if at least
 | |
| /// one is on the left side, or they are all floating. If one or more are docked right, and none are docked
 | |
| /// left, then it will put them all on the right side. Hopefully this isn't too much of a problem.
 | |
| /// </summary>
 | |
| void Fractorium::OnActionResetWorkspace(bool checked)
 | |
| {
 | |
| 	QDockWidget* firstDock = nullptr;
 | |
| 
 | |
| 	for (auto dock : m_Docks)
 | |
| 	{
 | |
| 		dock->setFloating(true);
 | |
| 		dock->setGeometry(QRect(100, 100, dock->width(), dock->height()));//Doesn't seem to have an effect.
 | |
| 		dock->setFloating(false);
 | |
| 		dock->show();
 | |
| 
 | |
| 		if (firstDock)
 | |
| 			tabifyDockWidget(firstDock, dock);
 | |
| 
 | |
| 		firstDock = dock;
 | |
| 	}
 | |
| 
 | |
| 	ui.LibraryDockWidget->raise();
 | |
| 	ui.LibraryDockWidget->show();
 | |
| }
 | |
| 
 | |
| /// <summary>
 | |
| /// Add reflective symmetry to the current ember.
 | |
| /// Resets the rendering process.
 | |
| /// </summary>
 | |
| template <typename T>
 | |
| void FractoriumEmberController<T>::AddReflectiveSymmetry()
 | |
| {
 | |
| 	QComboBox* combo = m_Fractorium->ui.CurrentXformCombo;
 | |
| 
 | |
| 	Update([&]()
 | |
| 	{
 | |
| 		m_Ember.AddSymmetry(-1, m_Rand);
 | |
| 		int index = m_Ember.TotalXformCount() - (m_Ember.UseFinalXform() ? 2 : 1);//Set index to the last item before final.
 | |
| 		FillXforms(index);
 | |
| 	});
 | |
| }
 | |
| 
 | |
| void Fractorium::OnActionAddReflectiveSymmetry(bool checked) { m_Controller->AddReflectiveSymmetry(); }
 | |
| 
 | |
| /// <summary>
 | |
| /// Add rotational symmetry to the current ember.
 | |
| /// Resets the rendering process.
 | |
| /// </summary>
 | |
| template <typename T>
 | |
| void FractoriumEmberController<T>::AddRotationalSymmetry()
 | |
| {
 | |
| 	QComboBox* combo = m_Fractorium->ui.CurrentXformCombo;
 | |
| 
 | |
| 	Update([&]()
 | |
| 	{
 | |
| 		m_Ember.AddSymmetry(2, m_Rand);
 | |
| 		int index = m_Ember.TotalXformCount() - (m_Ember.UseFinalXform() ? 2 : 1);//Set index to the last item before final.
 | |
| 		FillXforms(index);
 | |
| 	});
 | |
| }
 | |
| 
 | |
| void Fractorium::OnActionAddRotationalSymmetry(bool checked) { m_Controller->AddRotationalSymmetry(); }
 | |
| 
 | |
| /// <summary>
 | |
| /// Add both reflective and rotational symmetry to the current ember.
 | |
| /// Resets the rendering process.
 | |
| /// </summary>
 | |
| template <typename T>
 | |
| void FractoriumEmberController<T>::AddBothSymmetry()
 | |
| {
 | |
| 	QComboBox* combo = m_Fractorium->ui.CurrentXformCombo;
 | |
| 
 | |
| 	Update([&]()
 | |
| 	{
 | |
| 		m_Ember.AddSymmetry(-2, m_Rand);
 | |
| 		int index = m_Ember.TotalXformCount() - (m_Ember.UseFinalXform() ? 2 : 1);//Set index to the last item before final.
 | |
| 		FillXforms(index);
 | |
| 	});
 | |
| }
 | |
| 
 | |
| void Fractorium::OnActionAddBothSymmetry(bool checked) { m_Controller->AddBothSymmetry(); }
 | |
| 
 | |
| /// <summary>
 | |
| /// Adds a FlattenVariation to every xform in the current ember.
 | |
| /// Resets the rendering process.
 | |
| /// </summary>
 | |
| template <typename T>
 | |
| void FractoriumEmberController<T>::Flatten() { UpdateXform([&] (Xform<T>* xform) { m_Ember.Flatten(XmlToEmber<T>::m_FlattenNames); FillVariationTreeWithXform(xform); }); }
 | |
| void Fractorium::OnActionFlatten(bool checked) { m_Controller->Flatten(); }
 | |
| 	
 | |
| /// <summary>
 | |
| /// Removes pre/reg/post FlattenVariation from every xform in the current ember.
 | |
| /// Resets the rendering process.
 | |
| /// </summary>
 | |
| template <typename T>
 | |
| void FractoriumEmberController<T>::Unflatten() { UpdateXform([&] (Xform<T>* xform) { m_Ember.Unflatten(); FillVariationTreeWithXform(xform); }); }
 | |
| void Fractorium::OnActionUnflatten(bool checked) { m_Controller->Unflatten(); }
 | |
| 
 | |
| /// <summary>
 | |
| /// Delete all but one xform in the current ember.
 | |
| /// Clear that xform's variations.
 | |
| /// Resets the rendering process.
 | |
| /// </summary>
 | |
| template <typename T>
 | |
| void FractoriumEmberController<T>::ClearFlame()
 | |
| {
 | |
| 	Update([&]()
 | |
| 	{
 | |
| 		while (m_Ember.TotalXformCount() > 1)
 | |
| 			m_Ember.DeleteTotalXform(m_Ember.TotalXformCount() - 1);
 | |
| 
 | |
| 		if (m_Ember.XformCount() == 1)
 | |
| 		{
 | |
| 			if (Xform<T>* xform = m_Ember.GetXform(0))
 | |
| 			{
 | |
| 				xform->Clear();
 | |
| 				xform->ParentEmber(&m_Ember);
 | |
| 			}
 | |
| 		}
 | |
| 
 | |
| 		FillXforms();
 | |
| 	});
 | |
| }
 | |
| 
 | |
| void Fractorium::OnActionClearFlame(bool checked) { m_Controller->ClearFlame(); }
 | |
| 
 | |
| /// <summary>
 | |
| /// Re-render all previews.
 | |
| /// </summary>
 | |
| void Fractorium::OnActionRenderPreviews(bool checked)
 | |
| {
 | |
| 	m_Controller->RenderPreviews();
 | |
| }
 | |
| 
 | |
| /// <summary>
 | |
| /// Stop all previews from being rendered. This is handy if the user
 | |
| /// opens a large file with many embers in it, such as an animation sequence.
 | |
| /// </summary>
 | |
| void Fractorium::OnActionStopRenderingPreviews(bool checked) { m_Controller->StopPreviewRender(); }
 | |
| 
 | |
| /// <summary>
 | |
| /// Show the final render dialog as a modeless dialog to allow
 | |
| /// the user to minimize the main window while doing a lengthy final render.
 | |
| /// Note: The user probably should not be otherwise interacting with the main GUI
 | |
| /// while the final render is taking place.
 | |
| /// </summary>
 | |
| /// <param name="checked">Ignored</param>
 | |
| void Fractorium::OnActionFinalRender(bool checked)
 | |
| {
 | |
| 	//First completely stop what the current rendering process is doing.
 | |
| 	m_Controller->DeleteRenderer();//Delete the renderer, but not the controller.
 | |
| 	OnActionSaveCurrentToOpenedFile(true);//Save whatever was edited back to the current open file.
 | |
| 	m_RenderStatusLabel->setText("Renderer stopped.");
 | |
| 	m_FinalRenderDialog->show();
 | |
| }
 | |
| 
 | |
| /// <summary>
 | |
| /// Called when the final render dialog has been closed.
 | |
| /// </summary>
 | |
| /// <param name="result">Ignored</param>
 | |
| void Fractorium::OnFinalRenderClose(int result)
 | |
| {
 | |
| 	m_RenderStatusLabel->setText("Renderer starting...");
 | |
| 	StartRenderTimer();//Re-create the renderer and start rendering again.
 | |
| }
 | |
| 
 | |
| /// <summary>
 | |
| /// Show the final options dialog.
 | |
| /// Restart rendering and sync options after the options dialog is dismissed with Ok.
 | |
| /// Called when the options dialog is finished with ok.
 | |
| /// </summary>
 | |
| /// <param name="checked">Ignored</param>
 | |
| void Fractorium::OnActionOptions(bool checked)
 | |
| {
 | |
| 	if (m_OptionsDialog->exec())
 | |
| 	{
 | |
| 		SyncOptionsToToolbar();//This won't trigger a recreate, the call below handles it.
 | |
| 		ShutdownAndRecreateFromOptions();//This will recreate the controller and/or the renderer from the options if necessary, then start the render timer.
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /// <summary>
 | |
| /// Show the about dialog.
 | |
| /// </summary>
 | |
| /// <param name="checked">Ignored</param>
 | |
| void Fractorium::OnActionAbout(bool checked)
 | |
| {
 | |
| 	m_AboutDialog->exec();
 | |
| }
 | |
| 
 | |
| template class FractoriumEmberController<float>;
 | |
| 
 | |
| #ifdef DO_DOUBLE
 | |
| 	template class FractoriumEmberController<double>;
 | |
| #endif
 |