Switched from euler angle rotation to matrix rotation. Gimbal lock is now broken.

Wed, 08 Feb 2017 16:25:06 +0200

author
Teemu Piippo <teemu@hecknology.net>
date
Wed, 08 Feb 2017 16:25:06 +0200
changeset 1092
7a50a7f6b492
parent 1091
4a754362f660
child 1093
3fb96ed48e49

Switched from euler angle rotation to matrix rotation. Gimbal lock is now broken.

src/format.h file | annotate | diff | comparison | revisions
src/glRenderer.cpp file | annotate | diff | comparison | revisions
src/glRenderer.h file | annotate | diff | comparison | revisions
--- a/src/format.h	Sat Feb 04 14:44:39 2017 +0200
+++ b/src/format.h	Wed Feb 08 16:25:06 2017 +0200
@@ -18,10 +18,10 @@
 
 #pragma once
 #include <QIODevice>
+#include <QGenericMatrix>
 #include "basics.h"
 #include "colors.h"
 
-
 // Converts a given value into a string that can be retrieved with text().
 // Used as the argument type to the formatting functions, hence its name.
 class StringFormatArg
@@ -62,6 +62,29 @@
 		m_text += "}";
 	}
 
+	template<int Columns, int Rows, typename Type>
+	StringFormatArg(const QGenericMatrix<Columns, Rows, Type>& matrix)
+	{
+		m_text = "{";
+		for (int row = 0; row < Rows; ++row)
+		{
+			if (row > 0)
+				m_text += ", ";
+
+			m_text += "{";
+
+			for (int column = 0; column < Rows; ++column)
+			{
+				if (column > 0)
+					m_text += ", ";
+
+				m_text += StringFormatArg{matrix(row, column)}.text();
+			}
+
+			m_text += "}";
+		}
+	}
+
 	inline QString text() const
 	{
 		return m_text;
--- a/src/glRenderer.cpp	Sat Feb 04 14:44:39 2017 +0200
+++ b/src/glRenderer.cpp	Wed Feb 08 16:25:06 2017 +0200
@@ -210,9 +210,15 @@
 //
 void GLRenderer::resetAngles()
 {
-	rotation (X) = 30.0f;
-	rotation (Y) = 325.f;
-	panning (X) = panning (Y) = rotation (Z) = 0.0f;
+	// Why did I even bother trying to compute this by pen and paper? Let GL figure it out...
+	glMatrixMode(GL_MODELVIEW);
+	glPushMatrix();
+	glLoadIdentity();
+	glRotated(30, 1, 0, 0);
+	glRotated(330, 0, 1, 0);
+	glGetDoublev(GL_MODELVIEW_MATRIX, currentDocumentData().rotationMatrix);
+	glPopMatrix();
+	panning(X) = panning(Y) = 0.0f;
 	needZoomToFit();
 }
 
@@ -400,9 +406,7 @@
 		glLoadIdentity();
 		glTranslatef(0.0f, 0.0f, -2.0f);
 		glTranslatef(panning (X), panning (Y), -zoom());
-		glRotatef(rotation(X), 1.0f, 0.0f, 0.0f);
-		glRotatef(rotation(Y), 0.0f, 1.0f, 0.0f);
-		glRotatef(rotation(Z), 0.0f, 0.0f, 1.0f);
+		glMultMatrixd(currentDocumentData().rotationMatrix);
 	}
 
 	glEnableClientState (GL_VERTEX_ARRAY);
@@ -607,8 +611,8 @@
 
 #ifndef RELEASE
 	{
-		QString text = format("Rotation: (%1°, %2°, %3°)\nPanning: (%4, %5), Zoom: %6",
-			rotation(X), rotation(Y), rotation(Z), panning(X), panning(Y), zoom());
+		QString text = format("Rotation: %1\nPanning: (%2, %3), Zoom: %4",
+		    QGenericMatrix<4, 4, GLdouble>(currentDocumentData().rotationMatrix), panning(X), panning(Y), zoom());
 		QRect textSize = metrics.boundingRect(0, 0, m_width, m_height, Qt::AlignCenter, text);
 		painter.setPen(textPen());
 		painter.drawText((width() - textSize.width()) / 2, height() - textSize.height(), textSize.width(),
@@ -807,12 +811,17 @@
 			m_panning = true;
 			m_isCameraMoving = true;
 		}
-		else if (left and camera() == FreeCamera)
+		else if (left and camera() == FreeCamera and (xMove != 0 or yMove != 0))
 		{
-			rotation(X) = rotation(X) + yMove;
-			rotation(Y) = rotation(Y) + xMove;
-			clampAngle(rotation (X));
-			clampAngle(rotation (Y));
+			// Apply current rotation input to the rotation matrix
+			// ref: https://forums.ldraw.org/thread-22006-post-24426.html#pid24426
+			glPushMatrix();
+			glLoadIdentity();
+			// 0.6 is an arbitrary rotation sensitivity scalar
+			glRotated(0.6 * hypot(xMove, yMove), yMove, xMove, 0);
+			glMultMatrixd(currentDocumentData().rotationMatrix);
+			glGetDoublev(GL_MODELVIEW_MATRIX, currentDocumentData().rotationMatrix);
+			glPopMatrix();
 			m_isCameraMoving = true;
 		}
 	}
@@ -1623,14 +1632,6 @@
 	return *document()->glData();
 }
 
-double& GLRenderer::rotation (Axis ax)
-{
-	return
-		(ax == X) ? currentDocumentData().rotationX :
-		(ax == Y) ? currentDocumentData().rotationY :
-					currentDocumentData().rotationZ;
-}
-
 double& GLRenderer::panning (Axis ax)
 {
 	return (ax == X) ? currentDocumentData().panX[camera()] :
--- a/src/glRenderer.h	Sat Feb 04 14:44:39 2017 +0200
+++ b/src/glRenderer.h	Wed Feb 08 16:25:06 2017 +0200
@@ -69,9 +69,7 @@
 //
 struct LDGLData
 {
-	double			rotationX;
-	double			rotationY;
-	double			rotationZ;
+	GLdouble		rotationMatrix[16];
 	double			panX[7];
 	double			panY[7];
 	double			zoom[7];
@@ -81,9 +79,7 @@
 	bool			needZoomToFit;
 
 	LDGLData() :
-		rotationX (0.0),
-		rotationY (0.0),
-		rotationZ (0.0),
+	    rotationMatrix {1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1},
 		init (false),
 		needZoomToFit (true)
 	{
@@ -249,7 +245,6 @@
 	LDOverlay* findOverlayObject (Camera cam);
 	double& panning (Axis ax);
 	double panning (Axis ax) const;
-	double& rotation (Axis ax);
 	double& zoom();
 	void zoomToFit();
 	void zoomAllToFit();

mercurial