From e426fe8a530b550526e8d599d130fadd70fbdbc6 Mon Sep 17 00:00:00 2001 From: Jonathan Westhues Date: Thu, 27 Mar 2008 01:53:51 -0800 Subject: [PATCH] Start to add OpenGL support to SolveSpace, for the graphical view. I've got the user interface to pan and rotate an object, more or less works. [git-p4: depot-paths = "//depot/solvespace/": change = 1654] --- Makefile | 3 +- dsc.h | 15 ++++++- graphicswin.cpp | 102 +++++++++++++++++++++++++++++++++++++++++++++- solvespace.cpp | 1 + solvespace.h | 10 +++++ ui.h | 18 ++++++-- util.cpp | 80 ++++++++++++++++++++++++++++++++++++ win32/w32main.cpp | 100 +++++++++++++++++++++++++++++++++++++++++++-- 8 files changed, 319 insertions(+), 10 deletions(-) create mode 100644 util.cpp diff --git a/Makefile b/Makefile index ec52930..ff99717 100644 --- a/Makefile +++ b/Makefile @@ -12,9 +12,10 @@ W32OBJS = $(OBJDIR)\w32main.obj \ SSOBJS = $(OBJDIR)\solvespace.obj \ $(OBJDIR)\cmdline.obj \ $(OBJDIR)\graphicswin.obj \ + $(OBJDIR)\util.obj \ -LIBS = user32.lib gdi32.lib comctl32.lib advapi32.lib +LIBS = user32.lib gdi32.lib comctl32.lib advapi32.lib opengl32.lib glu32.lib all: $(OBJDIR)/solvespace.exe @cp $(OBJDIR)/solvespace.exe . diff --git a/dsc.h b/dsc.h index 945054b..685de7b 100644 --- a/dsc.h +++ b/dsc.h @@ -5,10 +5,23 @@ typedef unsigned long DWORD; typedef unsigned char BYTE; -typedef struct { +typedef struct VectorTag Vector; + +typedef struct VectorTag { double x, y, z; + + Vector Cross(Vector b); + double Vector::Dot(Vector b); + Vector RotatedAbout(Vector axis, double theta); + double Magnitude(void); + Vector ScaledBy(double v); + } Vector; +typedef struct { + double x, y; +} Point2d; + template struct IdList { typedef struct { T v; diff --git a/graphicswin.cpp b/graphicswin.cpp index 0be61a4..d6c5fdc 100644 --- a/graphicswin.cpp +++ b/graphicswin.cpp @@ -1,5 +1,9 @@ -#include "solvespace.h" #include +#include +#include +#include + +#include "solvespace.h" const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = { { 0, "&File", 0, NULL }, @@ -33,3 +37,99 @@ const GraphicsWindow::MenuEntry GraphicsWindow::menu[] = { { -1 }, }; +void GraphicsWindow::Init(void) { + offset.x = offset.y = offset.z = 0.9; + scale = 1; + projRight.x = 1; projRight.y = projRight.z = 0; + projDown.y = 1; projDown.z = projDown.x = 0; + +} + +void GraphicsWindow::NormalizeProjectionVectors(void) { + Vector norm = projRight.Cross(projDown); + projDown = norm.Cross(projRight); + + projDown = projDown.ScaledBy(1/projDown.Magnitude()); + projRight = projRight.ScaledBy(1/projRight.Magnitude()); +} + +void GraphicsWindow::MouseMoved(double x, double y, bool leftDown, + bool middleDown, bool rightDown, bool shiftDown, bool ctrlDown) +{ + if(middleDown) { + double dx = (x - orig.mouse.x) / scale; + double dy = (y - orig.mouse.y) / scale; + + if(shiftDown) { + offset.x = orig.offset.x + dx*projRight.x + dy*projDown.x; + offset.y = orig.offset.y + dx*projRight.y + dy*projDown.y; + offset.z = orig.offset.z + dx*projRight.z + dy*projDown.z; + } else if(ctrlDown) { + double theta = atan2(orig.mouse.y, orig.mouse.x); + theta -= atan2(y, x); + + Vector normal = orig.projRight.Cross(orig.projDown); + projRight = orig.projRight.RotatedAbout(normal, theta); + projDown = orig.projDown.RotatedAbout(normal, theta); + + NormalizeProjectionVectors(); + } else { + double s = 0.3*(PI/180); // degrees per pixel + projRight = orig.projRight.RotatedAbout(orig.projDown, -s*dx); + projDown = orig.projDown.RotatedAbout(orig.projRight, s*dy); + + NormalizeProjectionVectors(); + + orig.projRight = projRight; + orig.projDown = projDown; + orig.mouse.x = x; + orig.mouse.y = y; + } + + Invalidate(); + } +} + +void GraphicsWindow::MouseMiddleDown(double x, double y) { + orig.offset = offset; + orig.projDown = projDown; + orig.projRight = projRight; + orig.mouse.x = x; + orig.mouse.y = y; +} + +void GraphicsWindow::MouseLeftDown(double x, double y) { +} + +void GraphicsWindow::Paint(int w, int h) { + glViewport(0, 0, w, h); + + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + glScaled(scale*2.0/w, scale*2.0/h, 0); + + double tx = projRight.Dot(offset); + double ty = projDown.Dot(offset); + double mat[16]; + MakeMatrix(mat, projRight.x, projRight.y, projRight.z, tx, + projDown.x, projDown.y, projDown.z, ty, + 0, 0, 0, 0, + 0, 0, 0, 1); + glMultMatrixd(mat); + + glEnable(GL_DEPTH_TEST); + + glClearIndex((GLfloat)0); + glClearDepth(1.0); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + + GLUquadricObj *quadObj; + quadObj = gluNewQuadric(); + gluQuadricDrawStyle(quadObj, GLU_LINE); + gluSphere(quadObj, 300, 4, 4); +} + diff --git a/solvespace.cpp b/solvespace.cpp index cca84e3..ad64dec 100644 --- a/solvespace.cpp +++ b/solvespace.cpp @@ -4,6 +4,7 @@ SolveSpace SS; void SolveSpace::Init(void) { TW.Init(); + GW.Init(); int i; for(i = 0; i < 20; i++) { diff --git a/solvespace.h b/solvespace.h index aa86fae..b66bd22 100644 --- a/solvespace.h +++ b/solvespace.h @@ -5,6 +5,7 @@ #include #include #include +#include #include "dsc.h" #include "ui.h" #include "sketch.h" @@ -13,8 +14,17 @@ #define oops() do { dbp("oops at line %d, file %s", __LINE__, __FILE__); \ exit(-1); } while(0) void dbp(char *str, ...); +void Invalidate(void); + + #define arraylen(x) (sizeof((x))/sizeof((x)[0])) +#define PI (3.1415926535897931) +void MakeMatrix(double *mat, double a11, double a12, double a13, double a14, + double a21, double a22, double a23, double a24, + double a31, double a32, double a33, double a34, + double a41, double a42, double a43, double a44); + typedef struct { TextWindow TW; diff --git a/ui.h b/ui.h index 4986533..c0d315d 100644 --- a/ui.h +++ b/ui.h @@ -64,16 +64,26 @@ typedef struct { // coordinates of the 3d sketch points. We will use an axonometric // projection. Vector offset; - double scale; Vector projRight; Vector projDown; + double scale; + struct { + Vector offset; + Vector projRight; + Vector projDown; + Point2d mouse; + } orig; + + void Init(void); + void NormalizeProjectionVectors(void); // These are called by the platform-specific code. - void Paint(void); + void Paint(int w, int h); void MouseMoved(double x, double y, bool leftDown, bool middleDown, - bool rightDown); - void MouseLeftClick(double x, double y); + bool rightDown, bool shiftDown, bool ctrlDown); + void MouseLeftDown(double x, double y); void MouseLeftDoubleClick(double x, double y); + void MouseMiddleDown(double x, double y); void MouseScroll(int delta); } GraphicsWindow; diff --git a/util.cpp b/util.cpp new file mode 100644 index 0000000..3b4806c --- /dev/null +++ b/util.cpp @@ -0,0 +1,80 @@ +#include "solvespace.h" + +void MakeMatrix(double *mat, double a11, double a12, double a13, double a14, + double a21, double a22, double a23, double a24, + double a31, double a32, double a33, double a34, + double a41, double a42, double a43, double a44) +{ + mat[ 0] = a11; + mat[ 1] = a21; + mat[ 2] = a31; + mat[ 3] = a41; + mat[ 4] = a12; + mat[ 5] = a22; + mat[ 6] = a32; + mat[ 7] = a42; + mat[ 8] = a13; + mat[ 9] = a23; + mat[10] = a33; + mat[11] = a43; + mat[12] = a14; + mat[13] = a24; + mat[14] = a34; + mat[15] = a44; +} + + +Vector Vector::Cross(Vector b) +{ + Vector r; + + r.x = -(z*b.y) + (y*b.z); + r.y = (z*b.x) - (x*b.z); + r.z = -(y*b.x) + (x*b.y); + + return r; +} + +double Vector::Dot(Vector b) +{ + return (x*b.x + y*b.y + z*b.z); +} + +Vector Vector::RotatedAbout(Vector axis, double theta) +{ + double c = cos(theta); + double s = sin(theta); + + Vector r; + + r.x = (x)*(c + (1 - c)*(axis.x)*(axis.x)) + + (y)*((1 - c)*(axis.x)*(axis.y) - s*(axis.z)) + + (z)*((1 - c)*(axis.x)*(axis.z) + s*(axis.y)); + + r.y = (x)*((1 - c)*(axis.y)*(axis.x) + s*(axis.z)) + + (y)*(c + (1 - c)*(axis.y)*(axis.y)) + + (z)*((1 - c)*(axis.y)*(axis.z) - s*(axis.x)); + + r.z = (x)*((1 - c)*(axis.z)*(axis.x) - s*(axis.y)) + + (y)*((1 - c)*(axis.z)*(axis.y) + s*(axis.x)) + + (z)*(c + (1 - c)*(axis.z)*(axis.z)); + + return r; +} + +double Vector::Magnitude(void) +{ + return sqrt(x*x + y*y + z*z); +} + +Vector Vector::ScaledBy(double v) +{ + Vector r; + + r.x = x * v; + r.y = y * v; + r.z = z * v; + + return r; +} + diff --git a/win32/w32main.cpp b/win32/w32main.cpp index 04bcdc9..3fee7a0 100644 --- a/win32/w32main.cpp +++ b/win32/w32main.cpp @@ -1,5 +1,7 @@ #include #include +#include +#include #include #include #include @@ -197,6 +199,40 @@ LRESULT CALLBACK TextWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) return 1; } +static HGLRC CreateGlContext(HDC hdc) +{ + PIXELFORMATDESCRIPTOR pfd; + int pixelFormat; + + memset(&pfd, 0, sizeof(pfd)); + pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR); + pfd.nVersion = 1; + pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | + PFD_DOUBLEBUFFER; + pfd.dwLayerMask = PFD_MAIN_PLANE; + pfd.iPixelType = PFD_TYPE_COLORINDEX; + pfd.cColorBits = 8; + pfd.cDepthBits = 16; + pfd.cAccumBits = 0; + pfd.cStencilBits = 0; + + pixelFormat = ChoosePixelFormat(hdc, &pfd); + if(!pixelFormat) oops(); + + if(!SetPixelFormat(hdc, pixelFormat, &pfd)) oops(); + + HGLRC hgrc = wglCreateContext(hdc); + wglMakeCurrent(hdc, hgrc); + + return hgrc; +} + +void Invalidate(void) +{ + InvalidateRect(GraphicsWnd, NULL, FALSE); + InvalidateRect(TextWnd, NULL, FALSE); +} + LRESULT CALLBACK GraphicsWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) { @@ -208,6 +244,64 @@ LRESULT CALLBACK GraphicsWndProc(HWND hwnd, UINT msg, WPARAM wParam, InvalidateRect(TextWnd, NULL, FALSE); break; + case WM_ERASEBKGND: + break; + + case WM_SIZE: + InvalidateRect(GraphicsWnd, NULL, FALSE); + break; + + case WM_PAINT: { + InvalidateRect(GraphicsWnd, NULL, FALSE); + PAINTSTRUCT ps; + HDC hdc = BeginPaint(hwnd, &ps); + + HGLRC hgrc = CreateGlContext(hdc); + + RECT r; + GetClientRect(GraphicsWnd, &r); + int w = r.right - r.left; + int h = r.bottom - r.top; + + SS.GW.Paint(w, h); + + SwapBuffers(hdc); + + wglMakeCurrent(NULL, NULL); + wglDeleteContext(hgrc); + + EndPaint(hwnd, &ps); + break; + } + + case WM_MOUSEMOVE: + case WM_LBUTTONDOWN: + case WM_MBUTTONDOWN: { + int x = LOWORD(lParam); + int y = HIWORD(lParam); + + RECT r; + GetClientRect(GraphicsWnd, &r); + x = x - (r.right - r.left)/2; + y = (r.bottom - r.top)/2 - y; + + if(msg == WM_LBUTTONDOWN) { + SS.GW.MouseLeftDown(x, y); + } else if(msg == WM_MBUTTONDOWN) { + SS.GW.MouseMiddleDown(x, y); + } else if(msg == WM_MOUSEMOVE) { + SS.GW.MouseMoved(x, y, + !!(wParam & MK_LBUTTON), + !!(wParam & MK_MBUTTON), + !!(wParam & MK_RBUTTON), + !!(wParam & MK_SHIFT), + !!(wParam & MK_CONTROL)); + } else { + oops(); + } + break; + } + case WM_CLOSE: case WM_DESTROY: PostQuitMessage(0); @@ -260,7 +354,7 @@ static void CreateMainWindows(void) wc.style = CS_BYTEALIGNCLIENT | CS_BYTEALIGNWINDOW | CS_OWNDC | CS_DBLCLKS; wc.lpfnWndProc = (WNDPROC)GraphicsWndProc; - wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH); + wc.hbrBackground = (HBRUSH)(COLOR_WINDOW+1); wc.lpszClassName = "GraphicsWnd"; wc.lpszMenuName = NULL; wc.hCursor = LoadCursor(NULL, IDC_ARROW); @@ -271,8 +365,8 @@ static void CreateMainWindows(void) HMENU top = CreateGraphicsWindowMenus(); GraphicsWnd = CreateWindowEx(0, "GraphicsWnd", "SolveSpace (View Sketch)", WS_OVERLAPPED | WS_THICKFRAME | WS_CLIPCHILDREN | WS_MAXIMIZEBOX | - WS_MINIMIZEBOX | WS_SYSMENU | WS_SIZEBOX, - 600, 300, 400, 400, NULL, top, Instance, NULL); + WS_MINIMIZEBOX | WS_SYSMENU | WS_SIZEBOX | WS_CLIPSIBLINGS, + 600, 300, 200, 200, NULL, top, Instance, NULL); if(!GraphicsWnd) oops();