2023-04-25 19:59:54 -04:00
# include "FractoriumPch.h"
# include "GLWidget.h"
# include "Fractorium.h"
# ifdef USE_GLSL
static const char * vertexShaderSource =
" attribute vec4 posattr; \n "
" uniform mat4 matrix; \n "
" uniform float ps; \n "
" void main() { \n "
" gl_Position = matrix * posattr; \n "
" gl_PointSize = ps; \n "
" } \n " ;
static const char * fragmentShaderSource =
" uniform vec4 mycolor; \n "
" void main() { \n "
" gl_FragColor = mycolor; \n "
" } \n " ;
static const char * quadVertexShaderSource =
" attribute vec4 posattr; \n "
" uniform mat4 matrix; \n "
" varying vec4 texcoord; \n "
" void main() { \n "
" gl_Position = matrix * posattr; \n "
" texcoord = posattr; \n "
" } \n " ;
static const char * quadFragmentShaderSource =
" uniform sampler2D quadtex; \n "
" varying vec4 texcoord; \n "
" void main() { \n "
" gl_FragColor = texture2D(quadtex, texcoord.st); \n "
" } \n " ;
# endif
/// <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>
/// <param name="p">The parent widget</param>
GLWidget : : GLWidget ( QWidget * p )
: QOpenGLWidget ( p )
{
/*
auto qsf = this - > format ( ) ;
qDebug ( ) < < " Version: " < < qsf . majorVersion ( ) < < ' , ' < < qsf . minorVersion ( ) ;
qDebug ( ) < < " Profile: " < < qsf . profile ( ) ;
qDebug ( ) < < " Depth buffer size: " < < qsf . depthBufferSize ( ) ;
qDebug ( ) < < " Swap behavior: " < < qsf . swapBehavior ( ) ;
qDebug ( ) < < " Swap interval: " < < qsf . swapInterval ( ) ;
//QSurfaceFormat qsf;
//QSurfaceFormat::FormatOptions fo;
//fo.
//qsf.setDepthBufferSize(24);
//qsf.setSwapInterval(1);//Vsync.
//qsf.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
# ifndef USE_GLSL
qsf . setVersion ( 2 , 0 ) ;
qsf . setProfile ( QSurfaceFormat : : CompatibilityProfile ) ;
# else
qsf . setVersion ( 3 , 3 ) ;
//qsf.setProfile(QSurfaceFormat::CoreProfile);
# endif
setFormat ( qsf ) ;
*/
/*
QSurfaceFormat fmt ;
fmt . setDepthBufferSize ( 24 ) ;
// Request OpenGL 3.3 compatibility or OpenGL ES 3.0.
if ( QOpenGLContext : : openGLModuleType ( ) = = QOpenGLContext : : LibGL )
{
qDebug ( " Requesting 3.3 compatibility context " ) ;
fmt . setVersion ( 3 , 3 ) ;
fmt . setProfile ( QSurfaceFormat : : CoreProfile ) ;
}
else
{
qDebug ( " Requesting 3.0 context " ) ;
fmt . setVersion ( 3 , 0 ) ;
}
setFormat ( fmt ) ;
*/
//auto qsf = this->format();
//qDebug() << "Constructor*****************\nVersion: " << qsf.majorVersion() << ',' << qsf.minorVersion();
//qDebug() << "Profile: " << qsf.profile();
//qDebug() << "Depth buffer size: " << qsf.depthBufferSize();
//qDebug() << "Swap behavior: " << qsf.swapBehavior();
//qDebug() << "Swap interval: " << qsf.swapInterval();
}
/// <summary>
/// Empty destructor.
/// </summary>
GLWidget : : ~ GLWidget ( )
{
}
/// <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 )
{
//auto qsf = this->format();
//qDebug() << "InitGL*****************\nVersion: " << qsf.majorVersion() << ',' << qsf.minorVersion();
//qDebug() << "Profile: " << qsf.profile();
//qDebug() << "Depth buffer size: " << qsf.depthBufferSize();
//qDebug() << "Swap behavior: " << qsf.swapBehavior();
//qDebug() << "Swap interval: " << qsf.swapInterval();
const auto w = std : : ceil ( m_Fractorium - > ui . GLParentScrollArea - > width ( ) * devicePixelRatioF ( ) ) ;
const auto h = std : : ceil ( m_Fractorium - > ui . GLParentScrollArea - > height ( ) * devicePixelRatioF ( ) ) ;
SetDimensions ( w , h ) ;
//Start with either a flock of random embers, or the last flame from the previous run.
//Can't do this until now because the window wasn't maximized yet, so the sizes would have been off.
bool b = m_Fractorium - > m_Settings - > LoadLast ( ) ;
if ( b )
{
auto path = GetDefaultUserPath ( ) ;
const QDir dir ( path ) ;
const QString filename = path + " /lastonshutdown.flame " ;
if ( dir . exists ( filename ) )
{
QStringList ql ;
ql < < filename ;
m_Fractorium - > m_Controller - > OpenAndPrepFiles ( ql , false ) ;
}
else
b = false ;
}
if ( ! b )
{
m_Fractorium - > m_WidthSpin - > setValue ( w ) ;
m_Fractorium - > m_HeightSpin - > setValue ( h ) ;
m_Fractorium - > OnActionNewFlock ( false ) ; //This must come after the previous two lines because it uses the values of the spinners.
}
m_Fractorium - > m_Controller - > DelayedStartRenderTimer ( ) ;
m_Init = true ;
/*
auto clinfo = OpenCLInfo : : DefInstance ( ) ;
auto & platforms = clinfo - > Platforms ( ) ;
auto & alldevices = clinfo - > Devices ( ) ;
std : : vector < std : : string > strs ;
auto cdc = wglGetCurrentDC ( ) ;
auto cc = wglGetCurrentContext ( ) ;
ostringstream os ;
strs . push_back ( os . str ( ) ) ; os . str ( " " ) ; os < < " GLWidget::InitGL(): " ;
strs . push_back ( os . str ( ) ) ; os . str ( " " ) ; os < < " \n Current DC: " < < cdc ;
strs . push_back ( os . str ( ) ) ; os . str ( " " ) ; os < < " \n Current Context: " < < cc ;
for ( int platform = 0 ; platform < platforms . size ( ) ; platform + + )
{
cl_context_properties props [ ] =
{
CL_GL_CONTEXT_KHR , ( cl_context_properties ) wglGetCurrentContext ( ) ,
CL_WGL_HDC_KHR , ( cl_context_properties ) wglGetCurrentDC ( ) ,
CL_CONTEXT_PLATFORM , reinterpret_cast < cl_context_properties > ( ( platforms [ platform ] ) ( ) ) ,
0
} ;
// Find CL capable devices in the current GL context
//wglMakeCurrent(wglGetCurrentDC(), wglGetCurrentContext());
: : wglMakeCurrent ( wglGetCurrentDC ( ) , wglGetCurrentContext ( ) ) ;
size_t sizedev ;
cl_device_id devices [ 32 ] ;
clGetGLContextInfoKHR_fn clGetGLContextInfo = ( clGetGLContextInfoKHR_fn ) clGetExtensionFunctionAddressForPlatform ( platforms [ platform ] ( ) , " clGetGLContextInfoKHR " ) ;
clGetGLContextInfo ( props , CL_DEVICES_FOR_GL_CONTEXT_KHR , 32 * sizeof ( cl_device_id ) , devices , & sizedev ) ;
sizedev = ( cl_uint ) ( sizedev / sizeof ( cl_device_id ) ) ;
for ( int i = 0 ; i < sizedev ; i + + )
{
std : : string s ;
size_t pi , di ;
auto dd = clinfo - > DeviceFromId ( devices [ i ] , pi , di ) ;
if ( dd )
{
auto & dev = * dd ;
auto & plat = platforms [ pi ] ;
strs . push_back ( os . str ( ) ) ; os . str ( " " ) ; os < < " \n Platform[ " < < pi < < " ], device[ " < < di < < " ] is GL capable. " ;
strs . push_back ( os . str ( ) ) ; os . str ( " " ) ; os < < " \n Platform profile: " < < plat . getInfo < CL_PLATFORM_PROFILE > ( nullptr ) . c_str ( ) < < endl ;
strs . push_back ( os . str ( ) ) ; os . str ( " " ) ; os < < " \n Platform version: " < < plat . getInfo < CL_PLATFORM_VERSION > ( nullptr ) . c_str ( ) < < endl ;
strs . push_back ( os . str ( ) ) ; os . str ( " " ) ; os < < " \n Platform name: " < < plat . getInfo < CL_PLATFORM_NAME > ( nullptr ) . c_str ( ) < < endl ;
strs . push_back ( os . str ( ) ) ; os . str ( " " ) ; os < < " \n Platform vendor: " < < plat . getInfo < CL_PLATFORM_VENDOR > ( nullptr ) . c_str ( ) < < endl ;
strs . push_back ( os . str ( ) ) ; os . str ( " " ) ; os < < " \n Platform extensions: " < < plat . getInfo < CL_PLATFORM_EXTENSIONS > ( nullptr ) . c_str ( ) < < endl ;
strs . push_back ( os . str ( ) ) ; os . str ( " " ) ; os < < " \n Vendor: " < < dev . getInfo < CL_DEVICE_VENDOR > ( nullptr ) . c_str ( ) < < endl ;
strs . push_back ( os . str ( ) ) ; os . str ( " " ) ; os < < " \n Device: " < < dev . getInfo < CL_DEVICE_NAME > ( nullptr ) . c_str ( ) < < endl ;
strs . push_back ( os . str ( ) ) ; os . str ( " " ) ; os < < " \n Driver version: " < < dev . getInfo < CL_DRIVER_VERSION > ( nullptr ) . c_str ( ) < < endl ;
strs . push_back ( os . str ( ) ) ; os . str ( " " ) ; os < < " \n Device profile: " < < dev . getInfo < CL_DEVICE_PROFILE > ( nullptr ) . c_str ( ) < < endl ;
strs . push_back ( os . str ( ) ) ; os . str ( " " ) ; os < < " \n Device version: " < < dev . getInfo < CL_DEVICE_VERSION > ( nullptr ) . c_str ( ) < < endl ;
strs . push_back ( os . str ( ) ) ; os . str ( " " ) ; os < < " \n Device extensions: " < < dev . getInfo < CL_DEVICE_EXTENSIONS > ( nullptr ) . c_str ( ) < < endl ;
strs . push_back ( os . str ( ) ) ; os . str ( " " ) ; os < < " \n Device OpenCL C version: " < < dev . getInfo < CL_DEVICE_OPENCL_C_VERSION > ( nullptr ) . c_str ( ) < < endl ;
}
}
}
m_Fractorium - > ErrorReportToQTextEdit ( strs , m_Fractorium - > ui . InfoRenderingTextEdit ) ;
*/
}
}
/// <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 ( )
{
# ifndef USE_GLSL
glEnable ( GL_TEXTURE_2D ) ;
auto renderer = m_Fractorium - > m_Controller - > Renderer ( ) ;
auto finalImage = m_Fractorium - > m_Controller - > FinalImage ( ) ;
//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.
auto scaledW = std : : ceil ( width ( ) * devicePixelRatioF ( ) ) ;
auto scaledH = std : : ceil ( height ( ) * devicePixelRatioF ( ) ) ;
//Only draw if the dimensions match exactly.
if ( m_TexWidth = = m_Fractorium - > m_Controller - > FinalRasW ( ) & &
m_TexHeight = = m_Fractorium - > m_Controller - > FinalRasH ( ) & &
( ( m_TexWidth * m_TexHeight ) = = static_cast < GLint > ( finalImage - > size ( ) ) ) )
{
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.
if ( renderer - > RendererType ( ) = = eRendererType : : CPU_RENDERER )
glTexSubImage2D ( GL_TEXTURE_2D , 0 , 0 , 0 , m_TexWidth , m_TexHeight , GL_RGBA , GL_FLOAT , 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_TEXTURE_2D ) ;
# else
this - > glEnable ( GL_TEXTURE_2D ) ;
this - > glActiveTexture ( GL_TEXTURE0 ) ;
const auto renderer = m_Fractorium - > m_Controller - > Renderer ( ) ;
//Ensure all allocation has taken place first.
if ( m_OutputTexID ! = 0 )
{
glBindTexture ( GL_TEXTURE_2D , m_OutputTexID ) ; //The texture to draw to.
const auto scaledW = std : : ceil ( width ( ) * devicePixelRatioF ( ) ) ;
const auto scaledH = std : : ceil ( height ( ) * devicePixelRatioF ( ) ) ;
//Only draw if the dimensions match exactly.
if ( m_TexWidth = = m_Fractorium - > m_Controller - > FinalRasW ( ) & & m_TexHeight = = m_Fractorium - > m_Controller - > FinalRasH ( ) )
{
//Copy data from CPU to OpenGL if using a CPU renderer. This is not needed when using OpenCL.
if ( renderer - > RendererType ( ) = = eRendererType : : CPU_RENDERER | | ! renderer - > Shared ( ) )
{
const auto finalImage = m_Fractorium - > m_Controller - > FinalImage ( ) ;
if ( finalImage & & //Make absolutely sure all image dimensions match when copying host side buffer to GL texture.
! finalImage - > empty ( ) & &
( ( m_TexWidth * m_TexHeight ) = = static_cast < GLint > ( finalImage - > size ( ) ) ) & &
( finalImage - > size ( ) = = renderer - > FinalDimensions ( ) ) )
this - > glTexSubImage2D ( GL_TEXTURE_2D , 0 , 0 , 0 , m_TexWidth , m_TexHeight , GL_RGBA , GL_FLOAT , finalImage - > data ( ) ) ;
}
m_QuadProgram - > bind ( ) ;
this - > glVertexAttribPointer ( m_TexturePosAttr , 2 , GL_FLOAT , GL_FALSE , 0 , m_TexVerts . data ( ) ) ;
this - > glEnableVertexAttribArray ( 0 ) ;
this - > glDrawArrays ( GL_TRIANGLE_STRIP , 0 , 5 ) ;
this - > glDisableVertexAttribArray ( 0 ) ;
m_QuadProgram - > release ( ) ;
}
}
this - > glBindTexture ( GL_TEXTURE_2D , 0 ) ; //Stop using this texture.
this - > glDisable ( GL_TEXTURE_2D ) ;
# endif
}
/// <summary>
/// Set drag and drag modifier states to nothing.
/// </summary>
void GLEmberControllerBase : : ClearDrag ( )
{
m_DragModifier = 0 ;
m_DragState = eDragState : : DragNone ;
}
/// <summary>
/// Wrapper around Allocate() call on the GL widget.
/// </summary>
bool GLEmberControllerBase : : Allocate ( bool force ) { return m_GL - > Allocate ( force ) ; }
/// <summary>
/// Helpers to set/get/clear which keys are pressed while dragging.
/// </summary>
/// <returns>bool</returns>
bool GLEmberControllerBase : : GetAlt ( ) { return ( m_DragModifier & static_cast < et > ( eDragModifier : : DragModAlt ) ) = = static_cast < et > ( eDragModifier : : DragModAlt ) ; }
bool GLEmberControllerBase : : GetShift ( ) { return ( m_DragModifier & static_cast < et > ( eDragModifier : : DragModShift ) ) = = static_cast < et > ( eDragModifier : : DragModShift ) ; }
bool GLEmberControllerBase : : GetControl ( ) { return ( m_DragModifier & static_cast < et > ( eDragModifier : : DragModControl ) ) = = static_cast < et > ( eDragModifier : : DragModControl ) ; }
void GLEmberControllerBase : : SetAlt ( ) { m_DragModifier | = static_cast < et > ( eDragModifier : : DragModAlt ) ; }
void GLEmberControllerBase : : SetShift ( ) { m_DragModifier | = static_cast < et > ( eDragModifier : : DragModShift ) ; }
void GLEmberControllerBase : : SetControl ( ) { m_DragModifier | = static_cast < et > ( eDragModifier : : DragModControl ) ; }
void GLEmberControllerBase : : ClearAlt ( ) { m_DragModifier & = ~ static_cast < et > ( eDragModifier : : DragModAlt ) ; }
void GLEmberControllerBase : : ClearShift ( ) { m_DragModifier & = ~ static_cast < et > ( eDragModifier : : DragModShift ) ; }
void GLEmberControllerBase : : ClearControl ( ) { m_DragModifier & = ~ static_cast < et > ( eDragModifier : : DragModControl ) ; }
/// <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 ( )
{
const auto ember = m_FractoriumEmberController - > CurrentEmber ( ) ;
m_GL - > makeCurrent ( ) ;
m_GL - > glClearColor ( ember - > m_Background . r , ember - > m_Background . g , ember - > m_Background . b , 1.0 ) ;
m_GL - > glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ;
}
/// <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 been repainted, so there's no need to do it again.
if ( m_SelectedXform ! = xform )
{
m_SelectedXform = xform ;
if ( m_GL - > m_Init )
//m_GL->update();
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 ( ) const { return m_Init ; }
bool GLWidget : : Drawing ( ) const { return m_Drawing ; }
GLint GLWidget : : MaxTexSize ( ) const { return m_MaxTexSize ; }
GLuint GLWidget : : OutputTexID ( ) const { return m_OutputTexID ; }
GLint GLWidget : : TexWidth ( ) const { return m_TexWidth ; }
GLint GLWidget : : TexHeight ( ) const { return m_TexHeight ; }
/// <summary>
/// Initialize OpenGL, called once at startup after the main window constructor finishes.
/// 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.
/// Once this is done, the render timer is started after a short delay.
/// Rendering is then clear to begin.
/// </summary>
void GLWidget : : initializeGL ( )
{
# ifdef USE_GLSL
//auto qsf = this->format();
//qDebug() << "initializeGL*****************\nVersion: " << qsf.majorVersion() << ',' << qsf.minorVersion();
//qDebug() << "Profile: " << qsf.profile();
//qDebug() << "Depth buffer size: " << qsf.depthBufferSize();
//qDebug() << "Swap behavior: " << qsf.swapBehavior();
//qDebug() << "Swap interval: " << qsf.swapInterval();
if ( ! m_Init & & m_Fractorium )
{
this - > initializeOpenGLFunctions ( ) ;
if ( ! m_Program )
{
m_Program = new QOpenGLShaderProgram ( this ) ;
if ( ! m_Program - > addShaderFromSourceCode ( QOpenGLShader : : Vertex , vertexShaderSource ) )
{
QMessageBox : : critical ( m_Fractorium , " Shader Error " , " Error compiling affine vertex source: " + m_Program - > log ( ) ) ;
QApplication : : exit ( 1 ) ;
}
if ( ! m_Program - > addShaderFromSourceCode ( QOpenGLShader : : Fragment , fragmentShaderSource ) )
{
QMessageBox : : critical ( m_Fractorium , " Shader Error " , " Error compiling affine fragment source: " + m_Program - > log ( ) ) ;
QApplication : : exit ( 1 ) ;
}
if ( ! m_Program - > link ( ) )
{
QMessageBox : : critical ( m_Fractorium , " Shader Error " , " Error linking affine source: " + m_Program - > log ( ) ) ;
QApplication : : exit ( 1 ) ;
}
m_PosAttr = m_Program - > attributeLocation ( " posattr " ) ;
m_ColAttr = m_Program - > uniformLocation ( " mycolor " ) ;
m_PointSizeUniform = m_Program - > uniformLocation ( " ps " ) ;
m_MatrixUniform = m_Program - > uniformLocation ( " matrix " ) ;
}
if ( ! m_QuadProgram )
{
m_QuadProgram = new QOpenGLShaderProgram ( this ) ;
if ( ! m_QuadProgram - > addShaderFromSourceCode ( QOpenGLShader : : Vertex , quadVertexShaderSource ) )
{
QMessageBox : : critical ( m_Fractorium , " Shader Error " , " Error compiling image texture vertex source: " + m_QuadProgram - > log ( ) ) ;
QApplication : : exit ( 1 ) ;
}
if ( ! m_QuadProgram - > addShaderFromSourceCode ( QOpenGLShader : : Fragment , quadFragmentShaderSource ) )
{
QMessageBox : : critical ( m_Fractorium , " Shader Error " , " Error compiling image texture fragment source: " + m_QuadProgram - > log ( ) ) ;
QApplication : : exit ( 1 ) ;
}
if ( ! m_QuadProgram - > link ( ) )
{
QMessageBox : : critical ( m_Fractorium , " Shader Error " , " Error linking image texture source: " + m_QuadProgram - > log ( ) ) ;
QApplication : : exit ( 1 ) ;
}
m_TexturePosAttr = m_QuadProgram - > attributeLocation ( " posattr " ) ;
m_TextureUniform = m_QuadProgram - > uniformLocation ( " quadtex " ) ;
m_TextureMatrixUniform = m_QuadProgram - > uniformLocation ( " matrix " ) ;
m_TextureProjMatrix . ortho ( 0 , 1 , 1 , 0 , - 1 , 1 ) ;
m_QuadProgram - > bind ( ) ;
m_QuadProgram - > setUniformValue ( m_TextureUniform , 0 ) ;
m_QuadProgram - > setUniformValue ( m_TextureMatrixUniform , m_TextureProjMatrix ) ;
m_QuadProgram - > release ( ) ;
}
# else
if ( ! m_Init & & initializeOpenGLFunctions ( ) & & m_Fractorium )
{
# endif
//cout << "GL Version: " << (char *) glGetString(GL_VERSION) << endl;
//cout << "GLSL version: " << (char *) glGetString(GL_SHADING_LANGUAGE_VERSION) << endl;
this - > glClearColor ( 0.0 , 0.0 , 0.0 , 1.0 ) ;
this - > glDisable ( GL_DEPTH_TEST ) ; //This will remain disabled for the duration of the program.
this - > glEnable ( GL_TEXTURE_2D ) ;
this - > glEnable ( GL_PROGRAM_POINT_SIZE ) ;
this - > glGetIntegerv ( GL_MAX_TEXTURE_SIZE , & m_MaxTexSize ) ;
this - > glDisable ( GL_TEXTURE_2D ) ;
m_Fractorium - > m_WidthSpin - > setMaximum ( m_MaxTexSize ) ;
m_Fractorium - > m_HeightSpin - > setMaximum ( m_MaxTexSize ) ;
}
}
/// <summary>
/// The main drawing/update function.
/// First the quad will be drawn, then the remaining affine circles.
/// </summary>
void GLWidget : : paintGL ( )
{
/*
auto qsf = this - > format ( ) ;
qDebug ( ) < < " paintGL***************** \n Version: " < < qsf . majorVersion ( ) < < ' , ' < < qsf . minorVersion ( ) ;
qDebug ( ) < < " Profile: " < < qsf . profile ( ) ;
qDebug ( ) < < " Depth buffer size: " < < qsf . depthBufferSize ( ) ;
qDebug ( ) < < " Swap behavior: " < < qsf . swapBehavior ( ) ;
qDebug ( ) < < " Swap interval: " < < qsf . swapInterval ( ) ;
*/
const auto controller = m_Fractorium - > m_Controller . get ( ) ;
//Ensure there is a renderer and that it's supposed to be drawing, signified by the running timer.
if ( controller & & controller - > Renderer ( ) /* && controller->ProcessState() != eProcessState::NONE*/ ) //Need a way to determine if at leat one successful render has happened.
{
const auto renderer = controller - > Renderer ( ) ;
const auto unitX = std : : abs ( renderer - > UpperRightX ( false ) - renderer - > LowerLeftX ( false ) ) / 2.0f ;
const auto unitY = std : : abs ( renderer - > UpperRightY ( false ) - renderer - > LowerLeftY ( false ) ) / 2.0f ;
if ( unitX > 100000 | | unitY > 100000 ) //Need a better way to do this.//TODO
{
qDebug ( ) < < unitX < < " " < < unitY ;
//return;
}
m_Drawing = true ;
if ( m_Fractorium - > DrawImage ( ) )
{
GLController ( ) - > DrawImage ( ) ;
}
else
{
glClearColor ( 0.0 , 0.0 , 0.0 , 1.0 ) ;
glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ;
}
//Affine drawing.
const auto pre = m_Fractorium - > DrawPreAffines ( ) ;
const auto post = m_Fractorium - > DrawPostAffines ( ) ;
this - > glEnable ( GL_BLEND ) ;
this - > glEnable ( GL_LINE_SMOOTH ) ;
this - > glEnable ( GL_POINT_SMOOTH ) ;
# if defined (__APPLE__) || defined(MACOSX)
this - > glBlendFuncSeparate ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA , GL_ONE , GL_ONE_MINUS_SRC_COLOR ) ;
# else
this - > glBlendFuncSeparate ( GL_SRC_ALPHA , GL_ONE_MINUS_SRC_ALPHA , GL_ONE , GL_ZERO ) ;
# endif
# ifndef USE_GLSL
this - > glMatrixMode ( GL_PROJECTION ) ;
this - > glPushMatrix ( ) ;
this - > glLoadIdentity ( ) ;
this - > glOrtho ( - unitX , unitX , - unitY , unitY , - 1 , 1 ) ; //Projection matrix: OpenGL camera is always centered, just move the ember internally inside the renderer.
this - > glMatrixMode ( GL_MODELVIEW ) ;
this - > glPushMatrix ( ) ;
this - > glLoadIdentity ( ) ;
controller - > GLController ( ) - > DrawAffines ( pre , post ) ;
this - > glMatrixMode ( GL_PROJECTION ) ;
this - > glPopMatrix ( ) ;
this - > glMatrixMode ( GL_MODELVIEW ) ;
this - > glPopMatrix ( ) ;
# else
m_Program - > bind ( ) ;
m_ProjMatrix . setToIdentity ( ) ;
m_ProjMatrix . ortho ( - unitX , unitX , - unitY , unitY , - 1 , 1 ) ; //Projection matrix: OpenGL camera is always centered, just move the ember internally inside the renderer.
m_ModelViewMatrix . setToIdentity ( ) ;
//this->DrawUnitSquare();
controller - > GLController ( ) - > DrawAffines ( pre , post ) ;
m_Program - > release ( ) ;
# endif
this - > glDisable ( GL_BLEND ) ;
this - > glDisable ( GL_LINE_SMOOTH ) ;
this - > glDisable ( GL_POINT_SMOOTH ) ;
m_Drawing = false ;
}
}
/// <summary>
/// Draw the image on the quad.
/// </summary>
template < typename T >
void GLEmberController < T > : : DrawImage ( )
{
const auto renderer = m_FractoriumEmberController - > Renderer ( ) ;
const auto ember = m_FractoriumEmberController - > CurrentEmber ( ) ;
m_GL - > glClearColor ( ember - > m_Background . r , ember - > m_Background . g , ember - > m_Background . b , 1.0 ) ;
m_GL - > glClear ( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ) ;
renderer - > EnterFinalAccum ( ) ; //Lock, may not be necessary, but just in case.
renderer - > EnterResize ( ) ;
if ( SizesMatch ( ) ) //Ensure all sizes are correct. If not, do nothing.
{
m_GL - > DrawQuad ( ) ; //Output image is drawn here.
}
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.
if ( ! m_Fractorium - > DrawXforms ( ) )
return ;
const auto ember = m_FractoriumEmberController - > CurrentEmber ( ) ;
const auto dragging = m_DragState = = eDragState : : DragDragging ;
const auto forceFinal = m_Fractorium - > HaveFinal ( ) ;
if ( m_DragState = = eDragState : : DragRotateScale )
{
const auto dprf = m_GL - > devicePixelRatioF ( ) ;
const auto world = ScrolledCenter ( true ) ;
m_GL - > glLineWidth ( 1.0f * dprf ) ;
const GLfloat vertices [ ] =
{
static_cast < GLfloat > ( m_MouseWorldPos . x ) , static_cast < GLfloat > ( m_MouseWorldPos . y ) , //Mouse position while dragging with right button down...
static_cast < GLfloat > ( world . x ) , static_cast < GLfloat > ( world . y ) //...to center.
} ;
const QVector4D col ( 0.0f , 1.0f , 1.0f , 1.0f ) ;
m_GL - > DrawPointOrLine ( col , vertices , 2 , GL_LINES ) ;
}
//Draw grid if control key is pressed.
if ( ( m_GL - > hasFocus ( ) & & GetControl ( ) ) | | m_Fractorium - > DrawGrid ( ) )
DrawGrid ( ) ;
//When dragging, only draw the selected xform's affine and hide all others.
if ( ! m_Fractorium - > m_Settings - > ShowAllXforms ( ) & & dragging )
{
if ( m_SelectedXform )
DrawAffine ( m_SelectedXform , m_AffineType = = eAffineType : : AffinePre , true , false ) ;
}
else //Show all while dragging, or not dragging just hovering/mouse move.
{
if ( pre & & m_Fractorium - > DrawAllPre ( ) ) //Draw all pre affine if specified.
{
size_t i = 0 ;
bool any = false ;
while ( auto xform = ember - > GetTotalXform ( i , forceFinal ) )
if ( m_Fractorium - > IsXformSelected ( i + + ) )
{
any = true ;
break ;
}
i = 0 ;
while ( const auto xform = ember - > GetTotalXform ( i , forceFinal ) )
{
bool selected = m_Fractorium - > IsXformSelected ( i + + ) | | ( ! any & & m_SelectedXform = = xform ) ;
DrawAffine ( xform , true , selected , ! dragging & & ( m_HoverXform = = xform ) ) ;
}
}
else if ( pre & & m_Fractorium - > DrawSelectedPre ( ) ) //Only draw selected pre affine, and if none are selected, draw current. All are considered "selected", so circles are drawn around them.
{
size_t i = 0 ;
bool any = false ;
while ( const auto xform = ember - > GetTotalXform ( i , forceFinal ) )
{
if ( m_Fractorium - > IsXformSelected ( i + + ) )
{
DrawAffine ( xform , true , true , ! dragging & & ( m_HoverXform = = xform ) ) ;
any = true ;
}
}
if ( ! any )
DrawAffine ( m_FractoriumEmberController - > CurrentXform ( ) , true , true , ! dragging & & ( m_HoverXform = = m_FractoriumEmberController - > CurrentXform ( ) ) ) ;
}
if ( post & & m_Fractorium - > DrawAllPost ( ) ) //Draw all post affine if specified.
{
size_t i = 0 ;
bool any = false ;
while ( const auto xform = ember - > GetTotalXform ( i , forceFinal ) )
if ( m_Fractorium - > IsXformSelected ( i + + ) )
{
any = true ;
break ;
}
i = 0 ;
while ( const auto xform = ember - > GetTotalXform ( i , forceFinal ) )
{
bool selected = m_Fractorium - > IsXformSelected ( i + + ) | | ( ! any & & m_SelectedXform = = xform ) ;
DrawAffine ( xform , false , selected , ! dragging & & ( m_HoverXform = = xform ) ) ;
}
}
else if ( post & & m_Fractorium - > DrawSelectedPost ( ) ) //Only draw selected post, and if none are selected, draw current. All are considered "selected", so circles are drawn around them.
{
size_t i = 0 ;
bool any = false ;
while ( const auto xform = ember - > GetTotalXform ( i , forceFinal ) )
{
if ( m_Fractorium - > IsXformSelected ( i + + ) )
{
DrawAffine ( xform , false , true , ! dragging & & ( m_HoverXform = = xform ) ) ;
any = true ;
}
}
if ( ! any )
DrawAffine ( m_FractoriumEmberController - > CurrentXform ( ) , false , true , ! dragging & & ( m_HoverXform = = m_FractoriumEmberController - > CurrentXform ( ) ) ) ;
}
}
if ( dragging ) //Draw large yellow dot on select or drag.
{
# ifndef USE_GLSL
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 ( ) ;
# else
const GLfloat vertices [ ] = //Should these be of type T?//TODO
{
static_cast < GLfloat > ( m_DragHandlePos . x ) , static_cast < GLfloat > ( m_DragHandlePos . y )
} ;
const QVector4D col ( 1.0f , 1.0f , 0.5f , 1.0f ) ;
m_GL - > DrawPointOrLine ( col , vertices , 1 , GL_POINTS , false , 6.0f ) ;
# endif
}
else if ( m_DragState = = eDragState : : DragSelect )
{
m_GL - > glLineWidth ( 2.0f * m_GL - > devicePixelRatioF ( ) ) ;
# ifndef USE_GLSL
m_GL - > glBegin ( GL_LINES ) ;
m_GL - > glColor4f ( 0.0f , 0.0f , 1.0f , 1.0f ) ;
m_GL - > glVertex2f ( m_MouseDownWorldPos . x , m_MouseDownWorldPos . y ) ; //UL->UR
m_GL - > glVertex2f ( m_MouseWorldPos . x , m_MouseDownWorldPos . y ) ;
m_GL - > glVertex2f ( m_MouseDownWorldPos . x , m_MouseWorldPos . y ) ; //LL->LR
m_GL - > glVertex2f ( m_MouseWorldPos . x , m_MouseWorldPos . y ) ;
m_GL - > glVertex2f ( m_MouseDownWorldPos . x , m_MouseDownWorldPos . y ) ; //UL->LL
m_GL - > glVertex2f ( m_MouseDownWorldPos . x , m_MouseWorldPos . y ) ;
m_GL - > glVertex2f ( m_MouseWorldPos . x , m_MouseDownWorldPos . y ) ; //UR->LR
m_GL - > glVertex2f ( m_MouseWorldPos . x , m_MouseWorldPos . y ) ;
m_GL - > glEnd ( ) ;
# else
const GLfloat vertices [ ] = //Should these be of type T?//TODO
{
static_cast < GLfloat > ( m_MouseDownWorldPos . x ) , static_cast < GLfloat > ( m_MouseDownWorldPos . y ) , //UL->UR
static_cast < GLfloat > ( m_MouseWorldPos . x ) , static_cast < GLfloat > ( m_MouseDownWorldPos . y ) ,
static_cast < GLfloat > ( m_MouseDownWorldPos . x ) , static_cast < GLfloat > ( m_MouseWorldPos . y ) , //LL->LR
static_cast < GLfloat > ( m_MouseWorldPos . x ) , static_cast < GLfloat > ( m_MouseWorldPos . y ) ,
static_cast < GLfloat > ( m_MouseDownWorldPos . x ) , static_cast < GLfloat > ( m_MouseDownWorldPos . y ) , //UL->LL
static_cast < GLfloat > ( m_MouseDownWorldPos . x ) , static_cast < GLfloat > ( m_MouseWorldPos . y ) ,
static_cast < GLfloat > ( m_MouseWorldPos . x ) , static_cast < GLfloat > ( m_MouseDownWorldPos . y ) , //UR->LR
static_cast < GLfloat > ( m_MouseWorldPos . x ) , static_cast < GLfloat > ( m_MouseWorldPos . y )
} ;
const QVector4D col ( 0.0f , 0.0f , 1.0f , 1.0f ) ;
m_GL - > DrawPointOrLine ( col , vertices , 8 , GL_LINES ) ;
# endif
m_GL - > glLineWidth ( 1.0f * m_GL - > devicePixelRatioF ( ) ) ;
}
else if ( m_HoverType ! = eHoverType : : HoverNone & & m_HoverXform = = m_SelectedXform ) //Draw large turquoise dot on hover if they are hovering over the selected xform.
{
# ifndef USE_GLSL
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 ( ) ;
# else
const GLfloat vertices [ ] = //Should these be of type T?//TODO
{
static_cast < GLfloat > ( m_HoverHandlePos . x ) , static_cast < GLfloat > ( m_HoverHandlePos . y )
} ;
const QVector4D col ( 0.5f , 1.0f , 1.0f , 1.0f ) ;
m_GL - > DrawPointOrLine ( col , vertices , 1 , GL_POINTS , false , 6.0f ) ;
# endif
}
}
/// <summary>
/// Set drag modifiers based on key press.
/// </summary>
/// <param name="e">The event</param>
bool GLEmberControllerBase : : KeyPress_ ( QKeyEvent * e )
{
if ( e - > key ( ) = = Qt : : Key_Control )
{
SetControl ( ) ;
return true ;
}
return false ;
}
/// <summary>
/// Call controller KeyPress().
/// </summary>
/// <param name="e">The event</param>
void GLWidget : : keyPressEvent ( QKeyEvent * e )
{
if ( ! GLController ( ) | | ! GLController ( ) - > KeyPress_ ( e ) )
QOpenGLWidget : : keyPressEvent ( e ) ;
update ( ) ;
}
/// <summary>
/// Set drag modifiers based on key release.
/// </summary>
/// <param name="e">The event</param>
bool GLEmberControllerBase : : KeyRelease_ ( QKeyEvent * e )
{
if ( e ! = nullptr & & e - > key ( ) = = Qt : : Key_Control )
{
ClearControl ( ) ;
return true ;
}
return false ;
}
/// <summary>
/// Call controller KeyRelease_().
/// </summary>
/// <param name="e">The event</param>
void GLWidget : : keyReleaseEvent ( QKeyEvent * e )
{
if ( e )
{
if ( ! GLController ( ) | | ! GLController ( ) - > KeyRelease_ ( e ) )
QOpenGLWidget : : keyReleaseEvent ( e ) ;
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 )
{
if ( ! e )
return ;
const auto x = e - > position ( ) . x ( ) ;
const auto y = e - > position ( ) . y ( ) ;
v3T const mouseFlipped ( x * m_GL - > devicePixelRatioF ( ) , m_Viewport [ 3 ] - y * m_GL - > devicePixelRatioF ( ) , 0 ) ; //Must flip y because in OpenGL, 0,0 is bottom left, but in windows, it's top left.
const auto ember = m_FractoriumEmberController - > CurrentEmber ( ) ;
const auto renderer = m_FractoriumEmberController - > Renderer ( ) ;
//Ensure everything has been initialized.
if ( ! renderer )
return ;
m_MouseDownPos = glm : : ivec2 ( x * m_GL - > devicePixelRatioF ( ) , y * m_GL - > devicePixelRatioF ( ) ) ; //Capture the raster coordinates of where the mouse was clicked.
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 ) ;
const auto mod = e - > modifiers ( ) ;
if ( mod . testFlag ( Qt : : ShiftModifier ) )
SetShift ( ) ;
if ( mod . testFlag ( Qt : : AltModifier ) )
SetAlt ( ) ;
if ( m_DragState = = eDragState : : DragNone ) //Only take action if the user wasn't already dragging.
{
m_MouseDownWorldPos = m_MouseWorldPos ; //Set the mouse down position to the current position.
if ( e - > button ( ) & Qt : : LeftButton )
{
const auto xformIndex = UpdateHover ( mouseFlipped ) ; //Determine if an affine circle was clicked.
if ( m_HoverXform & & xformIndex ! = - 1 )
{
m_SelectedXform = m_HoverXform ;
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.
//The user has selected an xform by clicking on it, so update the main GUI by selecting this xform in the combo box.
m_Fractorium - > CurrentXform ( xformIndex ) ; //Must do this first so UpdateXform() below properly grabs the current plus any selected.
m_DragSrcPreTransforms . clear ( ) ;
m_DragSrcPostTransforms . clear ( ) ;
m_FractoriumEmberController - > UpdateXform ( [ & ] ( Xform < T > * xform , size_t xfindex , size_t selIndex )
{
if ( m_AffineType = = eAffineType : : AffinePre )
m_DragSrcPreTransforms [ xfindex ] = xform - > m_Affine ;
else
m_DragSrcPostTransforms [ xfindex ] = xform - > m_Post ;
} , eXformUpdate : : UPDATE_SELECTED , false ) ; //Don't update renderer here.
m_DragHandlePos = m_HoverHandlePos ; //The location in local coordinates of the point selected on the spinner, x, y or center.
m_DragState = eDragState : : DragDragging ;
m_GL - > repaint ( ) ;
}
else //Nothing was selected.
{
//m_SelectedXform = nullptr;
m_DragSrcPreTransforms . clear ( ) ;
m_DragSrcPostTransforms . clear ( ) ;
m_DragState = eDragState : : DragNone ;
}
}
else if ( e - > button ( ) = = Qt : : MiddleButton | | ( e - > button ( ) = = Qt : : RightButton & & e - > modifiers ( ) & Qt : : ShiftModifier & & ! ( e - > modifiers ( ) & Qt : : AltModifier ) ) ) //Middle button or right button with shift key, do 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 ;
m_DragState = eDragState : : DragPanning ;
}
else if ( e - > button ( ) = = Qt : : RightButton ) //Right button does whole image rotation and scaling.
{
if ( m_Fractorium - > DrawImage ( ) )
{
m_CenterDownX = ember - > m_CenterX ; //Capture these because they will change when rotating and scaling.
m_CenterDownY = ember - > m_CenterY ;
if ( GetAlt ( ) & & GetShift ( ) )
{
m_PitchDown = ember - > m_CamPitch * RAD_2_DEG_T ;
m_YawDown = ember - > m_CamYaw * RAD_2_DEG_T ;
m_DragState = eDragState : : DragPitchYaw ;
}
else
{
m_RotationDown = ember - > m_Rotate ;
m_ScaleDown = ember - > m_PixelsPerUnit ;
m_DragState = eDragState : : DragRotateScale ;
}
}
}
}
}
/// <summary>
/// Call controller MousePress().
/// </summary>
/// <param name="e">The event</param>
void GLWidget : : mousePressEvent ( QMouseEvent * e )
{
if ( e )
{
setFocus ( ) ; //Must do this so that this window gets keyboard events.
if ( const auto controller = GLController ( ) )
controller - > MousePress ( e ) ;
QOpenGLWidget : : mousePressEvent ( e ) ;
}
}
/// <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 )
{
if ( e )
{
const auto x = e - > position ( ) . x ( ) ;
const auto y = e - > position ( ) . y ( ) ;
v3T const mouseFlipped ( x * m_GL - > devicePixelRatioF ( ) , m_Viewport [ 3 ] - y * m_GL - > devicePixelRatioF ( ) , 0 ) ; //Must flip y because in OpenGL, 0,0 is bottom left, but in windows, it's top left.
m_MouseWorldPos = WindowToWorld ( mouseFlipped , false ) ;
if ( m_DragState = = eDragState : : DragDragging & & ( e - > button ( ) & Qt : : LeftButton ) )
UpdateHover ( mouseFlipped ) ;
if ( m_DragState = = eDragState : : DragNone )
m_Fractorium - > OnXformsSelectNoneButtonClicked ( false ) ;
m_DragState = eDragState : : DragNone ;
m_DragModifier = 0 ;
m_GL - > update ( ) ;
}
}
/// <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.
if ( const auto controller = GLController ( ) )
controller - > MouseRelease ( e ) ;
QOpenGLWidget : : mouseReleaseEvent ( e ) ;
}
/// <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 )
{
const auto draw = true ;
const auto x = e - > position ( ) . x ( ) ;
const auto y = e - > position ( ) . y ( ) ;
const glm : : ivec2 mouse ( x * m_GL - > devicePixelRatioF ( ) , y * m_GL - > devicePixelRatioF ( ) ) ;
const v3T mouseFlipped ( x * m_GL - > devicePixelRatioF ( ) , m_Viewport [ 3 ] - y * m_GL - > devicePixelRatioF ( ) , 0 ) ; //Must flip y because in OpenGL, 0,0 is bottom left, but in windows, it's top left.
const auto ember = m_FractoriumEmberController - > CurrentEmber ( ) ;
//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 ( ) )
m_Fractorium - > SetCoordinateStatus ( x * m_GL - > devicePixelRatioF ( ) , y * m_GL - > devicePixelRatioF ( ) , m_MouseWorldPos . x , m_MouseWorldPos . y ) ;
if ( m_SelectedXform & & m_DragState = = eDragState : : DragDragging ) //Dragging and affine.
{
const bool pre = m_AffineType = = eAffineType : : AffinePre ;
if ( m_HoverType = = eHoverType : : HoverTranslation )
CalcDragTranslation ( ) ;
else if ( m_HoverType = = eHoverType : : HoverXAxis )
CalcDragXAxis ( ) ;
else if ( m_HoverType = = eHoverType : : HoverYAxis )
CalcDragYAxis ( ) ;
m_FractoriumEmberController - > FillAffineWithXform ( m_SelectedXform , pre ) ; //Update the spinners in the affine tab of the main window.
m_FractoriumEmberController - > UpdateRender ( ) ; //Restart the rendering process.
}
else if ( ( m_DragState = = eDragState : : DragNone | | m_DragState = = eDragState : : DragSelect ) & & ( e - > buttons ( ) & Qt : : LeftButton ) )
{
m_DragState = eDragState : : DragSelect ; //Only set drag state once the user starts moving the mouse with the left button down.
//Iterate over each xform, seeing if it's in the bounding box.
const QPointF tl ( m_MouseDownWorldPos . x , m_MouseDownWorldPos . y ) ;
const QPointF br ( m_MouseWorldPos . x , m_MouseWorldPos . y ) ;
const QRectF qrf ( tl , br ) ;
const T scale = m_FractoriumEmberController - > AffineScaleCurrentToLocked ( ) ;
const auto i = 0 ;
m_FractoriumEmberController - > UpdateXform ( [ & ] ( Xform < T > * xform , size_t xfindex , size_t selIndex )
{
if ( m_Fractorium - > DrawAllPre ( ) | | xform = = m_SelectedXform ) //Draw all pre affine if specified.
{
const QPointF cd ( xform - > m_Affine . C ( ) * scale , xform - > m_Affine . F ( ) * scale ) ;
bool b = qrf . contains ( cd ) ;
m_FractoriumEmberController - > XformCheckboxAt ( static_cast < int > ( xfindex ) , [ & ] ( QCheckBox * cb )
{
cb - > setChecked ( b ) ;
} ) ;
}
if ( m_Fractorium - > DrawAllPost ( ) | | xform = = m_SelectedXform )
{
const QPointF cd ( xform - > m_Post . C ( ) * scale , xform - > m_Post . F ( ) * scale ) ;
bool b = qrf . contains ( cd ) ;
m_FractoriumEmberController - > XformCheckboxAt ( static_cast < int > ( xfindex ) , [ & ] ( QCheckBox * cb )
{
if ( ! cb - > isChecked ( ) & & b )
cb - > setChecked ( b ) ;
} ) ;
}
} , eXformUpdate : : UPDATE_ALL , false ) ;
}
else if ( m_DragState = = eDragState : : DragPanning ) //Translating the whole image.
{
const auto x = - ( m_MouseWorldPos . x - m_MouseDownWorldPos . x ) ;
const auto y = ( m_MouseWorldPos . y - m_MouseDownWorldPos . y ) ;
Affine2D < T > rotMat ;
rotMat . C ( m_CenterDownX ) ;
rotMat . F ( m_CenterDownY ) ;
rotMat . Rotate ( ember - > m_Rotate * DEG_2_RAD_T ) ;
const v2T v1 ( x , y ) ;
const v2T v2 = rotMat . TransformVector ( v1 ) ;
ember - > m_CenterX = v2 . x ;
ember - > m_CenterY = ember - > m_RotCenterY = v2 . y ;
m_FractoriumEmberController - > SetCenter ( ember - > m_CenterX , ember - > m_CenterY ) ; //Will restart the rendering process.
}
else if ( m_DragState = = eDragState : : DragRotateScale ) //Rotating and scaling the whole image.
{
const T rot = CalcRotation ( ) ;
const T scale = CalcScale ( ) ;
ember - > m_Rotate = NormalizeDeg180 < T > ( m_RotationDown + rot ) ;
m_Fractorium - > SetRotation ( ember - > m_Rotate , true ) ;
m_Fractorium - > SetScale ( std : : max ( static_cast < T > ( 10 ) , m_ScaleDown + scale ) ) ; //Will restart the rendering process.
}
else if ( m_DragState = = eDragState : : DragPitchYaw ) //Pitching and yawing the whole image.
{
T pitch ;
T yaw ;
auto rotate = ember - > m_Rotate ;
if ( ( rotate < = 45 & & rotate > = - 45 ) | | ( rotate > = 135 ) | | ( rotate < = - 135 ) )
{
pitch = m_PitchDown + ( m_MouseWorldPos . y - m_MouseDownWorldPos . y ) * 100 ;
yaw = m_YawDown + ( m_MouseWorldPos . x - m_MouseDownWorldPos . x ) * 100 ;
}
else
{
pitch = m_PitchDown + ( m_MouseWorldPos . x - m_MouseDownWorldPos . x ) * 100 ;
yaw = m_YawDown + ( m_MouseWorldPos . y - m_MouseDownWorldPos . y ) * 100 ;
}
m_Fractorium - > SetPitch ( pitch ) ;
m_Fractorium - > SetYaw ( yaw ) ;
}
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 )
{
m_HoverXform = nullptr ;
}
}
//Only update if the user was dragging or hovered over a point.
//Use repaint() to update immediately for a more responsive feel.
if ( ( m_DragState ! = eDragState : : DragNone ) | | draw )
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.
if ( const auto controller = GLController ( ) )
controller - > MouseMove ( e ) ;
QOpenGLWidget : : mouseMoveEvent ( e ) ;
}
/// <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.
/// If Alt is pressed, only the scale of the affines is changed, the scale of the image remains untouched.
/// </summary>
/// <param name="e">The event</param>
template < typename T >
void GLEmberController < T > : : Wheel ( QWheelEvent * e )
{
if ( ( e - > modifiers ( ) & Qt : : AltModifier ) & & m_Fractorium - > DrawXforms ( ) )
{
# ifdef __APPLE__
m_FractoriumEmberController - > ChangeLockedScale ( e - > angleDelta ( ) . y ( ) > = 0 ? 1.0981 : 0.9 ) ;
# else
m_FractoriumEmberController - > ChangeLockedScale ( e - > angleDelta ( ) . x ( ) > = 0 ? 1.0981 : 0.9 ) ;
# endif
m_GL - > update ( ) ;
}
else
{
if ( m_Fractorium - > DrawImage ( ) & & ! ( e - > buttons ( ) & Qt : : MiddleButton ) & & ! ( e - > modifiers ( ) & Qt : : ShiftModifier ) ) //Middle button does whole image translation, so ignore the mouse wheel while panning to avoid inadvertent zooming. ShiftModifier for sensitive mouse.
{
auto ember = m_FractoriumEmberController - > CurrentEmber ( ) ;
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 )
{
if ( e )
{
if ( auto controller = GLController ( ) )
{
controller - > Wheel ( e ) ;
e - > accept ( ) ; //Prevents it from being sent to the main scroll bars. Scrolling should only affect the scale parameter and affine display zooming.
}
}
//Do not call QOpenGLWidget::wheelEvent(e) because this should only affect the scale and not the position of the scroll bars.
}
/// <summary>
/// Wrapper around drawing a simple primitive, like a point or line, using a GLSL program.
/// </summary>
/// <param name="col">The color to draw with</param>
/// <param name="vertices">The vertices to use</param>
/// <param name="drawType">The type of primitive to draw, such as GL_POINT or GL_LINES</param>
/// <param name="dashed">True to draw dashed lines, else solid</param>
/// <param name="pointSize">The size in pixels of points, which is internally scaled by the device pixel ratio.</param>
void GLWidget : : DrawPointOrLine ( const QVector4D & col , const std : : vector < float > & vertices , int drawType , bool dashed , GLfloat pointSize )
{
DrawPointOrLine ( col , vertices . data ( ) , int ( vertices . size ( ) / 2 ) , drawType , dashed , pointSize ) ;
}
/// <summary>
/// Wrapper around drawing a simple primitive, like a point or line, using a GLSL program.
/// </summary>
/// <param name="col">The color to draw with</param>
/// <param name="vertices">The vertices to use</param>
/// <param name="size">The number of verticies. This is usually the size of vertices / 2.</param>
/// <param name="drawType">The type of primitive to draw, such as GL_POINT or GL_LINES</param>
/// <param name="dashed">True to draw dashed lines, else solid</param>
/// <param name="pointSize">The size in pixels of points, which is internally scaled by the device pixel ratio.</param>
void GLWidget : : DrawPointOrLine ( const QVector4D & col , const GLfloat * vertices , int size , int drawType , bool dashed , GLfloat pointSize )
{
# ifdef USE_GLSL
if ( dashed & & ( drawType = = GL_LINES | | drawType = = GL_LINE_LOOP ) )
{
glLineStipple ( 1 , 0XFF00 ) ;
glEnable ( GL_LINE_STIPPLE ) ;
}
m_ModelViewProjectionMatrix = m_ProjMatrix * m_ModelViewMatrix ;
m_Program - > setUniformValue ( m_ColAttr , col ) ;
m_Program - > setUniformValue ( m_PointSizeUniform , pointSize * GLfloat ( devicePixelRatioF ( ) ) ) ;
m_Program - > setUniformValue ( m_MatrixUniform , m_ModelViewProjectionMatrix ) ;
this - > glVertexAttribPointer ( m_PosAttr , 2 , GL_FLOAT , GL_FALSE , 0 , vertices ) ;
this - > glEnableVertexAttribArray ( 0 ) ;
this - > glDrawArrays ( drawType , 0 , size ) ;
this - > glDisableVertexAttribArray ( 0 ) ;
if ( dashed & & ( drawType = = GL_LINES | | drawType = = GL_LINE_LOOP ) )
glDisable ( GL_LINE_STIPPLE ) ;
# endif
}
/// <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>
void GLWidget : : SetDimensions ( int w , int h )
{
const auto downscaledW = std : : ceil ( w / devicePixelRatioF ( ) ) ;
const auto downscaledH = std : : ceil ( h / devicePixelRatioF ( ) ) ;
setFixedSize ( downscaledW , downscaledH ) ;
}
/// <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 ;
//auto scaledW = std::ceil(width() * devicePixelRatioF());
const auto w = m_Fractorium - > m_Controller - > FinalRasW ( ) ;
const auto h = m_Fractorium - > m_Controller - > FinalRasH ( ) ;
bool const doResize = force | | m_TexWidth ! = w | | m_TexHeight ! = h ;
bool const doIt = doResize | | m_OutputTexID = = 0 ;
# ifndef USE_GLSL
if ( doIt )
{
m_TexWidth = static_cast < GLint > ( w ) ;
m_TexHeight = static_cast < GLint > ( h ) ;
glEnable ( GL_TEXTURE_2D ) ;
glTexEnvf ( GL_TEXTURE_ENV , GL_TEXTURE_ENV_MODE , GL_REPLACE ) ;
if ( doResize )
Deallocate ( ) ;
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 ) ;
# if defined (__APPLE__) || defined(MACOSX)
glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGB32F , m_TexWidth , m_TexHeight , 0 , GL_RGB , GL_FLOAT , nullptr ) ;
# else
glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGBA32F , m_TexWidth , m_TexHeight , 0 , GL_RGBA , GL_FLOAT , nullptr ) ;
# endif
alloc = true ;
}
if ( alloc )
{
glBindTexture ( GL_TEXTURE_2D , 0 ) ;
glDisable ( GL_TEXTURE_2D ) ;
}
# else
if ( doIt )
{
m_TexWidth = static_cast < GLint > ( w ) ;
m_TexHeight = static_cast < GLint > ( h ) ;
this - > glEnable ( GL_TEXTURE_2D ) ;
if ( doResize )
Deallocate ( ) ;
this - > glActiveTexture ( GL_TEXTURE0 ) ;
this - > glGenTextures ( 1 , & m_OutputTexID ) ;
this - > glBindTexture ( GL_TEXTURE_2D , m_OutputTexID ) ;
this - > glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MIN_FILTER , GL_LINEAR ) ; //Fractron had this as GL_LINEAR_MIPMAP_LINEAR for OpenCL and Cuda.
this - > glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_MAG_FILTER , GL_LINEAR ) ;
this - > glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_S , GL_CLAMP ) ;
this - > glTexParameteri ( GL_TEXTURE_2D , GL_TEXTURE_WRAP_T , GL_CLAMP ) ;
# if defined (__APPLE__) || defined(MACOSX)
this - > glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGB32F , m_TexWidth , m_TexHeight , 0 , GL_RGB , GL_FLOAT , nullptr ) ;
# else
this - > glTexImage2D ( GL_TEXTURE_2D , 0 , GL_RGBA32F , m_TexWidth , m_TexHeight , 0 , GL_RGBA , GL_FLOAT , nullptr ) ;
# endif
alloc = true ;
}
if ( alloc )
{
this - > glBindTexture ( GL_TEXTURE_2D , 0 ) ;
this - > glDisable ( GL_TEXTURE_2D ) ;
}
# endif
this - > glFinish ( ) ;
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 )
{
this - > glBindTexture ( GL_TEXTURE_2D , m_OutputTexID ) ;
this - > 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 ( )
{
if ( m_Init & & ( m_ViewWidth ! = m_TexWidth | | m_ViewHeight ! = m_TexHeight ) )
{
this - > glViewport ( 0 , 0 , GLint { m_TexWidth } , GLint { m_TexHeight } ) ;
# ifdef USE_GLSL
m_Viewport = glm : : ivec4 ( 0 , 0 , m_TexWidth , m_TexHeight ) ;
# endif
m_ViewWidth = m_TexWidth ;
m_ViewHeight = m_TexHeight ;
}
}
/// <summary>
/// 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.
/// </summary>
/// <returns>True if all sizes match, else false.</returns>
template < typename T >
bool GLEmberController < T > : : SizesMatch ( )
{
//auto scaledW = std::ceil(m_GL->width() * m_GL->devicePixelRatioF());
//auto scaledH = std::ceil(m_GL->height() * m_GL->devicePixelRatioF());
const auto ember = m_FractoriumEmberController - > CurrentEmber ( ) ;
return ( ember & &
ember - > m_FinalRasW = = m_GL - > m_TexWidth & &
ember - > m_FinalRasH = = m_GL - > m_TexHeight & &
m_GL - > m_TexWidth = = m_GL - > m_ViewWidth & &
m_GL - > m_TexHeight = = m_GL - > m_ViewHeight ) ;
}
/// <summary>
/// Draw the unit square.
/// </summary>
void GLWidget : : DrawUnitSquare ( )
{
glLineWidth ( 1.0f * devicePixelRatioF ( ) ) ;
# ifndef USE_GLSL
glBegin ( GL_LINES ) ;
glColor4f ( 1.0f , 1.0f , 1.0f , 0.25f ) ;
glVertex2f ( - 1 , - 1 ) ;
glVertex2f ( 1 , - 1 ) ;
glVertex2f ( - 1 , 1 ) ;
glVertex2f ( 1 , 1 ) ;
glVertex2f ( - 1 , - 1 ) ;
glVertex2f ( - 1 , 1 ) ;
glVertex2f ( 1 , - 1 ) ;
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 ) ;
glVertex2f ( 0 , - 1 ) ;
glVertex2f ( 0 , 1 ) ;
glEnd ( ) ;
# else
GLfloat vertices [ ] = //Should these be of type T?//TODO
{
- 1 , - 1 ,
1 , - 1 ,
- 1 , 1 ,
1 , 1 ,
- 1 , - 1 ,
- 1 , 1 ,
1 , - 1 ,
1 , 1
} ;
QVector4D col ( 1.0f , 1.0f , 1.0f , 0.25f ) ;
DrawPointOrLine ( col , vertices , 8 , GL_LINES ) ;
const GLfloat vertices2 [ ] = //Should these be of type T?//TODO
{
- 1 , 0 ,
1 , 0
} ;
QVector4D col2 ( 1.0f , 0.0f , 0.0f , 0.5f ) ;
DrawPointOrLine ( col2 , vertices2 , 2 , GL_LINES ) ;
const GLfloat vertices3 [ ] = //Should these be of type T?//TODO
{
0 , - 1 ,
0 , 1
} ;
const QVector4D col3 ( 0.0f , 1.0f , 0.0f , 0.5f ) ;
DrawPointOrLine ( col3 , vertices3 , 2 , GL_LINES ) ;
# endif
}
/// <summary>
/// Draw the grid
/// The frequency of the grid lines will change depending on the zoom (ALT+WHEEL).
/// Calculated with the frame always centered, the renderer just moves the camera.
/// </summary>
template < typename T >
void GLEmberController < T > : : DrawGrid ( )
{
const auto renderer = m_Fractorium - > m_Controller - > Renderer ( ) ;
const auto scale = m_FractoriumEmberController - > AffineScaleCurrentToLocked ( ) ;
//qDebug() << renderer->UpperRightX(false) << " " << renderer->LowerLeftX(false) << " " << renderer->UpperRightY(false) << " " << renderer->LowerLeftY(false);
const float unitX = ( std : : abs ( renderer - > UpperRightX ( false ) - renderer - > LowerLeftX ( false ) ) / 2.0f ) / scale ;
const float unitY = ( std : : abs ( renderer - > UpperRightY ( false ) - renderer - > LowerLeftY ( false ) ) / 2.0f ) / scale ;
if ( unitX > 100000 | | unitY > 100000 ) //Need a better way to do this.//TODO
{
qDebug ( ) < < unitX < < " " < < unitY ;
//return;
}
const float xLow = std : : floor ( - unitX ) ;
const float xHigh = std : : ceil ( unitX ) ;
const float yLow = std : : floor ( - unitY ) ;
const float yHigh = std : : ceil ( unitY ) ;
const float alpha = 0.25f ;
const int xsteps = std : : ceil ( std : : abs ( xHigh - xLow ) / GridStep ) ; //Need these because sometimes the float value never reaches the max and it gets stuck in an infinite loop.
const int ysteps = std : : ceil ( std : : abs ( yHigh - yLow ) / GridStep ) ;
Affine2D < T > temp ;
m_GL - > glLineWidth ( 1.0f * m_GL - > devicePixelRatioF ( ) ) ;
# ifndef USE_GLSL
m4T mat = ( temp * scale ) . ToMat4RowMajor ( ) ;
m_GL - > glPushMatrix ( ) ;
m_GL - > glLoadIdentity ( ) ;
MultMatrix ( mat ) ;
m_GL - > glBegin ( GL_LINES ) ;
m_GL - > glColor4f ( 0.5f , 0.5f , 0.5f , alpha ) ;
for ( float fx = xLow , i = 0 ; fx < = xHigh & & i < xsteps ; fx + = GridStep , i + + )
{
m_GL - > glVertex2f ( fx , yLow ) ;
m_GL - > glVertex2f ( fx , yHigh ) ;
}
for ( float fy = yLow , i = 0 ; fy < yHigh & & i < ysteps ; fy + = GridStep , i + + )
{
m_GL - > glVertex2f ( xLow , fy ) ;
m_GL - > glVertex2f ( xHigh , fy ) ;
}
m_GL - > glColor4f ( 1.0f , 0.0f , 0.0f , alpha ) ;
m_GL - > glVertex2f ( 0.0f , 0.0f ) ;
m_GL - > glVertex2f ( xHigh , 0.0f ) ;
m_GL - > glColor4f ( 0.5f , 0.0f , 0.0f , alpha ) ;
m_GL - > glVertex2f ( 0.0f , 0.0f ) ;
m_GL - > glVertex2f ( xLow , 0.0f ) ;
m_GL - > glColor4f ( 0.0f , 1.0f , 0.0f , alpha ) ;
m_GL - > glVertex2f ( 0.0f , 0.0f ) ;
m_GL - > glVertex2f ( 0.0f , yHigh ) ;
m_GL - > glColor4f ( 0.0f , 0.5f , 0.0f , alpha ) ;
m_GL - > glVertex2f ( 0.0f , 0.0f ) ;
m_GL - > glVertex2f ( 0.0f , yLow ) ;
m_GL - > glColor4f ( 0.0f , 0.0f , 0.0f , 1.0f ) ;
m_GL - > glEnd ( ) ;
m_GL - > glPopMatrix ( ) ;
# else
const m4T mat = ( temp * scale ) . ToMat4ColMajor ( ) ;
glm : : tmat4x4 < float , glm : : defaultp > tempmat4 = mat ;
m_GL - > m_ModelViewMatrix = QMatrix4x4 ( glm : : value_ptr ( tempmat4 ) ) ;
m_GL - > glLineWidth ( 1.0f * m_GL - > devicePixelRatioF ( ) ) ;
m_Verts . clear ( ) ;
for ( float fx = xLow , i = 0 ; fx < = xHigh & & i < xsteps ; fx + = GridStep , i + + )
{
m_Verts . push_back ( fx ) ;
m_Verts . push_back ( yLow ) ;
m_Verts . push_back ( fx ) ;
m_Verts . push_back ( yHigh ) ;
}
for ( float fy = yLow , i = 0 ; fy < yHigh & & i < ysteps ; fy + = GridStep , i + + )
{
m_Verts . push_back ( xLow ) ;
m_Verts . push_back ( fy ) ;
m_Verts . push_back ( xHigh ) ;
m_Verts . push_back ( fy ) ;
}
QVector4D col ( 0.5f , 0.5f , 0.5f , alpha ) ;
m_GL - > DrawPointOrLine ( col , m_Verts , GL_LINES ) ;
m_Verts . clear ( ) ;
m_Verts . push_back ( 0.0f ) ;
m_Verts . push_back ( 0.0f ) ;
m_Verts . push_back ( xHigh ) ;
m_Verts . push_back ( 0.0f ) ;
col = QVector4D ( 1.0f , 0.0f , 0.0f , alpha ) ;
m_GL - > DrawPointOrLine ( col , m_Verts , GL_LINES ) ;
m_Verts . clear ( ) ;
m_Verts . push_back ( 0.0f ) ;
m_Verts . push_back ( 0.0f ) ;
m_Verts . push_back ( xLow ) ;
m_Verts . push_back ( 0.0f ) ;
col = QVector4D ( 0.5f , 0.0f , 0.0f , alpha ) ;
m_GL - > DrawPointOrLine ( col , m_Verts , GL_LINES ) ;
m_Verts . clear ( ) ;
m_Verts . push_back ( 0.0f ) ;
m_Verts . push_back ( 0.0f ) ;
m_Verts . push_back ( 0.0f ) ;
m_Verts . push_back ( yHigh ) ;
col = QVector4D ( 0.0f , 1.0f , 0.0f , alpha ) ;
m_GL - > DrawPointOrLine ( col , m_Verts , GL_LINES ) ;
m_Verts . clear ( ) ;
m_Verts . push_back ( 0.0f ) ;
m_Verts . push_back ( 0.0f ) ;
m_Verts . push_back ( 0.0f ) ;
m_Verts . push_back ( yLow ) ;
col = QVector4D ( 0.0f , 0.5f , 0.0f , alpha ) ;
m_GL - > DrawPointOrLine ( col , m_Verts , GL_LINES ) ;
# endif
}
/// <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>
/// <param name="hovered">True if the xform is being hovered over (draw tansparent disc), else false (no disc).</param>
template < typename T >
void GLEmberController < T > : : DrawAffine ( const Xform < T > * xform , bool pre , bool selected , bool hovered )
{
const auto ember = m_FractoriumEmberController - > CurrentEmber ( ) ;
const auto final = ember - > IsFinalXform ( xform ) ;
const auto index = ember - > GetXformIndex ( xform ) ;
const auto size = ember - > m_Palette . m_Entries . size ( ) ;
const auto color = ember - > m_Palette . m_Entries [ Clamp < T > ( xform - > m_ColorX * size , 0 , size - 1 ) ] ;
const auto & affine = pre ? xform - > m_Affine : xform - > m_Post ;
# ifndef USE_GLSL
//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.
m4T mat = ( affine * m_FractoriumEmberController - > AffineScaleCurrentToLocked ( ) ) . ToMat4RowMajor ( ) ;
m_GL - > glPushMatrix ( ) ;
m_GL - > glLoadIdentity ( ) ;
MultMatrix ( mat ) ;
//QueryMatrices(true);
m_GL - > glLineWidth ( 3.0f * m_GL - > devicePixelRatioF ( ) ) ; //One 3px wide, colored black, except green on x axis for post affine.
m_GL - > DrawAffineHelper ( index , selected , hovered , pre , final , true ) ;
m_GL - > glLineWidth ( 1.0f * m_GL - > devicePixelRatioF ( ) ) ; //Again 1px wide, colored white, to give a white middle with black outline effect.
m_GL - > DrawAffineHelper ( index , selected , hovered , pre , final , false ) ;
m_GL - > glPointSize ( 5.0f * m_GL - > devicePixelRatioF ( ) ) ; //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 * m_GL - > devicePixelRatioF ( ) ) ; //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 * m_GL - > devicePixelRatioF ( ) ) ; //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 - > glColor4f ( 0.0f , 0.0f , 0.0f , 1.0f ) ;
m_GL - > glEnd ( ) ;
m_GL - > glPopMatrix ( ) ;
# else
const m4T mat = ( affine * m_FractoriumEmberController - > AffineScaleCurrentToLocked ( ) ) . ToMat4ColMajor ( ) ;
glm : : tmat4x4 < float , glm : : defaultp > tempmat4 = mat ;
m_GL - > m_ModelViewMatrix = QMatrix4x4 ( glm : : value_ptr ( tempmat4 ) ) ;
m_GL - > DrawAffineHelper ( index , 1.0 , 3.0 , selected , hovered , pre , final , true ) ; //Circle line width is thinner than the line width for the axes, just to distinguish it.
m_GL - > DrawAffineHelper ( index , 0.5 , 2.0 , selected , hovered , pre , final , false ) ;
QVector4D col ( 0.0f , 0.0f , 0.0f , selected ? 1.0f : 0.5f ) ;
m_Verts . clear ( ) ;
m_Verts . push_back ( 0.0f ) ;
m_Verts . push_back ( 0.0f ) ;
m_Verts . push_back ( 1.0f ) ;
m_Verts . push_back ( 0.0f ) ;
m_Verts . push_back ( 0.0f ) ;
m_Verts . push_back ( 1.0f ) ;
m_GL - > DrawPointOrLine ( col , m_Verts , GL_POINTS , ! pre , 6.0f ) ; //Three black points, one in the center and two on the circle. Drawn big 5px first to give a black outline.
//Somewhat of a hack, since it's drawing over the Y axis line it just drew.
m_GL - > glLineWidth ( 2.0f * m_GL - > devicePixelRatioF ( ) ) ; //Draw lines again for y axis only, using the color of the selected xform.
m_Verts . clear ( ) ;
m_Verts . push_back ( 0.0f ) ;
m_Verts . push_back ( 0.0f ) ;
m_Verts . push_back ( 0.0f ) ;
m_Verts . push_back ( 1.0f ) ;
col = QVector4D ( color . r , color . g , color . b , 1.0f ) ;
m_GL - > DrawPointOrLine ( col , m_Verts , GL_LINES , ! pre ) ;
//Line from x to y in the color of the xform combo, thinner and solid with no background to somewhat distinguish it.
m_GL - > glLineWidth ( 0.5f * m_GL - > devicePixelRatioF ( ) ) ;
m_Verts . clear ( ) ;
m_Verts . push_back ( 0 ) ;
m_Verts . push_back ( 1 ) ;
m_Verts . push_back ( 1 ) ;
m_Verts . push_back ( 0 ) ;
auto qcol = final ? m_Fractorium - > m_FinalXformComboColor : m_Fractorium - > m_XformComboColors [ index % XFORM_COLOR_COUNT ] ;
m_GL - > DrawPointOrLine ( QVector4D ( qcol . redF ( ) , qcol . greenF ( ) , qcol . blueF ( ) , 1.0f ) , m_Verts , GL_LINES , ! pre ) ;
//
m_GL - > glLineWidth ( 2.0f * m_GL - > devicePixelRatioF ( ) ) ;
m_Verts . clear ( ) ;
m_Verts . push_back ( 0.0f ) ; //Center.
m_Verts . push_back ( 0.0f ) ;
col = QVector4D ( 1.0f , 1.0f , 1.0f , selected ? 1.0f : 0.5f ) ;
m_GL - > DrawPointOrLine ( col , m_Verts , GL_POINTS , false , 5.0f ) ; //Draw smaller white point, to give a black outline effect.
m_Verts . clear ( ) ;
m_Verts . push_back ( 1.0f ) ; //X axis.
m_Verts . push_back ( 0.0f ) ;
col = QVector4D ( 0.0f , 1.0f , 0.0f , selected ? 1.0f : 0.5f ) ;
m_GL - > DrawPointOrLine ( col , m_Verts , GL_POINTS , false , 5.0f ) ; //Draw smaller green point, to give a black outline effect.
m_Verts . clear ( ) ;
m_Verts . push_back ( 0.0f ) ; //Y axis.
m_Verts . push_back ( 1.0f ) ;
col = QVector4D ( 1.0f , 0.0f , 1.0f , selected ? 1.0f : 0.5f ) ;
m_GL - > DrawPointOrLine ( col , m_Verts , GL_POINTS , false , 5.0f ) ; //Draw smaller purple point, to give a black outline effect.
m_GL - > m_ModelViewMatrix . setToIdentity ( ) ;
# endif
}
/// <summary>
/// Draw the axes, and optionally the surrounding circle
/// of an affine transform.
/// </summary>
/// <param name="index"></param>
/// <param name="circleWidth"></param>
/// <param name="lineWidth"></param>
/// <param name="selected">True if selected (draw enclosing circle), else false (only draw axes).</param>
/// <param name="hovered">True if the xform is being hovered over (draw tansparent disc), else false (no disc).</param>
/// <param name="pre"></param>
/// <param name="final"></param>
/// <param name="background"></param>
void GLWidget : : DrawAffineHelper ( int index , float circleWidth , float lineWidth , bool selected , bool hovered , bool pre , bool final , bool background )
{
float px = 1.0f ;
float py = 0.0f ;
const auto col = final ? m_Fractorium - > m_FinalXformComboColor : m_Fractorium - > m_XformComboColors [ index % XFORM_COLOR_COUNT ] ;
# ifndef USE_GLSL
glBegin ( GL_LINES ) ;
//Circle part.
if ( ! background )
{
glColor4f ( col . redF ( ) , col . greenF ( ) , col . blueF ( ) , 1.0f ) ; //Draw pre affine transform with white.
}
else
{
glColor4f ( 0.0f , 0.0f , 0.0f , 1.0f ) ; //Draw pre affine transform outline with white.
}
if ( selected )
{
for ( size_t i = 1 ; i < = 64 ; i + + ) //The circle.
{
float theta = float ( M_PI ) * 2.0f * float ( i % 64 ) / 64.0f ;
float fx = std : : cos ( theta ) ;
float fy = std : : sin ( theta ) ;
glVertex2f ( px , py ) ;
glVertex2f ( fx , fy ) ;
px = fx ;
py = fy ;
}
}
//Lines from center to circle.
if ( ! background )
{
glColor4f ( col . redF ( ) , col . greenF ( ) , col . blueF ( ) , 1.0f ) ;
}
else
{
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.
}
//The lines from the center to the circle.
glVertex2f ( 0.0f , 0.0f ) ; //X axis.
glVertex2f ( 1.0f , 0.0f ) ;
if ( background )
glColor4f ( 0.0f , 0.0f , 0.0f , 1.0f ) ;
glVertex2f ( 0.0f , 0.0f ) ; //Y axis.
glVertex2f ( 0.0f , 1.0f ) ;
glEnd ( ) ;
# else
QVector4D color ;
//Circle part.
if ( ! background )
{
color = QVector4D ( col . redF ( ) , col . greenF ( ) , col . blueF ( ) , hovered ? 0.25f : 1.0f ) ; //Draw pre affine transform with white.
}
else
{
color = QVector4D ( 0.0f , 0.0f , 0.0f , hovered ? 0.25f : 1.0f ) ; //Draw pre affine transform outline with white.
}
m_Verts . clear ( ) ;
glLineWidth ( circleWidth * devicePixelRatioF ( ) ) ; //One thinner, colored black, except green on x axis for post affine.
if ( selected | | hovered )
{
for ( size_t i = 1 ; i < = 64 ; i + + ) //The circle.
{
const float theta = static_cast < float > ( M_PI ) * 2.0f * static_cast < float > ( i % 64 ) / 64.0f ;
const float fx = std : : cos ( theta ) ;
const float fy = std : : sin ( theta ) ;
m_Verts . push_back ( fx ) ;
m_Verts . push_back ( fy ) ;
}
DrawPointOrLine ( color , m_Verts , hovered ? GL_TRIANGLE_FAN : GL_LINE_LOOP , ! pre ) ;
}
glLineWidth ( lineWidth * devicePixelRatioF ( ) ) ; //One thicker, colored black, except green on x axis for post affine.
//Lines from center to circle.
if ( ! background )
{
color = QVector4D ( col . redF ( ) , col . greenF ( ) , col . blueF ( ) , 1.0f ) ;
}
else
{
if ( pre )
color = QVector4D ( 0.0f , 0.0f , 0.0f , 1.0f ) ; //Draw pre affine transform outline with white.
else
color = QVector4D ( 0.0f , 0.75f , 0.0f , 1.0f ) ; //Draw post affine transform outline with green.
}
//The lines from the center to the circle.
m_Verts . clear ( ) ;
m_Verts . push_back ( 0 ) ; //X axis.
m_Verts . push_back ( 0 ) ;
m_Verts . push_back ( 1 ) ;
m_Verts . push_back ( 0 ) ;
DrawPointOrLine ( color , m_Verts , GL_LINES , ! pre ) ;
if ( background )
color = QVector4D ( 0.0f , 0.0f , 0.0f , 1.0f ) ;
m_Verts . clear ( ) ;
m_Verts . push_back ( 0 ) ; //Y axis.
m_Verts . push_back ( 0 ) ;
m_Verts . push_back ( 0 ) ;
m_Verts . push_back ( 1 ) ;
DrawPointOrLine ( color , m_Verts , GL_LINES , ! pre ) ;
# endif
}
/// <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 ( const v3T & glCoords )
{
const bool pre = m_Fractorium - > DrawPreAffines ( ) ;
const bool post = m_Fractorium - > DrawPostAffines ( ) ;
int i = 0 , bestIndex = - 1 ;
T bestDist = 10 ;
const auto ember = m_FractoriumEmberController - > CurrentEmber ( ) ;
m_HoverType = eHoverType : : HoverNone ;
if ( m_Fractorium - > DrawXforms ( ) ) //Don't bother checking anything if the user wants to see no xforms.
{
const bool forceFinal = m_Fractorium - > HaveFinal ( ) ;
//If there's a selected/current xform, check it first so it gets precedence over the others.
if ( m_SelectedXform )
{
//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.
const bool isSel = m_Fractorium - > IsXformSelected ( ember - > GetTotalXformIndex ( m_SelectedXform ) ) ;
const bool checkPre = pre & & ( m_Fractorium - > DrawAllPre ( ) | | ( m_Fractorium - > DrawSelectedPre ( ) & & isSel ) ) ;
const bool checkPost = post & & ( m_Fractorium - > DrawAllPost ( ) | | ( m_Fractorium - > DrawSelectedPost ( ) & & isSel ) ) ;
if ( CheckXformHover ( m_SelectedXform , glCoords , bestDist , checkPre , checkPost ) )
{
m_HoverXform = m_SelectedXform ;
bestIndex = static_cast < int > ( ember - > GetTotalXformIndex ( m_SelectedXform , forceFinal ) ) ;
}
}
//Check all xforms.
while ( const auto xform = ember - > GetTotalXform ( i , forceFinal ) )
{
const bool isSel = m_Fractorium - > IsXformSelected ( i ) ;
if ( pre )
{
const bool checkPre = m_Fractorium - > DrawAllPre ( ) | | ( m_Fractorium - > DrawSelectedPre ( ) & & isSel ) | | ( m_SelectedXform = = xform ) ;
if ( checkPre ) //Only check pre affine if they are shown.
{
if ( CheckXformHover ( xform , glCoords , bestDist , true , false ) )
{
m_HoverXform = xform ;
bestIndex = i ;
}
}
}
if ( post )
{
const bool checkPost = m_Fractorium - > DrawAllPost ( ) | | ( m_Fractorium - > DrawSelectedPost ( ) & & isSel ) | | ( m_SelectedXform = = xform ) ;
if ( checkPost )
{
if ( CheckXformHover ( xform , glCoords , bestDist , false , true ) )
{
m_HoverXform = xform ;
bestIndex = i ;
}
}
}
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 ( const Xform < T > * xform , const v3T & glCoords , T & bestDist , bool pre , bool post )
{
bool preFound = false , postFound = false ;
T dist = 0 ;
const auto scale = m_FractoriumEmberController - > AffineScaleCurrentToLocked ( ) ;
v3T pos ;
if ( pre )
{
const auto affineScaled = xform - > m_Affine * scale ;
const v3T translation ( affineScaled . C ( ) , affineScaled . F ( ) , 0 ) ;
const v3T transScreen = glm : : project ( translation , m_Modelview , m_Projection , m_Viewport ) ;
const v3T xAxis ( affineScaled . A ( ) , affineScaled . D ( ) , 0 ) ;
const v3T xAxisScreen = glm : : project ( translation + xAxis , m_Modelview , m_Projection , m_Viewport ) ;
const v3T yAxis ( affineScaled . B ( ) , affineScaled . E ( ) , 0 ) ;
const v3T yAxisScreen = glm : : project ( translation + yAxis , m_Modelview , m_Projection , m_Viewport ) ;
pos = translation ;
dist = glm : : distance ( glCoords , transScreen ) ;
if ( dist < bestDist )
{
bestDist = dist ;
m_HoverType = eHoverType : : HoverTranslation ;
m_HoverHandlePos = pos ;
preFound = true ;
}
pos = translation + xAxis ;
dist = glm : : distance ( glCoords , xAxisScreen ) ;
if ( dist < bestDist )
{
bestDist = dist ;
m_HoverType = eHoverType : : HoverXAxis ;
m_HoverHandlePos = pos ;
preFound = true ;
}
pos = translation + yAxis ;
dist = glm : : distance ( glCoords , yAxisScreen ) ;
if ( dist < bestDist )
{
bestDist = dist ;
m_HoverType = eHoverType : : HoverYAxis ;
m_HoverHandlePos = pos ;
preFound = true ;
}
if ( preFound )
m_AffineType = eAffineType : : AffinePre ;
}
if ( post )
{
const auto affineScaled = xform - > m_Post * scale ;
const v3T translation ( affineScaled . C ( ) , affineScaled . F ( ) , 0 ) ;
const v3T transScreen = glm : : project ( translation , m_Modelview , m_Projection , m_Viewport ) ;
const v3T xAxis ( affineScaled . A ( ) , affineScaled . D ( ) , 0 ) ;
const v3T xAxisScreen = glm : : project ( translation + xAxis , m_Modelview , m_Projection , m_Viewport ) ;
const v3T yAxis ( affineScaled . B ( ) , affineScaled . E ( ) , 0 ) ;
const v3T yAxisScreen = glm : : project ( translation + yAxis , m_Modelview , m_Projection , m_Viewport ) ;
pos = translation ;
dist = glm : : distance ( glCoords , transScreen ) ;
if ( dist < bestDist )
{
bestDist = dist ;
m_HoverType = eHoverType : : HoverTranslation ;
m_HoverHandlePos = pos ;
postFound = true ;
}
pos = translation + xAxis ;
dist = glm : : distance ( glCoords , xAxisScreen ) ;
if ( dist < bestDist )
{
bestDist = dist ;
m_HoverType = eHoverType : : HoverXAxis ;
m_HoverHandlePos = pos ;
postFound = true ;
}
pos = translation + yAxis ;
dist = glm : : distance ( glCoords , yAxisScreen ) ;
if ( dist < bestDist )
{
bestDist = dist ;
m_HoverType = eHoverType : : HoverYAxis ;
m_HoverHandlePos = pos ;
postFound = true ;
}
if ( postFound )
m_AffineType = eAffineType : : AffinePost ;
}
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 only.
/// Local Pivot:
/// Shift: Scale and optionally Rotate about affine center.
/// Alt: Rotate single axis about affine center.
/// Shift + Alt: Free transform.
/// Control: Rotate, snapping to grid.
/// Control + Shift: Scale and optionally Rotate, snapping to grid.
/// Control + Alt: Rotate single axis about affine center, snapping to grid.
/// Control + Shift + Alt: Free transform, 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 >
void GLEmberController < T > : : CalcDragXAxis ( )
{
const T affineToWorldScale = static_cast < T > ( m_FractoriumEmberController - > AffineScaleLockedToCurrent ( ) ) ;
const T worldToAffineScale = static_cast < T > ( m_FractoriumEmberController - > AffineScaleCurrentToLocked ( ) ) ;
const bool pre = m_AffineType = = eAffineType : : AffinePre ;
const bool worldPivotShiftAlt = ! m_Fractorium - > LocalPivot ( ) & & GetShift ( ) & & GetAlt ( ) ;
const auto worldRelAxisStartScaled = ( v2T ( m_HoverHandlePos ) * affineToWorldScale ) - m_DragSrcTransform . O ( ) ; //World axis start position, relative, scaled by the zoom.
const T startAngle = std : : atan2 ( worldRelAxisStartScaled . y , worldRelAxisStartScaled . x ) ;
v3T relScaled = ( m_MouseWorldPos * affineToWorldScale ) - v3T ( m_DragSrcTransform . O ( ) , 0 ) ;
if ( ! GetShift ( ) )
{
if ( GetControl ( ) )
{
relScaled = SnapToNormalizedAngle ( relScaled , 24u ) ; //relScaled is using the relative scaled position of the axis.
}
const T endAngle = std : : atan2 ( relScaled . y , relScaled . x ) ;
const T angle = startAngle - endAngle ;
m_FractoriumEmberController - > UpdateXform ( [ & ] ( Xform < T > * xform , size_t xfindex , size_t selIndex )
{
auto it = m_DragSrcPreTransforms . find ( xfindex ) ;
if ( it ! = m_DragSrcPreTransforms . end ( ) )
{
auto src = it - > second ;
auto & affine = xform - > m_Affine ;
if ( GetAlt ( ) )
{
src . Rotate ( angle ) ;
affine . X ( src . X ( ) ) ;
}
else
{
src . Rotate ( angle ) ;
affine = src ;
}
}
it = m_DragSrcPostTransforms . find ( xfindex ) ;
if ( it ! = m_DragSrcPostTransforms . end ( ) )
{
auto src = it - > second ;
auto & affine = xform - > m_Post ;
if ( GetAlt ( ) )
{
src . Rotate ( angle ) ;
affine . X ( src . X ( ) ) ;
}
else
{
src . Rotate ( angle ) ;
affine = src ;
}
}
const auto & affine = pre ? xform - > m_Affine : xform - > m_Post ;
if ( xform = = m_FractoriumEmberController - > CurrentXform ( ) )
m_DragHandlePos = v3T ( ( affine . O ( ) + affine . X ( ) ) * worldToAffineScale , 0 ) ;
} , eXformUpdate : : UPDATE_SELECTED , false ) ; //Calling code will update renderer.
}
else
{
auto origmag = Zeps ( glm : : length ( m_DragSrcTransform . X ( ) ) ) ; //Magnitude of original dragged axis before it was dragged.
if ( GetControl ( ) )
{
relScaled = SnapToGrid ( relScaled ) ;
}
const auto newmag = glm : : length ( relScaled ) ;
const auto newprc = newmag / origmag ;
const T endAngle = std : : atan2 ( relScaled . y , relScaled . x ) ;
const T angle = startAngle - endAngle ;
m_FractoriumEmberController - > UpdateXform ( [ & ] ( Xform < T > * xform , size_t xfindex , size_t selIndex )
{
auto it = m_DragSrcPreTransforms . find ( xfindex ) ;
if ( it ! = m_DragSrcPreTransforms . end ( ) )
{
auto src = it - > second ;
auto & affine = xform - > m_Affine ;
if ( worldPivotShiftAlt )
{
src . X ( src . O ( ) + src . X ( ) ) ;
src . O ( v2T ( 0 ) ) ;
src . Rotate ( angle ) ;
affine . X ( src . X ( ) - affine . O ( ) ) ;
}
else if ( GetAlt ( ) )
{
affine . X ( v2T ( relScaled ) ) ; //Absolute, not ratio.
}
else
{
src . ScaleXY ( newprc ) ;
if ( m_Fractorium - > m_Settings - > RotateAndScale ( ) )
src . Rotate ( angle ) ;
affine = src ;
}
}
it = m_DragSrcPostTransforms . find ( xfindex ) ;
if ( it ! = m_DragSrcPostTransforms . end ( ) )
{
auto src = it - > second ;
auto & affine = xform - > m_Post ;
if ( worldPivotShiftAlt )
{
src . X ( src . O ( ) + src . X ( ) ) ;
src . O ( v2T ( 0 ) ) ;
src . Rotate ( angle ) ;
affine . X ( src . X ( ) - affine . O ( ) ) ;
}
else if ( GetAlt ( ) )
{
affine . X ( v2T ( relScaled ) ) ; //Absolute, not ratio.
}
else
{
src . ScaleXY ( newprc ) ;
if ( m_Fractorium - > m_Settings - > RotateAndScale ( ) )
src . Rotate ( angle ) ;
affine = src ;
}
}
const auto & affine = pre ? xform - > m_Affine : xform - > m_Post ;
if ( xform = = m_FractoriumEmberController - > CurrentXform ( ) )
m_DragHandlePos = v3T ( ( affine . O ( ) + affine . X ( ) ) * worldToAffineScale , 0 ) ;
} , eXformUpdate : : UPDATE_SELECTED , false ) ;
}
}
/// <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 only.
/// Local Pivot:
/// Shift: Scale and optionally Rotate about affine center.
/// Alt: Rotate single axis about affine center.
/// Shift + Alt: Free transform.
/// Control: Rotate, snapping to grid.
/// Control + Shift: Scale and optionally Rotate, snapping to grid.
/// Control + Alt: Rotate single axis about affine center, snapping to grid.
/// Control + Shift + Alt: Free transform, 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 >
void GLEmberController < T > : : CalcDragYAxis ( )
{
const T affineToWorldScale = static_cast < T > ( m_FractoriumEmberController - > AffineScaleLockedToCurrent ( ) ) ;
const T worldToAffineScale = static_cast < T > ( m_FractoriumEmberController - > AffineScaleCurrentToLocked ( ) ) ;
const bool pre = m_AffineType = = eAffineType : : AffinePre ;
const bool worldPivotShiftAlt = ! m_Fractorium - > LocalPivot ( ) & & GetShift ( ) & & GetAlt ( ) ;
const auto worldRelAxisStartScaled = ( v2T ( m_HoverHandlePos ) * affineToWorldScale ) - m_DragSrcTransform . O ( ) ; //World axis start position, relative, scaled by the zoom.
const T startAngle = std : : atan2 ( worldRelAxisStartScaled . y , worldRelAxisStartScaled . x ) ;
v3T relScaled = ( m_MouseWorldPos * affineToWorldScale ) - v3T ( m_DragSrcTransform . O ( ) , 0 ) ;
if ( ! GetShift ( ) )
{
if ( GetControl ( ) )
{
relScaled = SnapToNormalizedAngle ( relScaled , 24u ) ; //relScaled is using the relative scaled position of the axis.
}
const T endAngle = std : : atan2 ( relScaled . y , relScaled . x ) ;
const T angle = startAngle - endAngle ;
m_FractoriumEmberController - > UpdateXform ( [ & ] ( Xform < T > * xform , size_t xfindex , size_t selIndex )
{
auto it = m_DragSrcPreTransforms . find ( xfindex ) ;
if ( it ! = m_DragSrcPreTransforms . end ( ) )
{
auto src = it - > second ;
auto & affine = xform - > m_Affine ;
if ( GetAlt ( ) )
{
src . Rotate ( angle ) ;
affine . Y ( src . Y ( ) ) ;
}
else
{
src . Rotate ( angle ) ;
affine = src ;
}
}
it = m_DragSrcPostTransforms . find ( xfindex ) ;
if ( it ! = m_DragSrcPostTransforms . end ( ) )
{
auto src = it - > second ;
auto & affine = xform - > m_Post ;
if ( GetAlt ( ) )
{
src . Rotate ( angle ) ;
affine . Y ( src . Y ( ) ) ;
}
else
{
src . Rotate ( angle ) ;
affine = src ;
}
}
const auto & affine = pre ? xform - > m_Affine : xform - > m_Post ;
if ( xform = = m_FractoriumEmberController - > CurrentXform ( ) )
m_DragHandlePos = v3T ( ( affine . O ( ) + affine . Y ( ) ) * worldToAffineScale , 0 ) ;
} , eXformUpdate : : UPDATE_SELECTED , false ) ; //Calling code will update renderer.
}
else
{
const auto origmag = Zeps ( glm : : length ( m_DragSrcTransform . Y ( ) ) ) ; //Magnitude of original dragged axis before it was dragged.
if ( GetControl ( ) )
{
relScaled = SnapToGrid ( relScaled ) ;
}
const auto newmag = glm : : length ( relScaled ) ;
const auto newprc = newmag / origmag ;
const T endAngle = std : : atan2 ( relScaled . y , relScaled . x ) ;
const T angle = startAngle - endAngle ;
m_FractoriumEmberController - > UpdateXform ( [ & ] ( Xform < T > * xform , size_t xfindex , size_t selIndex )
{
auto it = m_DragSrcPreTransforms . find ( xfindex ) ;
if ( it ! = m_DragSrcPreTransforms . end ( ) )
{
auto src = it - > second ;
auto & affine = xform - > m_Affine ;
if ( worldPivotShiftAlt )
{
src . Y ( src . O ( ) + src . Y ( ) ) ;
src . O ( v2T ( 0 ) ) ;
src . Rotate ( angle ) ;
affine . Y ( src . Y ( ) - affine . O ( ) ) ;
}
else if ( GetAlt ( ) )
{
affine . Y ( v2T ( relScaled ) ) ; //Absolute, not ratio.
}
else
{
src . ScaleXY ( newprc ) ;
if ( m_Fractorium - > m_Settings - > RotateAndScale ( ) )
src . Rotate ( angle ) ;
affine = src ;
}
}
it = m_DragSrcPostTransforms . find ( xfindex ) ;
if ( it ! = m_DragSrcPostTransforms . end ( ) )
{
auto src = it - > second ;
auto & affine = xform - > m_Post ;
if ( worldPivotShiftAlt )
{
src . Y ( src . O ( ) + src . Y ( ) ) ;
src . O ( v2T ( 0 ) ) ;
src . Rotate ( angle ) ;
affine . Y ( src . Y ( ) - affine . O ( ) ) ;
}
else if ( GetAlt ( ) )
{
affine . Y ( v2T ( relScaled ) ) ; //Absolute, not ratio.
}
else
{
src . ScaleXY ( newprc ) ;
if ( m_Fractorium - > m_Settings - > RotateAndScale ( ) )
src . Rotate ( angle ) ;
affine = src ;
}
}
const auto & affine = pre ? xform - > m_Affine : xform - > m_Post ;
if ( xform = = m_FractoriumEmberController - > CurrentXform ( ) )
m_DragHandlePos = v3T ( ( affine . O ( ) + affine . Y ( ) ) * worldToAffineScale , 0 ) ;
} , eXformUpdate : : UPDATE_SELECTED , false ) ;
}
}
/// <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 >
void GLEmberController < T > : : CalcDragTranslation ( )
{
const T affineToWorldScale = static_cast < T > ( m_FractoriumEmberController - > AffineScaleLockedToCurrent ( ) ) ;
const T worldToAffineScale = static_cast < T > ( m_FractoriumEmberController - > AffineScaleCurrentToLocked ( ) ) ;
const bool worldPivotShift = ! m_Fractorium - > LocalPivot ( ) & & GetShift ( ) ;
const bool pre = m_AffineType = = eAffineType : : AffinePre ;
if ( GetShift ( ) )
{
const v3T snapped = GetControl ( ) ? SnapToNormalizedAngle ( m_MouseWorldPos , 24 ) : m_MouseWorldPos ;
const T startAngle = std : : atan2 ( m_DragSrcTransform . O ( ) . y , m_DragSrcTransform . O ( ) . x ) ;
const T endAngle = std : : atan2 ( snapped . y , snapped . x ) ;
const T angle = startAngle - endAngle ;
m_FractoriumEmberController - > UpdateXform ( [ & ] ( Xform < T > * xform , size_t xfindex , size_t selIndex )
{
auto it = m_DragSrcPreTransforms . find ( xfindex ) ;
if ( it ! = m_DragSrcPreTransforms . end ( ) )
{
auto src = it - > second ;
auto & affine = xform - > m_Affine ;
src . RotateTrans ( angle ) ;
if ( worldPivotShift )
{
src . Rotate ( angle ) ;
affine . X ( src . X ( ) ) ;
affine . Y ( src . Y ( ) ) ;
}
affine . O ( src . O ( ) ) ;
}
it = m_DragSrcPostTransforms . find ( xfindex ) ;
if ( it ! = m_DragSrcPostTransforms . end ( ) )
{
auto src = it - > second ;
auto & affine = xform - > m_Post ;
src . RotateTrans ( angle ) ;
if ( worldPivotShift )
{
src . Rotate ( angle ) ;
affine . X ( src . X ( ) ) ;
affine . Y ( src . Y ( ) ) ;
}
affine . O ( src . O ( ) ) ;
}
const auto & affine = pre ? xform - > m_Affine : xform - > m_Post ;
if ( xform = = m_FractoriumEmberController - > CurrentXform ( ) )
m_DragHandlePos = v3T ( affine . O ( ) , 0 ) * worldToAffineScale ;
} , eXformUpdate : : UPDATE_SELECTED , false ) ; //Calling code will update renderer.
}
else
{
const auto diff = m_MouseWorldPos - m_MouseDownWorldPos ;
if ( GetControl ( ) )
{
m_FractoriumEmberController - > UpdateXform ( [ & ] ( Xform < T > * xform , size_t xfindex , size_t selIndex )
{
auto it = m_DragSrcPreTransforms . find ( xfindex ) ;
if ( it ! = m_DragSrcPreTransforms . end ( ) )
{
const auto & src = it - > second ;
auto & affine = xform - > m_Affine ;
const auto offset = src . O ( ) + ( affineToWorldScale * v2T ( diff ) ) ;
const auto snapped = SnapToGrid ( offset ) ;
affine . O ( v2T ( snapped . x , snapped . y ) ) ;
}
it = m_DragSrcPostTransforms . find ( xfindex ) ;
if ( it ! = m_DragSrcPostTransforms . end ( ) )
{
auto & src = it - > second ;
auto & affine = xform - > m_Post ;
auto offset = src . O ( ) + ( affineToWorldScale * v2T ( diff ) ) ;
auto snapped = SnapToGrid ( offset ) ;
affine . O ( v2T ( snapped . x , snapped . y ) ) ;
}
auto & affine = pre ? xform - > m_Affine : xform - > m_Post ;
if ( xform = = m_FractoriumEmberController - > CurrentXform ( ) )
m_DragHandlePos = v3T ( affine . O ( ) , 0 ) * worldToAffineScale ;
} , eXformUpdate : : UPDATE_CURRENT_AND_SELECTED , false ) ;
}
else
{
m_FractoriumEmberController - > UpdateXform ( [ & ] ( Xform < T > * xform , size_t xfindex , size_t selIndex )
{
auto it = m_DragSrcPreTransforms . find ( xfindex ) ;
if ( it ! = m_DragSrcPreTransforms . end ( ) )
{
auto & src = it - > second ;
auto & affine = xform - > m_Affine ;
affine . O ( src . O ( ) + ( affineToWorldScale * v2T ( diff ) ) ) ;
}
it = m_DragSrcPostTransforms . find ( xfindex ) ;
if ( it ! = m_DragSrcPostTransforms . end ( ) )
{
auto & src = it - > second ;
auto & affine = xform - > m_Post ;
affine . O ( src . O ( ) + ( affineToWorldScale * v2T ( diff ) ) ) ;
}
auto & affine = pre ? xform - > m_Affine : xform - > m_Post ;
if ( xform = = m_FractoriumEmberController - > CurrentXform ( ) )
m_DragHandlePos = v3T ( affine . O ( ) , 0 ) * worldToAffineScale ;
} , eXformUpdate : : UPDATE_SELECTED , false ) ;
}
}
}
/// <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 ( ) ;
return nullptr ;
}
template class GLEmberController < float > ;
# ifdef DO_DOUBLE
template class GLEmberController < double > ;
# endif