2014-07-08 03:11:14 -04:00
# include "FractoriumPch.h"
# include "GLWidget.h"
# include "Fractorium.h"
/// <summary>
/// Constructor which passes parent widget to the base and initializes OpenGL profile.
/// This will need to change in the future to implement all drawing as shader programs.
/// </summary>
2014-12-11 00:50:15 -05:00
/// <param name="p">The parent widget</param>
GLWidget : : GLWidget ( QWidget * p )
2015-01-01 10:17:05 -05:00
: QOpenGLWidget ( p )
2014-07-08 03:11:14 -04:00
{
2015-01-01 10:17:05 -05:00
QSurfaceFormat qsf ;
qsf . setSwapInterval ( 1 ) ; //Vsync.
qsf . setSwapBehavior ( QSurfaceFormat : : DoubleBuffer ) ;
qsf . setVersion ( 2 , 0 ) ;
setFormat ( qsf ) ;
2014-07-08 03:11:14 -04:00
}
/// <summary>
/// Empty destructor.
/// </summary>
GLWidget : : ~ GLWidget ( )
{
}
2015-01-02 18:11:36 -05:00
/// <summary>
/// A manual initialization that must be called immediately after the main window is shown
/// and the virtual initializeGL() is called.
/// </summary>
void GLWidget : : InitGL ( )
{
if ( ! m_Init )
{
int w = m_Fractorium - > ui . GLParentScrollArea - > width ( ) ;
int h = m_Fractorium - > ui . GLParentScrollArea - > height ( ) ;
SetDimensions ( w , h ) ;
m_Fractorium - > m_WidthSpin - > setValue ( w ) ;
m_Fractorium - > m_HeightSpin - > setValue ( h ) ;
2016-03-28 21:49:10 -04:00
//Start with a flock of random embers. Can't do this until now because the window wasn't maximized yet, so the sizes would have been off.
2015-01-02 18:11:36 -05:00
m_Fractorium - > OnActionNewFlock ( false ) ;
m_Fractorium - > m_Controller - > DelayedStartRenderTimer ( ) ;
m_Init = true ;
}
}
2014-07-08 03:11:14 -04:00
/// <summary>
/// Draw the final rendered image as a texture on a quad that is the same size as the window.
/// Different action is taken based on whether a CPU or OpenCL renderer is used.
/// For CPU, the output image buffer must be copied to OpenGL every time it's drawn.
/// For OpenCL, the output image and the texture are the same thing, so no copying is necessary
/// and all image memory remains on the card.
/// </summary>
void GLWidget : : DrawQuad ( )
{
glEnable ( GL_TEXTURE_2D ) ;
glEnable ( GL_BLEND ) ;
glBlendFunc ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA ) ;
2016-02-12 00:38:21 -05:00
auto renderer = m_Fractorium - > m_Controller - > Renderer ( ) ;
auto finalImage = m_Fractorium - > m_Controller - > FinalImage ( ) ;
2014-07-08 03:11:14 -04:00
//Ensure all allocation has taken place first.
if ( m_OutputTexID ! = 0 & & finalImage & & ! finalImage - > empty ( ) )
{
glBindTexture ( GL_TEXTURE_2D , m_OutputTexID ) ; //The texture to draw to.
2016-01-04 19:50:15 -05:00
2014-07-08 03:11:14 -04:00
//Only draw if the dimensions match exactly.
2014-12-11 00:50:15 -05:00
if ( m_TexWidth = = width ( ) & & m_TexHeight = = height ( ) & & ( ( m_TexWidth * m_TexHeight * 4 ) = = GLint ( finalImage - > size ( ) ) ) )
2014-07-08 03:11:14 -04:00
{
glMatrixMode ( GL_PROJECTION ) ;
glPushMatrix ( ) ;
glLoadIdentity ( ) ;
glOrtho ( 0 , 1 , 1 , 0 , - 1 , 1 ) ;
glMatrixMode ( GL_MODELVIEW ) ;
glPushMatrix ( ) ;
glLoadIdentity ( ) ;
//Copy data from CPU to OpenGL if using a CPU renderer. This is not needed when using OpenCL.
2016-01-04 19:50:15 -05:00
if ( renderer - > RendererType ( ) = = eRendererType : : CPU_RENDERER )
2014-07-08 03:11:14 -04:00
glTexSubImage2D ( GL_TEXTURE_2D , 0 , 0 , 0 , m_TexWidth , m_TexHeight , GL_RGBA , GL_UNSIGNED_BYTE , finalImage - > data ( ) ) ;
glBegin ( GL_QUADS ) ; //This will need to be converted to a shader at some point in the future.
glTexCoord2f ( 0.0 , 0.0 ) ; glVertex2f ( 0.0 , 0.0 ) ;
glTexCoord2f ( 0.0 , 1.0 ) ; glVertex2f ( 0.0 , 1.0 ) ;
glTexCoord2f ( 1.0 , 1.0 ) ; glVertex2f ( 1.0 , 1.0 ) ;
glTexCoord2f ( 1.0 , 0.0 ) ; glVertex2f ( 1.0 , 0.0 ) ;
glEnd ( ) ;
glMatrixMode ( GL_PROJECTION ) ;
glPopMatrix ( ) ;
glMatrixMode ( GL_MODELVIEW ) ;
glPopMatrix ( ) ;
}
glBindTexture ( GL_TEXTURE_2D , 0 ) ; //Stop using this texture.
}
glDisable ( GL_BLEND ) ;
glDisable ( GL_TEXTURE_2D ) ;
}
/// <summary>
/// Set drag and drag modifier states to nothing.
/// </summary>
void GLEmberControllerBase : : ClearDrag ( )
{
m_DragModifier = 0 ;
2016-01-04 19:50:15 -05:00
m_DragState = eDragState : : DragNone ;
2014-07-08 03:11:14 -04:00
}
/// <summary>
/// Wrapper around Allocate() call on the GL widget.
/// </summary>
bool GLEmberControllerBase : : Allocate ( bool force ) { return m_GL - > Allocate ( force ) ; }
2016-01-04 19:50:15 -05:00
/// <summary>
/// Helpers to set/get/clear which keys are pressed while dragging.
/// </summary>
/// <returns>bool</returns>
2016-02-12 00:38:21 -05:00
bool GLEmberControllerBase : : GetAlt ( ) { return ( m_DragModifier & et ( eDragModifier : : DragModAlt ) ) = = et ( eDragModifier : : DragModAlt ) ; }
bool GLEmberControllerBase : : GetShift ( ) { return ( m_DragModifier & et ( eDragModifier : : DragModShift ) ) = = et ( eDragModifier : : DragModShift ) ; }
bool GLEmberControllerBase : : GetControl ( ) { return ( m_DragModifier & et ( eDragModifier : : DragModControl ) ) = = et ( eDragModifier : : DragModControl ) ; }
void GLEmberControllerBase : : SetAlt ( ) { m_DragModifier | = et ( eDragModifier : : DragModAlt ) ; }
void GLEmberControllerBase : : SetShift ( ) { m_DragModifier | = et ( eDragModifier : : DragModShift ) ; }
void GLEmberControllerBase : : SetControl ( ) { m_DragModifier | = et ( eDragModifier : : DragModControl ) ; }
void GLEmberControllerBase : : ClearAlt ( ) { m_DragModifier & = ~ et ( eDragModifier : : DragModAlt ) ; }
void GLEmberControllerBase : : ClearShift ( ) { m_DragModifier & = ~ et ( eDragModifier : : DragModShift ) ; }
2016-01-04 19:50:15 -05:00
void GLEmberControllerBase : : ClearControl ( ) { m_DragModifier & = ~ et ( eDragModifier : : DragModControl ) ; }
2014-07-08 03:11:14 -04:00
/// <summary>
/// Clear the OpenGL output window to be the background color of the current ember.
/// Both buffers must be cleared, else artifacts will show up.
/// </summary>
template < typename T >
void GLEmberController < T > : : ClearWindow ( )
{
2016-02-02 20:51:58 -05:00
auto ember = m_FractoriumEmberController - > CurrentEmber ( ) ;
2014-10-14 11:53:15 -04:00
m_GL - > makeCurrent ( ) ;
2014-07-08 03:11:14 -04:00
m_GL - > glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ;
m_GL - > glClearColor ( ember - > m_Background . r , ember - > m_Background . g , ember - > m_Background . b , 1.0 ) ;
}
/// <summary>
/// Set the currently selected xform.
/// The currently selected xform is drawn with a circle around it, with all others only showing their axes.
/// </summary>
/// <param name="xform">The xform.</param>
template < typename T >
void GLEmberController < T > : : SetSelectedXform ( Xform < T > * xform )
{
//By doing this check, it prevents triggering unnecessary events when selecting an xform on this window with the mouse,
//which will set the combo box on the main window, which will trigger this call. However, if the xform has been selected
//here with the mouse, the window has already repainted, so there's no need to do it again.
if ( m_SelectedXform ! = xform | | m_HoverXform ! = xform )
{
m_HoverXform = xform ;
m_SelectedXform = xform ;
if ( m_GL - > m_Init )
2015-01-01 10:17:05 -05:00
//m_GL->update();
2014-07-08 03:11:14 -04:00
m_GL - > repaint ( ) ; //Force immediate redraw with repaint() instead of update().
}
}
/// <summary>
/// Setters for main window pointers.
/// </summary>
void GLWidget : : SetMainWindow ( Fractorium * f ) { m_Fractorium = f ; }
/// <summary>
/// Getters for OpenGL state.
/// </summary>
bool GLWidget : : Init ( ) { return m_Init ; }
bool GLWidget : : Drawing ( ) { return m_Drawing ; }
2014-10-14 11:53:15 -04:00
GLint GLWidget : : MaxTexSize ( ) { return m_MaxTexSize ; }
2014-07-08 03:11:14 -04:00
GLuint GLWidget : : OutputTexID ( ) { return m_OutputTexID ; }
/// <summary>
/// Initialize OpenGL, called once at startup after the main window constructor finishes.
2014-10-14 11:53:15 -04:00
/// Although it seems an awkward place to put some of this setup code, the dimensions of the
/// main window and its widgets are not fully initialized before this is called.
2014-07-08 03:11:14 -04:00
/// Once this is done, the render timer is started after a short delay.
/// Rendering is then clear to begin.
/// </summary>
void GLWidget : : initializeGL ( )
{
if ( ! m_Init & & initializeOpenGLFunctions ( ) & & m_Fractorium )
{
glClearColor ( 0.0 , 0.0 , 0.0 , 1.0 ) ;
2014-10-14 11:53:15 -04:00
glEnable ( GL_TEXTURE_2D ) ;
glGetIntegerv ( GL_MAX_TEXTURE_SIZE , & m_MaxTexSize ) ;
glDisable ( GL_TEXTURE_2D ) ;
m_Fractorium - > m_WidthSpin - > setMaximum ( m_MaxTexSize ) ;
m_Fractorium - > m_HeightSpin - > setMaximum ( m_MaxTexSize ) ;
2014-07-08 03:11:14 -04:00
}
}
/// <summary>
/// The main drawing/update function.
/// First the quad will be drawn, then the remaining affine circles.
/// </summary>
void GLWidget : : paintGL ( )
{
2016-02-02 20:51:58 -05:00
auto controller = m_Fractorium - > m_Controller . get ( ) ;
2014-07-08 03:11:14 -04:00
//Ensure there is a renderer and that it's supposed to be drawing, signified by the running timer.
2016-02-02 20:51:58 -05:00
if ( controller & & controller - > Renderer ( ) )
2014-07-08 03:11:14 -04:00
{
2016-02-12 00:38:21 -05:00
auto renderer = controller - > Renderer ( ) ;
2014-07-08 03:11:14 -04:00
m_Drawing = true ;
2016-02-12 00:38:21 -05:00
GLController ( ) - > DrawImage ( ) ;
2014-07-08 03:11:14 -04:00
//Affine drawing.
bool pre = m_Fractorium - > ui . PreAffineGroupBox - > isChecked ( ) ;
bool post = m_Fractorium - > ui . PostAffineGroupBox - > isChecked ( ) ;
2016-01-12 23:42:12 -05:00
float unitX = std : : abs ( renderer - > UpperRightX ( false ) - renderer - > LowerLeftX ( false ) ) / 2.0f ;
float unitY = std : : abs ( renderer - > UpperRightY ( false ) - renderer - > LowerLeftY ( false ) ) / 2.0f ;
2014-07-08 03:11:14 -04:00
glEnable ( GL_DEPTH_TEST ) ;
glEnable ( GL_BLEND ) ;
glEnable ( GL_LINE_SMOOTH ) ;
glEnable ( GL_POINT_SMOOTH ) ;
glBlendFuncSeparate ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA , GL_ONE , GL_ZERO ) ;
glMatrixMode ( GL_PROJECTION ) ;
glPushMatrix ( ) ;
glLoadIdentity ( ) ;
glOrtho ( - unitX , unitX , - unitY , unitY , - 1 , 1 ) ; //Projection matrix: OpenGL camera is always centered, just move the ember internally inside the renderer.
glMatrixMode ( GL_MODELVIEW ) ;
glPushMatrix ( ) ;
glLoadIdentity ( ) ;
glDisable ( GL_DEPTH_TEST ) ;
controller - > GLController ( ) - > DrawAffines ( pre , post ) ;
glMatrixMode ( GL_PROJECTION ) ;
glPopMatrix ( ) ;
glMatrixMode ( GL_MODELVIEW ) ;
glPopMatrix ( ) ;
glDisable ( GL_DEPTH_TEST ) ;
glDisable ( GL_BLEND ) ;
glDisable ( GL_LINE_SMOOTH ) ;
glDisable ( GL_POINT_SMOOTH ) ;
m_Drawing = false ;
}
}
/// <summary>
/// Draw the image on the quad.
/// </summary>
template < typename T >
void GLEmberController < T > : : DrawImage ( )
{
2016-02-02 20:51:58 -05:00
auto renderer = m_FractoriumEmberController - > Renderer ( ) ;
auto ember = m_FractoriumEmberController - > CurrentEmber ( ) ;
2014-07-08 03:11:14 -04:00
m_GL - > glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ;
m_GL - > glClearColor ( ember - > m_Background . r , ember - > m_Background . g , ember - > m_Background . b , 1.0 ) ;
m_GL - > glDisable ( GL_DEPTH_TEST ) ;
renderer - > EnterFinalAccum ( ) ; //Lock, may not be necessary, but just in case.
renderer - > EnterResize ( ) ;
2016-01-04 19:50:15 -05:00
2014-10-14 11:53:15 -04:00
if ( SizesMatch ( ) ) //Ensure all sizes are correct. If not, do nothing.
2014-07-08 03:11:14 -04:00
{
2014-12-06 00:05:09 -05:00
vector < byte > * finalImage = m_FractoriumEmberController - > FinalImage ( ) ;
2016-01-04 19:50:15 -05:00
if ( ( renderer - > RendererType ( ) = = eRendererType : : OPENCL_RENDERER ) | | finalImage ) //Final image only matters for CPU renderer.
if ( ( renderer - > RendererType ( ) = = eRendererType : : OPENCL_RENDERER ) | | finalImage - > size ( ) = = renderer - > FinalBufferSize ( ) )
2014-07-08 03:11:14 -04:00
m_GL - > DrawQuad ( ) ; //Output image is drawn here.
}
2016-01-04 19:50:15 -05:00
2014-07-08 03:11:14 -04:00
renderer - > LeaveResize ( ) ; //Unlock, may not be necessary.
renderer - > LeaveFinalAccum ( ) ;
}
/// <summary>
/// Draw the affine circles.
/// </summary>
/// <param name="pre">True to draw pre affines, else don't.</param>
/// <param name="post">True to draw post affines, else don't.</param>
template < typename T >
void GLEmberController < T > : : DrawAffines ( bool pre , bool post )
{
QueryVMP ( ) ; //Resolves to float or double specialization function depending on T.
2016-02-02 20:51:58 -05:00
auto ember = m_FractoriumEmberController - > CurrentEmber ( ) ;
2016-01-04 19:50:15 -05:00
bool dragging = m_DragState = = eDragState : : DragDragging ;
2014-07-08 03:11:14 -04:00
//Draw grid if control key is pressed.
2016-01-04 19:50:15 -05:00
if ( m_GL - > hasFocus ( ) & & GetControl ( ) )
2014-07-08 03:11:14 -04:00
{
m_GL - > glLineWidth ( 1.0f ) ;
2016-02-02 20:51:58 -05:00
m_GL - > DrawGrid ( m_FractoriumEmberController - > AffineScaleLockedToCurrent ( ) ) ;
2014-07-08 03:11:14 -04:00
}
2014-07-26 15:03:51 -04:00
2014-07-08 03:11:14 -04:00
//When dragging, only draw the selected xform's affine and hide all others.
2014-07-26 15:03:51 -04:00
if ( ! m_Fractorium - > m_Settings - > ShowAllXforms ( ) & & dragging )
2014-07-08 03:11:14 -04:00
{
if ( m_SelectedXform )
2016-01-04 19:50:15 -05:00
DrawAffine ( m_SelectedXform , m_AffineType = = eAffineType : : AffinePre , true ) ;
2014-07-08 03:11:14 -04:00
}
2014-07-26 15:03:51 -04:00
else //Show all while dragging, or not dragging just hovering/mouse move.
2014-07-08 03:11:14 -04:00
{
if ( pre & & m_Fractorium - > DrawAllPre ( ) ) //Draw all pre affine if specified.
{
--User changes
-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.
2015-09-12 21:33:45 -04:00
for ( size_t i = 0 ; i < ember - > TotalXformCount ( ) ; i + + )
2014-07-08 03:11:14 -04:00
{
2016-02-02 20:51:58 -05:00
auto xform = ember - > GetTotalXform ( i ) ;
2016-02-20 21:44:52 -05:00
bool selected = m_Fractorium - > IsXformSelected ( i ) | | ( dragging ? ( m_SelectedXform = = xform ) : ( m_HoverXform = = xform ) ) ;
2014-07-08 03:11:14 -04:00
DrawAffine ( xform , true , selected ) ;
}
}
else if ( pre & & m_HoverXform ) //Only draw current pre affine.
{
DrawAffine ( m_HoverXform , true , true ) ;
}
if ( post & & m_Fractorium - > DrawAllPost ( ) ) //Draw all post affine if specified.
{
--User changes
-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.
2015-09-12 21:33:45 -04:00
for ( size_t i = 0 ; i < ember - > TotalXformCount ( ) ; i + + )
2014-07-08 03:11:14 -04:00
{
2016-02-02 20:51:58 -05:00
auto xform = ember - > GetTotalXform ( i ) ;
2016-02-20 21:44:52 -05:00
bool selected = m_Fractorium - > IsXformSelected ( i ) | | ( dragging ? ( m_SelectedXform = = xform ) : ( m_HoverXform = = xform ) ) ;
2014-07-08 03:11:14 -04:00
DrawAffine ( xform , false , selected ) ;
}
}
else if ( post & & m_HoverXform ) //Only draw current post affine.
{
DrawAffine ( m_HoverXform , false , true ) ;
}
2014-07-26 15:03:51 -04:00
}
2014-07-08 03:11:14 -04:00
2014-07-26 15:03:51 -04:00
if ( dragging ) //Draw large yellow dot on select or drag.
{
m_GL - > glPointSize ( 6.0f ) ;
m_GL - > glBegin ( GL_POINTS ) ;
m_GL - > glColor4f ( 1.0f , 1.0f , 0.5f , 1.0f ) ;
m_GL - > glVertex2f ( m_DragHandlePos . x , m_DragHandlePos . y ) ;
m_GL - > glEnd ( ) ;
m_GL - > glPointSize ( 1.0f ) ; //Restore point size.
}
2016-01-04 19:50:15 -05:00
else if ( m_HoverType ! = eHoverType : : HoverNone & & m_HoverXform = = m_SelectedXform ) //Draw large turquoise dot on hover if they are hovering over the selected xform.
2014-07-26 15:03:51 -04:00
{
m_GL - > glPointSize ( 6.0f ) ;
m_GL - > glBegin ( GL_POINTS ) ;
m_GL - > glColor4f ( 0.5f , 1.0f , 1.0f , 1.0f ) ;
m_GL - > glVertex2f ( m_HoverHandlePos . x , m_HoverHandlePos . y ) ;
m_GL - > glEnd ( ) ;
m_GL - > glPointSize ( 1.0f ) ;
2014-07-08 03:11:14 -04:00
}
}
/// <summary>
/// Set drag modifiers based on key press.
/// </summary>
/// <param name="e">The event</param>
2014-12-11 00:50:15 -05:00
bool GLEmberControllerBase : : KeyPress_ ( QKeyEvent * e )
2014-07-08 03:11:14 -04:00
{
if ( e - > key ( ) = = Qt : : Key_Control )
{
2016-01-04 19:50:15 -05:00
SetControl ( ) ;
2014-07-08 03:11:14 -04:00
return true ;
}
return false ;
}
/// <summary>
2014-12-11 00:50:15 -05:00
/// Call controller KeyPress_().
2014-07-08 03:11:14 -04:00
/// </summary>
/// <param name="e">The event</param>
void GLWidget : : keyPressEvent ( QKeyEvent * e )
{
2014-12-11 00:50:15 -05:00
if ( ! GLController ( ) | | ! GLController ( ) - > KeyPress_ ( e ) )
2015-01-01 10:17:05 -05:00
QOpenGLWidget : : keyPressEvent ( e ) ;
2014-07-08 03:11:14 -04:00
update ( ) ;
}
/// <summary>
/// Set drag modifiers based on key release.
/// </summary>
/// <param name="e">The event</param>
2014-12-11 00:50:15 -05:00
bool GLEmberControllerBase : : KeyRelease_ ( QKeyEvent * e )
2014-07-08 03:11:14 -04:00
{
if ( e - > key ( ) = = Qt : : Key_Control )
{
2016-01-04 19:50:15 -05:00
ClearControl ( ) ;
2014-07-08 03:11:14 -04:00
return true ;
}
return false ;
}
/// <summary>
2014-12-11 00:50:15 -05:00
/// Call controller KeyRelease_().
2014-07-08 03:11:14 -04:00
/// </summary>
/// <param name="e">The event</param>
void GLWidget : : keyReleaseEvent ( QKeyEvent * e )
2016-01-04 19:50:15 -05:00
{
2014-12-11 00:50:15 -05:00
if ( ! GLController ( ) | | ! GLController ( ) - > KeyRelease_ ( e ) )
2015-01-01 10:17:05 -05:00
QOpenGLWidget : : keyReleaseEvent ( e ) ;
2014-07-08 03:11:14 -04:00
update ( ) ;
}
/// <summary>
/// Determine if the mouse click was over an affine circle
/// and set the appropriate selection information to be used
/// on subsequent mouse move events.
/// If nothing was selected, then reset the selection and drag states.
/// </summary>
/// <param name="e">The event</param>
template < typename T >
void GLEmberController < T > : : MousePress ( QMouseEvent * e )
{
2015-02-28 17:18:07 -05:00
v3T mouseFlipped ( e - > x ( ) * m_GL - > devicePixelRatio ( ) , m_Viewport [ 3 ] - e - > y ( ) * m_GL - > devicePixelRatio ( ) , 0 ) ; //Must flip y because in OpenGL, 0,0 is bottom left, but in windows, it's top left.
2016-02-02 20:51:58 -05:00
auto ember = m_FractoriumEmberController - > CurrentEmber ( ) ;
auto renderer = m_FractoriumEmberController - > Renderer ( ) ;
2014-07-08 03:11:14 -04:00
//Ensure everything has been initialized.
if ( ! renderer )
return ;
2015-02-28 17:18:07 -05:00
m_MouseDownPos = glm : : ivec2 ( e - > x ( ) * m_GL - > devicePixelRatio ( ) , e - > y ( ) * m_GL - > devicePixelRatio ( ) ) ; //Capture the raster coordinates of where the mouse was clicked.
2014-07-08 03:11:14 -04:00
m_MouseWorldPos = WindowToWorld ( mouseFlipped , false ) ; //Capture the world cartesian coordinates of where the mouse is.
m_BoundsDown . w = renderer - > LowerLeftX ( false ) ; //Need to capture these because they'll be changing if scaling.
m_BoundsDown . x = renderer - > LowerLeftY ( false ) ;
m_BoundsDown . y = renderer - > UpperRightX ( false ) ;
m_BoundsDown . z = renderer - > UpperRightY ( false ) ;
2016-02-12 00:38:21 -05:00
auto mod = e - > modifiers ( ) ;
2016-01-04 19:50:15 -05:00
2014-07-08 03:11:14 -04:00
if ( mod . testFlag ( Qt : : ShiftModifier ) )
2016-01-04 19:50:15 -05:00
SetShift ( ) ;
2016-05-02 19:54:56 -04:00
if ( mod . testFlag ( Qt : : AltModifier ) )
2016-01-04 19:50:15 -05:00
SetAlt ( ) ;
if ( m_DragState = = eDragState : : DragNone ) //Only take action if the user wasn't already dragging.
2014-07-08 03:11:14 -04:00
{
m_MouseDownWorldPos = m_MouseWorldPos ; //Set the mouse down position to the current position.
if ( e - > button ( ) & Qt : : LeftButton )
{
int xformIndex = UpdateHover ( mouseFlipped ) ; //Determine if an affine circle was clicked.
if ( m_HoverXform & & xformIndex ! = - 1 )
{
m_SelectedXform = m_HoverXform ;
2016-01-04 19:50:15 -05:00
m_DragSrcTransform = Affine2D < T > ( m_AffineType = = eAffineType : : AffinePre ? m_SelectedXform - > m_Affine : m_SelectedXform - > m_Post ) ; //Copy the affine of the xform that was selected.
2014-07-08 03:11:14 -04:00
//The user has selected an xform by clicking on it, so update the main GUI by selecting this xform in the combo box.
2016-02-02 20:51:58 -05:00
m_Fractorium - > CurrentXform ( xformIndex ) ; //Must do this first so UpdateXform() below properly grabs the current plus any selected.
2016-02-12 00:38:21 -05:00
m_DragSrcTransforms . clear ( ) ;
m_FractoriumEmberController - > UpdateXform ( [ & ] ( Xform < T > * xform )
{
m_DragSrcTransforms . push_back ( m_AffineType = = eAffineType : : AffinePre ? xform - > m_Affine : xform - > m_Post ) ;
} , eXformUpdate : : UPDATE_CURRENT_AND_SELECTED , false ) ; //Don't update renderer here.
2016-02-02 20:51:58 -05:00
m_DragHandlePos = m_HoverHandlePos ; //The location in local coordinates of the point selected on the spinner, x, y or center.
m_DragHandleOffset = m_DragHandlePos - m_MouseWorldPos ; //The distance in world coordinates from the point selected to the center of the spinner.
m_DragState = eDragState : : DragDragging ;
2014-07-28 01:25:38 -04:00
//Draw large yellow dot on select or drag.
m_GL - > glPointSize ( 6.0f ) ;
m_GL - > glBegin ( GL_POINTS ) ;
m_GL - > glColor4f ( 1.0f , 1.0f , 0.5f , 1.0f ) ;
m_GL - > glVertex2f ( m_DragHandlePos . x , m_DragHandlePos . y ) ;
m_GL - > glEnd ( ) ;
m_GL - > glPointSize ( 1.0f ) ; //Restore point size.
m_GL - > repaint ( ) ;
2014-07-08 03:11:14 -04:00
}
else //Nothing was selected.
{
2015-01-02 18:11:36 -05:00
//m_SelectedXform = nullptr;
2016-01-04 19:50:15 -05:00
m_DragState = eDragState : : DragNone ;
2014-07-08 03:11:14 -04:00
}
}
else if ( e - > button ( ) = = Qt : : MiddleButton ) //Middle button does whole image translation.
{
m_CenterDownX = ember - > m_CenterX ; //Capture where the center of the image is because this value will change when panning.
m_CenterDownY = ember - > m_CenterY ;
2016-01-04 19:50:15 -05:00
m_DragState = eDragState : : DragPanning ;
2014-07-08 03:11:14 -04:00
}
else if ( e - > button ( ) = = Qt : : RightButton ) //Right button does whole image rotation and scaling.
{
UpdateHover ( mouseFlipped ) ;
m_SelectedXform = m_HoverXform ;
m_CenterDownX = ember - > m_CenterX ; //Capture these because they will change when rotating and scaling.
m_CenterDownY = ember - > m_CenterY ;
m_RotationDown = ember - > m_Rotate ;
m_ScaleDown = ember - > m_PixelsPerUnit ;
2016-01-04 19:50:15 -05:00
m_DragState = eDragState : : DragRotateScale ;
2014-07-08 03:11:14 -04:00
}
}
}
/// <summary>
/// Call controller MousePress().
/// </summary>
/// <param name="e">The event</param>
void GLWidget : : mousePressEvent ( QMouseEvent * e )
{
setFocus ( ) ; //Must do this so that this window gets keyboard events.
2016-02-12 00:38:21 -05:00
if ( auto controller = GLController ( ) )
2014-07-08 03:11:14 -04:00
controller - > MousePress ( e ) ;
2015-01-01 10:17:05 -05:00
QOpenGLWidget : : mousePressEvent ( e ) ;
2014-07-08 03:11:14 -04:00
}
/// <summary>
/// Reset the selection and dragging state, but re-calculate the
/// hovering state because the mouse might still be over an affine circle.
/// </summary>
/// <param name="e">The event</param>
template < typename T >
void GLEmberController < T > : : MouseRelease ( QMouseEvent * e )
{
2015-02-28 17:18:07 -05:00
v3T mouseFlipped ( e - > x ( ) * m_GL - > devicePixelRatio ( ) , m_Viewport [ 3 ] - e - > y ( ) * m_GL - > devicePixelRatio ( ) , 0 ) ; //Must flip y because in OpenGL, 0,0 is bottom left, but in windows, it's top left.
2014-07-08 03:11:14 -04:00
m_MouseWorldPos = WindowToWorld ( mouseFlipped , false ) ;
2016-01-04 19:50:15 -05:00
if ( m_DragState = = eDragState : : DragDragging & & ( e - > button ( ) & Qt : : LeftButton ) )
UpdateHover ( mouseFlipped ) ;
2014-07-08 03:11:14 -04:00
2016-01-04 19:50:15 -05:00
m_DragState = eDragState : : DragNone ;
2014-07-08 03:11:14 -04:00
m_DragModifier = 0 ;
m_GL - > repaint ( ) ; //Force immediate redraw.
}
/// <summary>
/// Call controller MouseRelease().
/// </summary>
/// <param name="e">The event</param>
void GLWidget : : mouseReleaseEvent ( QMouseEvent * e )
{
setFocus ( ) ; //Must do this so that this window gets keyboard events.
2016-02-12 00:38:21 -05:00
if ( auto controller = GLController ( ) )
2014-07-08 03:11:14 -04:00
controller - > MouseRelease ( e ) ;
2015-01-01 10:17:05 -05:00
QOpenGLWidget : : mouseReleaseEvent ( e ) ;
2014-07-08 03:11:14 -04:00
}
/// <summary>
/// If dragging, update relevant values and reset entire rendering process.
/// If hovering, update display.
/// </summary>
/// <param name="e">The event</param>
template < typename T >
void GLEmberController < T > : : MouseMove ( QMouseEvent * e )
{
bool draw = true ;
2015-02-28 17:18:07 -05:00
glm : : ivec2 mouse ( e - > x ( ) * m_GL - > devicePixelRatio ( ) , e - > y ( ) * m_GL - > devicePixelRatio ( ) ) ;
v3T mouseFlipped ( e - > x ( ) * m_GL - > devicePixelRatio ( ) , m_Viewport [ 3 ] - e - > y ( ) * m_GL - > devicePixelRatio ( ) , 0 ) ; //Must flip y because in OpenGL, 0,0 is bottom left, but in windows, it's top left.
2016-02-02 20:51:58 -05:00
auto ember = m_FractoriumEmberController - > CurrentEmber ( ) ;
2016-01-04 19:50:15 -05:00
2014-07-08 03:11:14 -04:00
//First check to see if the mouse actually moved.
if ( mouse = = m_MousePos )
return ;
m_MousePos = mouse ;
m_MouseWorldPos = WindowToWorld ( mouseFlipped , false ) ;
//Update status bar on main window, regardless of whether anything is being dragged.
if ( m_Fractorium - > m_Controller - > RenderTimerRunning ( ) )
2015-02-28 17:18:07 -05:00
m_Fractorium - > SetCoordinateStatus ( e - > x ( ) * m_GL - > devicePixelRatio ( ) , e - > y ( ) * m_GL - > devicePixelRatio ( ) , m_MouseWorldPos . x , m_MouseWorldPos . y ) ;
2014-07-08 03:11:14 -04:00
2016-01-04 19:50:15 -05:00
if ( m_SelectedXform & & m_DragState = = eDragState : : DragDragging ) //Dragging and affine.
2014-07-08 03:11:14 -04:00
{
2016-01-04 19:50:15 -05:00
bool pre = m_AffineType = = eAffineType : : AffinePre ;
2014-07-08 03:11:14 -04:00
2016-01-04 19:50:15 -05:00
if ( m_HoverType = = eHoverType : : HoverTranslation )
2016-02-12 00:38:21 -05:00
CalcDragTranslation ( ) ;
2016-01-04 19:50:15 -05:00
else if ( m_HoverType = = eHoverType : : HoverXAxis )
2016-02-12 00:38:21 -05:00
CalcDragXAxis ( ) ;
2016-01-04 19:50:15 -05:00
else if ( m_HoverType = = eHoverType : : HoverYAxis )
2016-02-12 00:38:21 -05:00
CalcDragYAxis ( ) ;
2014-07-08 03:11:14 -04:00
m_FractoriumEmberController - > FillAffineWithXform ( m_SelectedXform , pre ) ; //Update the spinners in the affine tab of the main window.
m_FractoriumEmberController - > UpdateRender ( ) ; //Restart the rendering process.
}
2016-01-04 19:50:15 -05:00
else if ( m_DragState = = eDragState : : DragPanning ) //Translating the whole image.
2014-07-08 03:11:14 -04:00
{
T x = - ( m_MouseWorldPos . x - m_MouseDownWorldPos . x ) ;
T y = ( m_MouseWorldPos . y - m_MouseDownWorldPos . y ) ;
Affine2D < T > rotMat ;
rotMat . C ( m_CenterDownX ) ;
rotMat . F ( m_CenterDownY ) ;
2016-02-12 00:38:21 -05:00
rotMat . Rotate ( ember - > m_Rotate * DEG_2_RAD_T ) ;
2014-07-08 03:11:14 -04:00
v2T v1 ( x , y ) ;
v2T v2 = rotMat . TransformVector ( v1 ) ;
ember - > m_CenterX = v2 . x ;
2014-10-27 17:21:06 -04:00
ember - > m_CenterY = ember - > m_RotCenterY = v2 . y ;
2014-07-08 03:11:14 -04:00
m_FractoriumEmberController - > SetCenter ( ember - > m_CenterX , ember - > m_CenterY ) ; //Will restart the rendering process.
}
2016-01-04 19:50:15 -05:00
else if ( m_DragState = = eDragState : : DragRotateScale ) //Rotating and scaling the whole image.
2014-07-08 03:11:14 -04:00
{
T rot = CalcRotation ( ) ;
T scale = CalcScale ( ) ;
ember - > m_Rotate = NormalizeDeg180 < T > ( m_RotationDown + rot ) ;
m_Fractorium - > SetRotation ( ember - > m_Rotate , true ) ;
2016-02-12 00:38:21 -05:00
m_Fractorium - > SetScale ( m_ScaleDown + scale ) ; //Will restart the rendering process.
2014-07-08 03:11:14 -04:00
}
else
{
//If the user doesn't already have a key down, and they aren't dragging, clear the keys to be safe.
//This is done because if they do an alt+tab between windows, it thinks the alt key is down.
if ( e - > modifiers ( ) = = Qt : : NoModifier )
ClearDrag ( ) ;
//Check if they weren't dragging and weren't hovering over any affine.
//In that case, nothing needs to be done.
if ( UpdateHover ( mouseFlipped ) = = - 1 )
draw = false ;
}
2016-01-04 19:50:15 -05:00
2014-07-08 03:11:14 -04:00
//Only update if the user was dragging or hovered over a point.
//Use repaint() to update immediately for a more responsive feel.
2016-01-04 19:50:15 -05:00
if ( ( m_DragState ! = eDragState : : DragNone ) | | draw )
2014-07-08 03:11:14 -04:00
m_GL - > update ( ) ;
}
/// <summary>
/// Call controller MouseMove().
/// </summary>
/// <param name="e">The event</param>
void GLWidget : : mouseMoveEvent ( QMouseEvent * e )
{
setFocus ( ) ; //Must do this so that this window gets keyboard events.
2016-01-04 19:50:15 -05:00
2016-02-12 00:38:21 -05:00
if ( auto controller = GLController ( ) )
2014-07-08 03:11:14 -04:00
controller - > MouseMove ( e ) ;
2016-01-04 19:50:15 -05:00
2015-01-01 10:17:05 -05:00
QOpenGLWidget : : mouseMoveEvent ( e ) ;
2014-07-08 03:11:14 -04:00
}
/// <summary>
/// Mouse wheel changes the scale (pixels per unit) which
/// will zoom in the image in our out, while sacrificing quality.
/// If the user needs to preserve quality, they can use the zoom spinner
/// on the main window.
/// </summary>
/// <param name="e">The event</param>
template < typename T >
void GLEmberController < T > : : Wheel ( QWheelEvent * e )
{
2016-02-02 20:51:58 -05:00
auto ember = m_FractoriumEmberController - > CurrentEmber ( ) ;
2014-07-08 03:11:14 -04:00
if ( m_Fractorium & & ! ( e - > buttons ( ) & Qt : : MiddleButton ) ) //Middle button does whole image translation, so ignore the mouse wheel while panning to avoid inadvertent zooming.
m_Fractorium - > SetScale ( ember - > m_PixelsPerUnit + ( e - > angleDelta ( ) . y ( ) > = 0 ? 50 : - 50 ) ) ;
}
/// <summary>
/// Call controller Wheel().
/// </summary>
/// <param name="e">The event</param>
void GLWidget : : wheelEvent ( QWheelEvent * e )
{
2016-02-12 00:38:21 -05:00
if ( auto controller = GLController ( ) )
2014-07-08 03:11:14 -04:00
controller - > Wheel ( e ) ;
2016-01-04 19:50:15 -05:00
2015-01-01 10:17:05 -05:00
//Do not call QOpenGLWidget::wheelEvent(e) because this should only affect the scale and not the position of the scroll bars.
2014-07-08 03:11:14 -04:00
}
2014-10-18 15:56:37 -04:00
/// <summary>
/// Set the dimensions of the drawing area.
/// This will be called from the main window's SyncSizes() function.
/// </summary>
/// <param name="w">Width in pixels</param>
/// <param name="h">Height in pixels</param>
2014-10-14 11:53:15 -04:00
void GLWidget : : SetDimensions ( int w , int h )
{
setFixedSize ( w , h ) ;
2014-07-08 03:11:14 -04:00
}
/// <summary>
/// Set up texture memory to match the size of the window.
/// If first allocation, generate, bind and set parameters.
/// If subsequent call, only take action if dimensions don't match the window. In such case,
/// first deallocate, then reallocate.
/// </summary>
/// <returns>True if success, else false.</returns>
bool GLWidget : : Allocate ( bool force )
{
bool alloc = false ;
2014-12-11 00:50:15 -05:00
bool doResize = force | | m_TexWidth ! = width ( ) | | m_TexHeight ! = height ( ) ;
bool doIt = doResize | | m_OutputTexID = = 0 ;
2014-07-08 03:11:14 -04:00
2014-10-14 11:53:15 -04:00
if ( doIt )
2014-07-08 03:11:14 -04:00
{
m_TexWidth = width ( ) ;
m_TexHeight = height ( ) ;
glEnable ( GL_TEXTURE_2D ) ;
glTexEnvf ( GL_TEXTURE_ENV , GL_TEXTURE_ENV_MODE , GL_REPLACE ) ;
2014-10-14 11:53:15 -04:00
2014-12-11 00:50:15 -05:00
if ( doResize )
2014-10-14 11:53:15 -04:00
Deallocate ( ) ;
2014-07-08 03:11:14 -04:00
glGenTextures ( 1 , & m_OutputTexID ) ;
glBindTexture ( GL_TEXTURE_2D , m_OutputTexID ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_LINEAR ) ; //Fractron had this as GL_LINEAR_MIPMAP_LINEAR for OpenCL and Cuda.
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_LINEAR ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_S , GL_CLAMP ) ;
glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_T , GL_CLAMP ) ;
2015-01-02 18:11:36 -05:00
glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGBA , m_TexWidth , m_TexHeight , 0 , GL_RGBA , GL_UNSIGNED_BYTE , nullptr ) ;
2014-07-08 03:11:14 -04:00
alloc = true ;
}
2016-01-04 19:50:15 -05:00
2014-07-08 03:11:14 -04:00
if ( alloc )
{
glBindTexture ( GL_TEXTURE_2D , 0 ) ;
glDisable ( GL_TEXTURE_2D ) ;
}
return m_OutputTexID ! = 0 ;
}
/// <summary>
/// Deallocate texture memory.
/// </summary>
/// <returns>True if anything deleted, else false.</returns>
bool GLWidget : : Deallocate ( )
{
bool deleted = false ;
if ( m_OutputTexID ! = 0 )
{
2015-01-01 10:17:05 -05:00
glBindTexture ( GL_TEXTURE_2D , m_OutputTexID ) ;
2014-07-08 03:11:14 -04:00
glDeleteTextures ( 1 , & m_OutputTexID ) ;
m_OutputTexID = 0 ;
deleted = true ;
}
return deleted ;
}
/// <summary>
/// Set the viewport to match the window dimensions.
/// If the dimensions already match, no action is taken.
/// </summary>
void GLWidget : : SetViewport ( )
{
2014-10-14 11:53:15 -04:00
if ( m_Init & & ( m_ViewWidth ! = m_TexWidth | | m_ViewHeight ! = m_TexHeight ) )
2014-07-08 03:11:14 -04:00
{
2014-12-11 00:50:15 -05:00
glViewport ( 0 , 0 , GLint ( m_TexWidth ) , GLint ( m_TexHeight ) ) ;
2014-10-14 11:53:15 -04:00
m_ViewWidth = m_TexWidth ;
m_ViewHeight = m_TexHeight ;
2014-07-08 03:11:14 -04:00
}
}
/// <summary>
2014-10-14 11:53:15 -04:00
/// Determine whether the dimensions of the renderer's current ember match
/// the dimensions of the widget, texture and viewport.
/// Since this uses the renderer's dimensions, this
/// must be called after the renderer has set the current ember.
2014-07-08 03:11:14 -04:00
/// </summary>
/// <returns>True if all sizes match, else false.</returns>
template < typename T >
2014-10-14 11:53:15 -04:00
bool GLEmberController < T > : : SizesMatch ( )
2014-07-08 03:11:14 -04:00
{
2016-02-02 20:51:58 -05:00
auto ember = m_FractoriumEmberController - > CurrentEmber ( ) ;
2014-10-14 11:53:15 -04:00
return ( ember & &
2016-01-04 19:50:15 -05:00
ember - > m_FinalRasW = = m_GL - > width ( ) & &
ember - > m_FinalRasH = = m_GL - > height ( ) & &
m_GL - > width ( ) = = m_GL - > m_TexWidth & &
m_GL - > height ( ) = = m_GL - > m_TexHeight & &
m_GL - > m_TexWidth = = m_GL - > m_ViewWidth & &
m_GL - > m_TexHeight = = m_GL - > m_ViewHeight ) ;
2014-07-08 03:11:14 -04:00
}
/// <summary>
/// Draw the grid in response to the control key being pressed.
/// The frequency of the grid lines will change depending on the zoom.
/// Calculated with the frame always centered, the renderer just moves the camera.
/// </summary>
2016-02-02 20:51:58 -05:00
/// <param name="scale">A value to scale by, used when locking the affine scale</param>
void GLWidget : : DrawGrid ( double scale )
2014-07-08 03:11:14 -04:00
{
2016-02-12 00:38:21 -05:00
auto renderer = m_Fractorium - > m_Controller - > Renderer ( ) ;
2016-01-12 23:42:12 -05:00
float unitX = std : : abs ( renderer - > UpperRightX ( false ) - renderer - > LowerLeftX ( false ) ) / 2.0f ;
float unitY = std : : abs ( renderer - > UpperRightY ( false ) - renderer - > LowerLeftY ( false ) ) / 2.0f ;
2016-02-02 20:51:58 -05:00
float rad = std : : max ( unitX * scale , unitY * scale ) ;
2014-07-08 03:11:14 -04:00
float xLow = floor ( - unitX ) ;
float xHigh = ceil ( unitX ) ;
float yLow = floor ( - unitY ) ;
float yHigh = ceil ( unitY ) ;
glBegin ( GL_LINES ) ;
if ( rad < = 8.0f )
{
glColor4f ( 0.5f , 0.5f , 0.5f , 0.5f ) ;
2014-12-11 00:50:15 -05:00
for ( float fx = xLow ; fx < = xHigh ; fx + = GridStep )
2014-07-08 03:11:14 -04:00
{
2014-12-11 00:50:15 -05:00
glVertex2f ( fx , yLow ) ;
glVertex2f ( fx , yHigh ) ;
2014-07-08 03:11:14 -04:00
}
2014-12-11 00:50:15 -05:00
for ( float fy = yLow ; fy < yHigh ; fy + = GridStep )
2014-07-08 03:11:14 -04:00
{
2014-12-11 00:50:15 -05:00
glVertex2f ( xLow , fy ) ;
glVertex2f ( xHigh , fy ) ;
2014-07-08 03:11:14 -04:00
}
}
2016-02-02 20:51:58 -05:00
unitX * = scale ;
unitY * = scale ;
2014-07-08 03:11:14 -04:00
if ( unitX < = 64.0f )
{
glColor4f ( 0.5f , 0.5f , 0.5f , 1.0f ) ;
2014-12-11 00:50:15 -05:00
for ( float fx = xLow ; fx < = xHigh ; fx + = 1.0f )
2014-07-08 03:11:14 -04:00
{
2014-12-11 00:50:15 -05:00
glVertex2f ( fx , yLow ) ;
glVertex2f ( fx , yHigh ) ;
2014-07-08 03:11:14 -04:00
}
2014-12-11 00:50:15 -05:00
for ( float fy = yLow ; fy < yHigh ; fy + = 1.0f )
2014-07-08 03:11:14 -04:00
{
2014-12-11 00:50:15 -05:00
glVertex2f ( xLow , fy ) ;
glVertex2f ( xHigh , fy ) ;
2014-07-08 03:11:14 -04:00
}
}
glColor4f ( 1.0f , 0.0f , 0.0f , 1.0f ) ;
glVertex2f ( 0.0f , 0.0f ) ;
glVertex2f ( xHigh , 0.0f ) ;
glColor4f ( 0.5f , 0.0f , 0.0f , 1.0f ) ;
glVertex2f ( 0.0f , 0.0f ) ;
glVertex2f ( xLow , 0.0f ) ;
glColor4f ( 0.0f , 1.0f , 0.0f , 1.0f ) ;
glVertex2f ( 0.0f , 0.0f ) ;
glVertex2f ( 0.0f , yHigh ) ;
glColor4f ( 0.0f , 0.5f , 0.0f , 1.0f ) ;
glVertex2f ( 0.0f , 0.0f ) ;
glVertex2f ( 0.0f , yLow ) ;
glEnd ( ) ;
}
/// <summary>
/// Draw the unit square.
/// </summary>
void GLWidget : : DrawUnitSquare ( )
{
glLineWidth ( 1.0f ) ;
glBegin ( GL_LINES ) ;
glColor4f ( 1.0f , 1.0f , 1.0f , 0.25f ) ;
2016-01-04 19:50:15 -05:00
glVertex2f ( - 1 , - 1 ) ;
glVertex2f ( 1 , - 1 ) ;
2014-07-08 03:11:14 -04:00
glVertex2f ( - 1 , 1 ) ;
glVertex2f ( 1 , 1 ) ;
2016-01-04 19:50:15 -05:00
glVertex2f ( - 1 , - 1 ) ;
2014-07-08 03:11:14 -04:00
glVertex2f ( - 1 , 1 ) ;
2016-01-04 19:50:15 -05:00
glVertex2f ( 1 , - 1 ) ;
2014-07-08 03:11:14 -04:00
glVertex2f ( 1 , 1 ) ;
glColor4f ( 1.0f , 0.0f , 0.0f , 0.5f ) ;
glVertex2f ( - 1 , 0 ) ;
glVertex2f ( 1 , 0 ) ;
glColor4f ( 0.0f , 1.0f , 0.0f , 0.5f ) ;
2016-01-04 19:50:15 -05:00
glVertex2f ( 0 , - 1 ) ;
2014-07-08 03:11:14 -04:00
glVertex2f ( 0 , 1 ) ;
glEnd ( ) ;
}
/// <summary>
/// Draw the pre or post affine circle for the passed in xform.
/// For drawing affine transforms, multiply the identity model view matrix by the
/// affine for each xform, so that all points are considered to be "1".
/// </summary>
/// <param name="xform">A pointer to the xform whose affine will be drawn</param>
/// <param name="pre">True for pre affine, else false for post.</param>
/// <param name="selected">True if selected (draw enclosing circle), else false (only draw axes).</param>
template < typename T >
void GLEmberController < T > : : DrawAffine ( Xform < T > * xform , bool pre , bool selected )
{
2016-02-02 20:51:58 -05:00
auto ember = m_FractoriumEmberController - > CurrentEmber ( ) ;
2016-02-12 00:38:21 -05:00
auto final = ember - > IsFinalXform ( xform ) ;
auto index = ember - > GetXformIndex ( xform ) ;
auto size = ember - > m_Palette . m_Entries . size ( ) ;
auto color = ember - > m_Palette . m_Entries [ Clamp < T > ( xform - > m_ColorX * size , 0 , size - 1 ) ] ;
2016-05-24 10:02:05 -04:00
auto & affine = pre ? xform - > m_Affine : xform - > m_Post ;
2014-07-08 03:11:14 -04:00
//For some incredibly strange reason, even though glm and OpenGL use matrices with a column-major
//data layout, nothing will work here unless they are flipped to row major order. This is how it was
//done in Fractron.
2016-05-24 10:02:05 -04:00
m4T mat = ( affine * m_FractoriumEmberController - > AffineScaleCurrentToLocked ( ) ) . ToMat4RowMajor ( ) ;
2014-07-08 03:11:14 -04:00
m_GL - > glPushMatrix ( ) ;
m_GL - > glLoadIdentity ( ) ;
MultMatrix ( mat ) ;
m_GL - > glLineWidth ( 3.0f ) ; //One 3px wide, colored black, except green on x axis for post affine.
2014-07-26 15:03:51 -04:00
m_GL - > DrawAffineHelper ( index , selected , pre , final , true ) ;
2014-07-08 03:11:14 -04:00
m_GL - > glLineWidth ( 1.0f ) ; //Again 1px wide, colored white, to give a white middle with black outline effect.
2014-07-26 15:03:51 -04:00
m_GL - > DrawAffineHelper ( index , selected , pre , final , false ) ;
2014-07-08 03:11:14 -04:00
m_GL - > glPointSize ( 5.0f ) ; //Three black points, one in the center and two on the circle. Drawn big 5px first to give a black outline.
m_GL - > glBegin ( GL_POINTS ) ;
m_GL - > glColor4f ( 0.0f , 0.0f , 0.0f , selected ? 1.0f : 0.5f ) ;
m_GL - > glVertex2f ( 0.0f , 0.0f ) ;
m_GL - > glVertex2f ( 1.0f , 0.0f ) ;
m_GL - > glVertex2f ( 0.0f , 1.0f ) ;
m_GL - > glEnd ( ) ;
m_GL - > glLineWidth ( 2.0f ) ; //Draw lines again for y axis only, without drawing the circle, using the color of the selected xform.
m_GL - > glBegin ( GL_LINES ) ;
m_GL - > glColor4f ( color . r , color . g , color . b , 1.0f ) ;
m_GL - > glVertex2f ( 0.0f , 0.0f ) ;
m_GL - > glVertex2f ( 0.0f , 1.0f ) ;
m_GL - > glEnd ( ) ;
m_GL - > glPointSize ( 3.0f ) ; //Draw smaller white points, to give a black outline effect.
m_GL - > glBegin ( GL_POINTS ) ;
m_GL - > glColor4f ( 1.0f , 1.0f , 1.0f , selected ? 1.0f : 0.5f ) ;
m_GL - > glVertex2f ( 0.0f , 0.0f ) ;
m_GL - > glVertex2f ( 1.0f , 0.0f ) ;
m_GL - > glVertex2f ( 0.0f , 1.0f ) ;
m_GL - > glEnd ( ) ;
m_GL - > glPopMatrix ( ) ;
}
/// <summary>
/// Draw the axes, and optionally the surrounding circle
/// of an affine transform.
/// </summary>
2014-07-26 15:03:51 -04:00
/// <param name="index"></param>
2014-07-08 03:11:14 -04:00
/// <param name="selected">True if selected (draw enclosing circle), else false (only draw axes).</param>
2014-07-26 15:03:51 -04:00
/// <param name="pre"></param>
/// <param name="final"></param>
/// <param name="background"></param>
void GLWidget : : DrawAffineHelper ( int index , bool selected , bool pre , bool final , bool background )
2014-07-08 03:11:14 -04:00
{
float px = 1.0f ;
float py = 0.0f ;
2016-02-12 00:38:21 -05:00
auto col = final ? m_Fractorium - > m_FinalXformComboColor : m_Fractorium - > m_XformComboColors [ index % XFORM_COLOR_COUNT ] ;
2014-07-08 03:11:14 -04:00
glBegin ( GL_LINES ) ;
//Circle part.
if ( ! background )
{
2014-07-26 15:03:51 -04:00
glColor4f ( col . redF ( ) , col . greenF ( ) , col . blueF ( ) , 1.0f ) ; //Draw pre affine transform with white.
2014-07-08 03:11:14 -04:00
}
else
{
2014-07-26 15:03:51 -04:00
glColor4f ( 0.0f , 0.0f , 0.0f , 1.0f ) ; //Draw pre affine transform outline with white.
2014-07-08 03:11:14 -04:00
}
if ( selected )
{
--User changes
-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.
2015-09-12 21:33:45 -04:00
for ( size_t i = 1 ; i < = 64 ; i + + ) //The circle.
2014-07-08 03:11:14 -04:00
{
2014-12-11 00:50:15 -05:00
float theta = float ( M_PI ) * 2.0f * float ( i % 64 ) / 64.0f ;
2016-02-12 00:38:21 -05:00
float fx = std : : cos ( theta ) ;
float fy = std : : sin ( theta ) ;
2014-07-08 03:11:14 -04:00
glVertex2f ( px , py ) ;
2014-12-11 00:50:15 -05:00
glVertex2f ( fx , fy ) ;
px = fx ;
py = fy ;
2014-07-08 03:11:14 -04:00
}
}
//Lines from center to circle.
if ( ! background )
{
2014-07-26 15:03:51 -04:00
glColor4f ( col . redF ( ) , col . greenF ( ) , col . blueF ( ) , 1.0f ) ;
2014-07-08 03:11:14 -04:00
}
else
{
2014-07-26 15:03:51 -04:00
if ( pre )
glColor4f ( 0.0f , 0.0f , 0.0f , 1.0f ) ; //Draw pre affine transform outline with white.
else
glColor4f ( 0.0f , 0.75f , 0.0f , 1.0f ) ; //Draw post affine transform outline with green.
2014-07-08 03:11:14 -04:00
}
//The lines from the center to the circle.
glVertex2f ( 0.0f , 0.0f ) ; //X axis.
glVertex2f ( 1.0f , 0.0f ) ;
2016-01-04 19:50:15 -05:00
2014-07-08 03:11:14 -04:00
if ( background )
glColor4f ( 0.0f , 0.0f , 0.0f , 1.0f ) ;
glVertex2f ( 0.0f , 0.0f ) ; //Y axis.
glVertex2f ( 0.0f , 1.0f ) ;
glEnd ( ) ;
}
/// <summary>
/// Determine the index of the xform being hovered over if any.
/// Give precedence to the currently selected xform, if any.
/// </summary>
/// <param name="glCoords">The mouse raster coordinates to check</param>
/// <returns>The index of the xform being hovered over, else -1 if no hover.</returns>
template < typename T >
int GLEmberController < T > : : UpdateHover ( v3T & glCoords )
{
bool pre = m_Fractorium - > ui . PreAffineGroupBox - > isChecked ( ) ;
bool post = m_Fractorium - > ui . PostAffineGroupBox - > isChecked ( ) ;
bool preAll = pre & & m_Fractorium - > DrawAllPre ( ) ;
bool postAll = post & & m_Fractorium - > DrawAllPost ( ) ;
2016-02-13 20:24:51 -05:00
int bestIndex = - 1 ;
2014-07-08 03:11:14 -04:00
T bestDist = 10 ;
2016-02-02 20:51:58 -05:00
auto ember = m_FractoriumEmberController - > CurrentEmber ( ) ;
2016-01-04 19:50:15 -05:00
m_HoverType = eHoverType : : HoverNone ;
2014-07-08 03:11:14 -04:00
//If there's a selected/current xform, check it first so it gets precedence over the others.
2015-10-27 00:31:35 -04:00
if ( m_SelectedXform )
2014-07-08 03:11:14 -04:00
{
//These checks prevent highlighting the pre/post selected xform circle, when one is set to show all, and the other
//is set to show current, and the user hovers over another xform, but doesn't select it, then moves the mouse
//back over the hidden circle for the pre/post that was set to only show current.
bool checkSelPre = preAll | | ( pre & & m_HoverXform = = m_SelectedXform ) ;
bool checkSelPost = postAll | | ( post & & m_HoverXform = = m_SelectedXform ) ;
if ( CheckXformHover ( m_SelectedXform , glCoords , bestDist , checkSelPre , checkSelPost ) )
{
m_HoverXform = m_SelectedXform ;
2016-02-13 20:24:51 -05:00
bestIndex = int ( ember - > GetTotalXformIndex ( m_SelectedXform ) ) ;
2014-07-08 03:11:14 -04:00
}
}
//Check all xforms.
2016-02-13 20:24:51 -05:00
for ( int i = 0 ; i < int ( ember - > TotalXformCount ( ) ) ; i + + )
2014-07-08 03:11:14 -04:00
{
2016-02-02 20:51:58 -05:00
auto xform = ember - > GetTotalXform ( i ) ;
2014-07-08 03:11:14 -04:00
if ( preAll | | ( pre & & m_HoverXform = = xform ) ) //Only check pre affine if they are shown.
{
if ( CheckXformHover ( xform , glCoords , bestDist , true , false ) )
{
m_HoverXform = xform ;
bestIndex = i ;
}
}
if ( postAll | | ( post & & m_HoverXform = = xform ) ) //Only check post affine if they are shown.
{
if ( CheckXformHover ( xform , glCoords , bestDist , false , true ) )
{
m_HoverXform = xform ;
bestIndex = i ;
}
}
}
return bestIndex ;
}
/// <summary>
/// Determine the passed in xform's pre/post affine transforms are being hovered over.
/// Meant to be called in succession when checking all xforms for hover, and the best
/// hover distance is recorded in the bestDist reference parameter.
/// Mouse coordinates will be converted internally to world cartesian coordinates for checking.
/// </summary>
/// <param name="xform">A pointer to the xform to check for hover</param>
/// <param name="glCoords">The mouse raster coordinates to check</param>
/// <param name="bestDist">Reference to hold the best distance found so far</param>
/// <param name="pre">True to check pre affine, else don't.</param>
/// <param name="post">True to check post affine, else don't.</param>
/// <returns>True if hovering and the distance is smaller than the bestDist parameter</returns>
template < typename T >
bool GLEmberController < T > : : CheckXformHover ( Xform < T > * xform , v3T & glCoords , T & bestDist , bool pre , bool post )
{
bool preFound = false , postFound = false ;
2016-02-02 20:51:58 -05:00
T dist = 0 , scale = m_FractoriumEmberController - > AffineScaleCurrentToLocked ( ) ;
2014-07-08 03:11:14 -04:00
v3T pos ;
if ( pre )
{
2016-02-02 20:51:58 -05:00
auto affineScaled = xform - > m_Affine * scale ;
v3T translation ( affineScaled . C ( ) , affineScaled . F ( ) , 0 ) ;
2014-07-08 03:11:14 -04:00
v3T transScreen = glm : : project ( translation , m_Modelview , m_Projection , m_Viewport ) ;
2016-02-02 20:51:58 -05:00
v3T xAxis ( affineScaled . A ( ) , affineScaled . D ( ) , 0 ) ;
2014-07-08 03:11:14 -04:00
v3T xAxisScreen = glm : : project ( translation + xAxis , m_Modelview , m_Projection , m_Viewport ) ;
2016-02-02 20:51:58 -05:00
v3T yAxis ( affineScaled . B ( ) , affineScaled . E ( ) , 0 ) ;
2014-07-08 03:11:14 -04:00
v3T yAxisScreen = glm : : project ( translation + yAxis , m_Modelview , m_Projection , m_Viewport ) ;
pos = translation ;
dist = glm : : distance ( glCoords , transScreen ) ;
if ( dist < bestDist )
{
bestDist = dist ;
2016-01-04 19:50:15 -05:00
m_HoverType = eHoverType : : HoverTranslation ;
2014-07-08 03:11:14 -04:00
m_HoverHandlePos = pos ;
preFound = true ;
}
pos = translation + xAxis ;
dist = glm : : distance ( glCoords , xAxisScreen ) ;
2016-01-04 19:50:15 -05:00
2014-07-08 03:11:14 -04:00
if ( dist < bestDist )
{
bestDist = dist ;
2016-01-04 19:50:15 -05:00
m_HoverType = eHoverType : : HoverXAxis ;
2014-07-08 03:11:14 -04:00
m_HoverHandlePos = pos ;
preFound = true ;
}
pos = translation + yAxis ;
dist = glm : : distance ( glCoords , yAxisScreen ) ;
2016-01-04 19:50:15 -05:00
2014-07-08 03:11:14 -04:00
if ( dist < bestDist )
{
bestDist = dist ;
2016-01-04 19:50:15 -05:00
m_HoverType = eHoverType : : HoverYAxis ;
2014-07-08 03:11:14 -04:00
m_HoverHandlePos = pos ;
preFound = true ;
}
if ( preFound )
2016-01-04 19:50:15 -05:00
m_AffineType = eAffineType : : AffinePre ;
2014-07-08 03:11:14 -04:00
}
if ( post )
{
2016-02-02 20:51:58 -05:00
auto affineScaled = xform - > m_Post * scale ;
v3T translation ( affineScaled . C ( ) , affineScaled . F ( ) , 0 ) ;
2014-07-08 03:11:14 -04:00
v3T transScreen = glm : : project ( translation , m_Modelview , m_Projection , m_Viewport ) ;
2016-02-02 20:51:58 -05:00
v3T xAxis ( affineScaled . A ( ) , affineScaled . D ( ) , 0 ) ;
2014-07-08 03:11:14 -04:00
v3T xAxisScreen = glm : : project ( translation + xAxis , m_Modelview , m_Projection , m_Viewport ) ;
2016-02-02 20:51:58 -05:00
v3T yAxis ( affineScaled . B ( ) , affineScaled . E ( ) , 0 ) ;
2014-07-08 03:11:14 -04:00
v3T yAxisScreen = glm : : project ( translation + yAxis , m_Modelview , m_Projection , m_Viewport ) ;
pos = translation ;
dist = glm : : distance ( glCoords , transScreen ) ;
if ( dist < bestDist )
{
bestDist = dist ;
2016-01-04 19:50:15 -05:00
m_HoverType = eHoverType : : HoverTranslation ;
2014-07-08 03:11:14 -04:00
m_HoverHandlePos = pos ;
postFound = true ;
}
pos = translation + xAxis ;
dist = glm : : distance ( glCoords , xAxisScreen ) ;
2016-01-04 19:50:15 -05:00
2014-07-08 03:11:14 -04:00
if ( dist < bestDist )
{
bestDist = dist ;
2016-01-04 19:50:15 -05:00
m_HoverType = eHoverType : : HoverXAxis ;
2014-07-08 03:11:14 -04:00
m_HoverHandlePos = pos ;
postFound = true ;
}
pos = translation + yAxis ;
dist = glm : : distance ( glCoords , yAxisScreen ) ;
2016-01-04 19:50:15 -05:00
2014-07-08 03:11:14 -04:00
if ( dist < bestDist )
{
bestDist = dist ;
2016-01-04 19:50:15 -05:00
m_HoverType = eHoverType : : HoverYAxis ;
2014-07-08 03:11:14 -04:00
m_HoverHandlePos = pos ;
postFound = true ;
}
if ( postFound )
2016-01-04 19:50:15 -05:00
m_AffineType = eAffineType : : AffinePost ;
2014-07-08 03:11:14 -04:00
}
return preFound | | postFound ;
}
/// <summary>
/// Calculate the new affine transform when dragging with the x axis with the left mouse button.
/// The value returned will depend on whether any modifier keys were held down.
/// None: Rotate and scale only.
/// Local Pivot:
/// Shift: Rotate only about affine center.
/// Alt: Free transform.
/// Shift + Alt: Rotate single axis about affine center.
/// Control: Rotate and scale, snapping to grid.
/// Control + Shift: Rotate only, snapping to grid.
/// Control + Alt: Free transform, snapping to grid.
/// Control + Shift + Alt: Rotate single axis about affine center, snapping to grid.
/// World Pivot:
/// Shift + Alt: Rotate single axis about world center.
/// Control + Shift + Alt: Rotate single axis about world center, snapping to grid.
/// All others are the same as local pivot.
/// </summary>
/// <returns>The new affine transform to be assigned to the selected xform</returns>
template < typename T >
2016-02-12 00:38:21 -05:00
void GLEmberController < T > : : CalcDragXAxis ( )
2014-07-08 03:11:14 -04:00
{
2016-02-12 00:38:21 -05:00
size_t index = 0 ;
2016-02-02 20:51:58 -05:00
auto scale = m_FractoriumEmberController - > AffineScaleLockedToCurrent ( ) ;
2016-02-12 00:38:21 -05:00
auto scaleBack = m_FractoriumEmberController - > AffineScaleCurrentToLocked ( ) ;
bool pre = m_AffineType = = eAffineType : : AffinePre ;
2016-01-04 19:50:15 -05:00
bool worldPivotShiftAlt = ! m_Fractorium - > LocalPivot ( ) & & GetShift ( ) & & GetAlt ( ) ;
2014-07-08 03:11:14 -04:00
2016-01-04 19:50:15 -05:00
if ( GetShift ( ) )
2014-07-08 03:11:14 -04:00
{
2016-02-13 20:24:51 -05:00
auto posOffset = m_MouseWorldPos + m_DragHandleOffset ;
v3T snapped = GetControl ( ) ? SnapToNormalizedAngle ( posOffset , 24u ) : posOffset ;
2016-02-12 00:38:21 -05:00
auto startDiff = ( v2T ( m_MouseDownWorldPos ) * scale ) - m_DragSrcTransform . O ( ) ;
auto endDiff = ( v2T ( snapped ) * scale ) - m_DragSrcTransform . O ( ) ;
T startAngle = std : : atan2 ( startDiff . y , startDiff . x ) ;
T endAngle = std : : atan2 ( endDiff . y , endDiff . x ) ;
T angle = startAngle - endAngle ;
m_FractoriumEmberController - > UpdateXform ( [ & ] ( Xform < T > * xform )
{
2016-05-24 10:02:05 -04:00
auto & affine = pre ? xform - > m_Affine : xform - > m_Post ;
2016-02-12 00:38:21 -05:00
auto srcRotated = m_DragSrcTransforms [ index + + ] ;
2014-07-08 03:11:14 -04:00
2016-02-12 00:38:21 -05:00
if ( worldPivotShiftAlt )
{
srcRotated . X ( srcRotated . O ( ) + srcRotated . X ( ) ) ;
srcRotated . O ( v2T ( 0 ) ) ;
srcRotated . Rotate ( angle ) ;
2016-05-24 10:02:05 -04:00
affine . X ( srcRotated . X ( ) - affine . O ( ) ) ;
2016-02-12 00:38:21 -05:00
}
else if ( GetAlt ( ) )
{
srcRotated . Rotate ( angle ) ;
2016-05-24 10:02:05 -04:00
affine . X ( srcRotated . X ( ) ) ;
2016-02-12 00:38:21 -05:00
}
else
{
srcRotated . Rotate ( angle ) ;
2016-05-24 10:02:05 -04:00
affine = srcRotated ;
2016-02-12 00:38:21 -05:00
}
2016-01-04 19:50:15 -05:00
2016-02-12 00:38:21 -05:00
if ( xform = = m_FractoriumEmberController - > CurrentXform ( ) )
2016-05-24 10:02:05 -04:00
m_DragHandlePos = v3T ( ( affine . O ( ) + affine . X ( ) ) * scaleBack , 0 ) ;
2016-02-12 00:38:21 -05:00
} , eXformUpdate : : UPDATE_CURRENT_AND_SELECTED , false ) ; //Calling code will update renderer.
2014-07-08 03:11:14 -04:00
}
else
{
2016-02-12 00:38:21 -05:00
v3T diff ;
2016-02-13 20:24:51 -05:00
auto posOffset = m_MouseWorldPos + m_DragHandleOffset ;
2016-02-12 00:38:21 -05:00
2016-01-04 19:50:15 -05:00
if ( GetControl ( ) )
2016-02-13 20:24:51 -05:00
diff = SnapToGrid ( posOffset ) - m_MouseDownWorldPos ;
2014-07-08 03:11:14 -04:00
else
2016-02-13 20:24:51 -05:00
diff = posOffset - m_MouseDownWorldPos ;
2014-07-08 03:11:14 -04:00
2016-02-12 00:38:21 -05:00
auto origXPlusOff = v3T ( m_DragSrcTransform . X ( ) , 0 ) + ( diff * scale ) ;
m_FractoriumEmberController - > UpdateXform ( [ & ] ( Xform < T > * xform )
{
2016-05-24 10:02:05 -04:00
auto & affine = pre ? xform - > m_Affine : xform - > m_Post ;
2016-02-12 00:38:21 -05:00
auto axis = v3T ( m_DragSrcTransforms [ index + + ] . X ( ) , 0 ) + ( diff * scale ) ;
2014-07-08 03:11:14 -04:00
2016-02-12 00:38:21 -05:00
if ( GetAlt ( ) )
2016-05-24 10:02:05 -04:00
affine . X ( v2T ( origXPlusOff ) ) ; //Absolute, not ratio.
2016-02-12 00:38:21 -05:00
else
2016-05-24 10:02:05 -04:00
affine . RotateScaleXTo ( v2T ( axis ) ) ;
2014-07-08 03:11:14 -04:00
2016-02-12 00:38:21 -05:00
if ( xform = = m_FractoriumEmberController - > CurrentXform ( ) )
2016-05-24 10:02:05 -04:00
m_DragHandlePos = v3T ( ( affine . O ( ) + affine . X ( ) ) * scaleBack , 0 ) ;
2016-02-12 00:38:21 -05:00
} , eXformUpdate : : UPDATE_CURRENT_AND_SELECTED , false ) ;
}
2014-07-08 03:11:14 -04:00
}
/// <summary>
/// Calculate the new affine transform when dragging with the y axis with the left mouse button.
/// The value returned will depend on whether any modifier keys were held down.
/// None: Rotate and scale only.
/// Local Pivot:
/// Shift: Rotate only about affine center.
/// Alt: Free transform.
/// Shift + Alt: Rotate single axis about affine center.
/// Control: Rotate and scale, snapping to grid.
/// Control + Shift: Rotate only, snapping to grid.
/// Control + Alt: Free transform, snapping to grid.
/// Control + Shift + Alt: Rotate single axis about affine center, snapping to grid.
/// World Pivot:
/// Shift + Alt: Rotate single axis about world center.
/// Control + Shift + Alt: Rotate single axis about world center, snapping to grid.
/// All others are the same as local pivot.
/// </summary>
/// <returns>The new affine transform to be assigned to the selected xform</returns>
template < typename T >
2016-02-12 00:38:21 -05:00
void GLEmberController < T > : : CalcDragYAxis ( )
2014-07-08 03:11:14 -04:00
{
2016-02-12 00:38:21 -05:00
size_t index = 0 ;
2016-02-02 20:51:58 -05:00
auto scale = m_FractoriumEmberController - > AffineScaleLockedToCurrent ( ) ;
2016-02-12 00:38:21 -05:00
auto scaleBack = m_FractoriumEmberController - > AffineScaleCurrentToLocked ( ) ;
bool pre = m_AffineType = = eAffineType : : AffinePre ;
2016-01-04 19:50:15 -05:00
bool worldPivotShiftAlt = ! m_Fractorium - > LocalPivot ( ) & & GetShift ( ) & & GetAlt ( ) ;
2014-07-08 03:11:14 -04:00
2016-01-04 19:50:15 -05:00
if ( GetShift ( ) )
2014-07-08 03:11:14 -04:00
{
2016-02-13 20:24:51 -05:00
auto posOffset = m_MouseWorldPos + m_DragHandleOffset ;
v3T snapped = GetControl ( ) ? SnapToNormalizedAngle ( posOffset , 24u ) : posOffset ;
2016-02-12 00:38:21 -05:00
auto startDiff = ( v2T ( m_MouseDownWorldPos ) * scale ) - m_DragSrcTransform . O ( ) ;
auto endDiff = ( v2T ( snapped ) * scale ) - m_DragSrcTransform . O ( ) ;
T startAngle = std : : atan2 ( startDiff . y , startDiff . x ) ;
T endAngle = std : : atan2 ( endDiff . y , endDiff . x ) ;
T angle = startAngle - endAngle ;
m_FractoriumEmberController - > UpdateXform ( [ & ] ( Xform < T > * xform )
{
2016-05-24 10:02:05 -04:00
auto & affine = pre ? xform - > m_Affine : xform - > m_Post ;
2016-02-12 00:38:21 -05:00
auto srcRotated = m_DragSrcTransforms [ index + + ] ;
2014-07-08 03:11:14 -04:00
2016-02-12 00:38:21 -05:00
if ( worldPivotShiftAlt )
{
srcRotated . Y ( srcRotated . O ( ) + srcRotated . Y ( ) ) ;
srcRotated . O ( v2T ( 0 ) ) ;
srcRotated . Rotate ( angle ) ;
2016-05-24 10:02:05 -04:00
affine . Y ( srcRotated . Y ( ) - affine . O ( ) ) ;
2016-02-12 00:38:21 -05:00
}
else if ( GetAlt ( ) )
{
srcRotated . Rotate ( angle ) ;
2016-05-24 10:02:05 -04:00
affine . Y ( srcRotated . Y ( ) ) ;
2016-02-12 00:38:21 -05:00
}
else
{
srcRotated . Rotate ( angle ) ;
2016-05-24 10:02:05 -04:00
affine = srcRotated ;
2016-02-12 00:38:21 -05:00
}
2014-07-08 03:11:14 -04:00
2016-02-12 00:38:21 -05:00
if ( xform = = m_FractoriumEmberController - > CurrentXform ( ) )
2016-05-24 10:02:05 -04:00
m_DragHandlePos = v3T ( ( affine . O ( ) + affine . Y ( ) ) * scaleBack , 0 ) ;
2016-02-12 00:38:21 -05:00
} , eXformUpdate : : UPDATE_CURRENT_AND_SELECTED , false ) ; //Calling code will update renderer.
2014-07-08 03:11:14 -04:00
}
else
{
2016-02-12 00:38:21 -05:00
v3T diff ;
2016-02-13 20:24:51 -05:00
auto posOffset = m_MouseWorldPos + m_DragHandleOffset ;
2016-02-12 00:38:21 -05:00
2016-01-04 19:50:15 -05:00
if ( GetControl ( ) )
2016-02-13 20:24:51 -05:00
diff = SnapToGrid ( posOffset ) - m_MouseDownWorldPos ;
2014-07-08 03:11:14 -04:00
else
2016-02-13 20:24:51 -05:00
diff = posOffset - m_MouseDownWorldPos ;
2014-07-08 03:11:14 -04:00
2016-02-12 00:38:21 -05:00
auto origXPlusOff = v3T ( m_DragSrcTransform . Y ( ) , 0 ) + ( diff * scale ) ;
m_FractoriumEmberController - > UpdateXform ( [ & ] ( Xform < T > * xform )
{
2016-05-24 10:02:05 -04:00
auto & affine = pre ? xform - > m_Affine : xform - > m_Post ;
2016-02-12 00:38:21 -05:00
auto axis = v3T ( m_DragSrcTransforms [ index + + ] . Y ( ) , 0 ) + ( diff * scale ) ;
2014-07-08 03:11:14 -04:00
2016-02-12 00:38:21 -05:00
if ( GetAlt ( ) )
2016-05-24 10:02:05 -04:00
affine . Y ( v2T ( origXPlusOff ) ) ; //Absolute, not ratio.
2016-02-12 00:38:21 -05:00
else
2016-05-24 10:02:05 -04:00
affine . RotateScaleYTo ( v2T ( axis ) ) ;
2014-07-08 03:11:14 -04:00
2016-02-12 00:38:21 -05:00
if ( xform = = m_FractoriumEmberController - > CurrentXform ( ) )
2016-05-24 10:02:05 -04:00
m_DragHandlePos = v3T ( ( affine . O ( ) + affine . Y ( ) ) * scaleBack , 0 ) ;
2016-02-12 00:38:21 -05:00
} , eXformUpdate : : UPDATE_CURRENT_AND_SELECTED , false ) ;
}
2014-07-08 03:11:14 -04:00
}
/// <summary>
/// Calculate the new affine transform when dragging the center with the left mouse button.
/// The value returned will depend on whether any modifier keys were held down.
/// None: Free transform.
/// Local Pivot:
/// Shift: Rotate about world center, keeping orientation the same.
/// Control: Free transform, snapping to grid.
/// Control + Shift: Rotate about world center, keeping orientation the same, snapping to grid.
/// World Pivot:
/// Shift: Rotate about world center, rotating orientation.
/// Control + Shift: Rotate about world center, rotating orientation, snapping to grid.
/// All others are the same as local pivot.
/// </summary>
template < typename T >
2016-02-12 00:38:21 -05:00
void GLEmberController < T > : : CalcDragTranslation ( )
2014-07-08 03:11:14 -04:00
{
2016-02-12 00:38:21 -05:00
size_t index = 0 ;
2016-02-02 20:51:58 -05:00
auto scale = m_FractoriumEmberController - > AffineScaleLockedToCurrent ( ) ;
2016-02-12 00:38:21 -05:00
auto scaleBack = m_FractoriumEmberController - > AffineScaleCurrentToLocked ( ) ;
2016-01-04 19:50:15 -05:00
bool worldPivotShift = ! m_Fractorium - > LocalPivot ( ) & & GetShift ( ) ;
2016-02-12 00:38:21 -05:00
bool pre = m_AffineType = = eAffineType : : AffinePre ;
2014-07-08 03:11:14 -04:00
2016-01-04 19:50:15 -05:00
if ( GetShift ( ) )
2014-07-08 03:11:14 -04:00
{
2016-02-12 00:38:21 -05:00
v3T snapped = GetControl ( ) ? SnapToNormalizedAngle ( m_MouseWorldPos , 24 ) : m_MouseWorldPos ;
T startAngle = std : : atan2 ( m_DragSrcTransform . O ( ) . y , m_DragSrcTransform . O ( ) . x ) ;
T endAngle = std : : atan2 ( snapped . y , snapped . x ) ;
T angle = startAngle - endAngle ;
m_FractoriumEmberController - > UpdateXform ( [ & ] ( Xform < T > * xform )
{
2016-05-24 10:02:05 -04:00
auto & affine = pre ? xform - > m_Affine : xform - > m_Post ;
2016-02-12 00:38:21 -05:00
auto srcRotated = m_DragSrcTransforms [ index + + ] ;
srcRotated . RotateTrans ( angle ) ;
2016-01-04 19:50:15 -05:00
2016-02-12 00:38:21 -05:00
if ( worldPivotShift )
{
srcRotated . Rotate ( angle ) ;
2016-05-24 10:02:05 -04:00
affine . X ( srcRotated . X ( ) ) ;
affine . Y ( srcRotated . Y ( ) ) ;
2016-02-12 00:38:21 -05:00
}
2016-01-04 19:50:15 -05:00
2016-05-24 10:02:05 -04:00
affine . O ( srcRotated . O ( ) ) ;
2014-07-08 03:11:14 -04:00
2016-02-12 00:38:21 -05:00
if ( xform = = m_FractoriumEmberController - > CurrentXform ( ) )
m_DragHandlePos = v3T ( srcRotated . O ( ) , 0 ) * scaleBack ;
} , eXformUpdate : : UPDATE_CURRENT_AND_SELECTED , false ) ; //Calling code will update renderer.
2014-07-08 03:11:14 -04:00
}
else
{
2016-02-12 00:38:21 -05:00
auto diff = m_MouseWorldPos - m_MouseDownWorldPos ;
2016-01-04 19:50:15 -05:00
if ( GetControl ( ) )
2016-02-02 20:51:58 -05:00
{
2016-02-12 00:38:21 -05:00
m_FractoriumEmberController - > UpdateXform ( [ & ] ( Xform < T > * xform )
{
2016-05-24 10:02:05 -04:00
auto & affine = pre ? xform - > m_Affine : xform - > m_Post ;
2016-02-12 00:38:21 -05:00
auto offset = m_DragSrcTransforms [ index + + ] . O ( ) + ( scale * v2T ( diff ) ) ;
auto snapped = SnapToGrid ( offset ) ;
2016-05-24 10:02:05 -04:00
affine . O ( v2T ( snapped . x , snapped . y ) ) ;
2016-02-12 00:38:21 -05:00
} , eXformUpdate : : UPDATE_CURRENT_AND_SELECTED , false ) ;
m_DragHandlePos = SnapToGrid ( m_MouseWorldPos ) ;
2016-02-02 20:51:58 -05:00
}
2014-07-08 03:11:14 -04:00
else
2016-02-02 20:51:58 -05:00
{
2016-02-12 00:38:21 -05:00
m_FractoriumEmberController - > UpdateXform ( [ & ] ( Xform < T > * xform )
{
2016-05-24 10:02:05 -04:00
auto & affine = pre ? xform - > m_Affine : xform - > m_Post ;
affine . O ( m_DragSrcTransforms [ index + + ] . O ( ) + ( scale * v2T ( diff ) ) ) ;
2016-02-12 00:38:21 -05:00
} , eXformUpdate : : UPDATE_CURRENT_AND_SELECTED , false ) ;
m_DragHandlePos = m_MouseWorldPos ;
2016-02-02 20:51:58 -05:00
}
2014-07-08 03:11:14 -04:00
}
}
/// <summary>
/// Thin wrapper to check if all controllers are ok and return a pointer to the GLController.
/// </summary>
/// <returns>A pointer to the GLController if everything is ok, else false.</returns>
GLEmberControllerBase * GLWidget : : GLController ( )
{
if ( m_Fractorium & & m_Fractorium - > ControllersOk ( ) )
return m_Fractorium - > m_Controller - > GLController ( ) ;
2015-01-02 18:11:36 -05:00
return nullptr ;
2014-07-08 03:11:14 -04:00
}
2014-12-11 00:50:15 -05:00
template class GLEmberController < float > ;
# ifdef DO_DOUBLE
2016-01-04 19:50:15 -05:00
template class GLEmberController < double > ;
2014-12-11 00:50:15 -05:00
# endif