/* SPDX-License-Identifier: GPL-2.0-or-later * Copyright 2001-2002 NaN Holding BV. All rights reserved. */ /** * Copyright (C) 2001 NaN Technologies B.V. * Simple test file for the GHOST library. * The OpenGL gear code is taken from the Qt sample code which, * in turn, is probably taken from somewhere as well. * Stereo code by Raymond de Vries, januari 2002 */ #include #include #include #if defined(WIN32) || defined(__APPLE__) # ifdef WIN32 # include # include # include # else // WIN32 \ // __APPLE__ is defined # include # endif // WIN32 #else // defined(WIN32) || defined(__APPLE__) # include #endif // defined(WIN32) || defined(__APPLE__) #include "GHOST_Rect.h" #include "GHOST_IEvent.h" #include "GHOST_IEventConsumer.h" #include "GHOST_ISystem.h" #define LEFT_EYE 0 #define RIGHT_EYE 1 static bool nVidiaWindows; // very dirty but hey, it's for testing only static void gearsTimerProc(GHOST_ITimerTask *task, uint64_t time); static class Application *fApp; static GLfloat view_rotx = 20.0, view_roty = 30.0, view_rotz = 0.0; static GLfloat fAngle = 0.0; static GHOST_ISystem *fSystem = 0; void StereoProjection(float left, float right, float bottom, float top, float nearplane, float farplane, float zero_plane, float dist, float eye); static void testTimerProc(GHOST_ITimerTask * /*task*/, uint64_t time) { std::cout << "timer1, time=" << int(time) << "\n"; } static void gearGL( GLfloat inner_radius, GLfloat outer_radius, GLfloat width, GLint teeth, GLfloat tooth_depth) { GLint i; GLfloat r0, r1, r2; GLfloat angle, da; GLfloat u, v, len; r0 = inner_radius; r1 = outer_radius - tooth_depth / 2.0; r2 = outer_radius + tooth_depth / 2.0; const double pi = 3.14159264; da = 2.0 * pi / teeth / 4.0; glShadeModel(GL_FLAT); glNormal3f(0.0, 0.0, 1.0); /* draw front face */ glBegin(GL_QUAD_STRIP); for (i = 0; i <= teeth; i++) { angle = i * 2.0 * pi / teeth; glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5); glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5); glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5); glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), width * 0.5); } glEnd(); /* draw front sides of teeth */ glBegin(GL_QUADS); da = 2.0 * pi / teeth / 4.0; for (i = 0; i < teeth; i++) { angle = i * 2.0 * pi / teeth; glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5); glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width * 0.5); glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), width * 0.5); glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), width * 0.5); } glEnd(); glNormal3f(0.0, 0.0, -1.0); /* draw back face */ glBegin(GL_QUAD_STRIP); for (i = 0; i <= teeth; i++) { angle = i * 2.0 * pi / teeth; glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5); glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5); glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -width * 0.5); glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5); } glEnd(); /* draw back sides of teeth */ glBegin(GL_QUADS); da = 2.0 * pi / teeth / 4.0; for (i = 0; i < teeth; i++) { angle = i * 2.0 * pi / teeth; glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -width * 0.5); glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), -width * 0.5); glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width * 0.5); glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5); } glEnd(); /* draw outward faces of teeth */ glBegin(GL_QUAD_STRIP); for (i = 0; i < teeth; i++) { angle = i * 2.0 * pi / teeth; glVertex3f(r1 * cos(angle), r1 * sin(angle), width * 0.5); glVertex3f(r1 * cos(angle), r1 * sin(angle), -width * 0.5); u = r2 * cos(angle + da) - r1 * cos(angle); v = r2 * sin(angle + da) - r1 * sin(angle); len = sqrt(u * u + v * v); u /= len; v /= len; glNormal3f(v, -u, 0.0); glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), width * 0.5); glVertex3f(r2 * cos(angle + da), r2 * sin(angle + da), -width * 0.5); glNormal3f(cos(angle), sin(angle), 0.0); glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), width * 0.5); glVertex3f(r2 * cos(angle + 2 * da), r2 * sin(angle + 2 * da), -width * 0.5); u = r1 * cos(angle + 3 * da) - r2 * cos(angle + 2 * da); v = r1 * sin(angle + 3 * da) - r2 * sin(angle + 2 * da); glNormal3f(v, -u, 0.0); glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), width * 0.5); glVertex3f(r1 * cos(angle + 3 * da), r1 * sin(angle + 3 * da), -width * 0.5); glNormal3f(cos(angle), sin(angle), 0.0); } glVertex3f(r1 * cos(0.0), r1 * sin(0.0), width * 0.5); glVertex3f(r1 * cos(0.0), r1 * sin(0.0), -width * 0.5); glEnd(); glShadeModel(GL_SMOOTH); /* draw inside radius cylinder */ glBegin(GL_QUAD_STRIP); for (i = 0; i <= teeth; i++) { angle = i * 2.0 * pi / teeth; glNormal3f(-cos(angle), -sin(angle), 0.0); glVertex3f(r0 * cos(angle), r0 * sin(angle), -width * 0.5); glVertex3f(r0 * cos(angle), r0 * sin(angle), width * 0.5); } glEnd(); } static void drawGearGL(int id) { static GLfloat pos[4] = {5.0f, 5.0f, 10.0f, 1.0f}; static GLfloat ared[4] = {0.8f, 0.1f, 0.0f, 1.0f}; static GLfloat agreen[4] = {0.0f, 0.8f, 0.2f, 1.0f}; static GLfloat ablue[4] = {0.2f, 0.2f, 1.0f, 1.0f}; glLightfv(GL_LIGHT0, GL_POSITION, pos); glEnable(GL_CULL_FACE); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_DEPTH_TEST); switch (id) { case 1: glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, ared); gearGL(1.0f, 4.0f, 1.0f, 20, 0.7f); break; case 2: glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, agreen); gearGL(0.5f, 2.0f, 2.0f, 10, 0.7f); break; case 3: glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, ablue); gearGL(1.3f, 2.0f, 0.5f, 10, 0.7f); break; default: break; } glEnable(GL_NORMALIZE); } void RenderCamera() { glRotatef(view_rotx, 1.0, 0.0, 0.0); glRotatef(view_roty, 0.0, 1.0, 0.0); glRotatef(view_rotz, 0.0, 0.0, 1.0); } void RenderScene() { glPushMatrix(); glTranslatef(-3.0, -2.0, 0.0); glRotatef(fAngle, 0.0, 0.0, 1.0); drawGearGL(1); glPopMatrix(); glPushMatrix(); glTranslatef(3.1f, -2.0f, 0.0f); glRotatef(-2.0 * fAngle - 9.0, 0.0, 0.0, 1.0); drawGearGL(2); glPopMatrix(); glPushMatrix(); glTranslatef(-3.1f, 2.2f, -1.8f); glRotatef(90.0f, 1.0f, 0.0f, 0.0f); glRotatef(2.0 * fAngle - 2.0, 0.0, 0.0, 1.0); drawGearGL(3); glPopMatrix(); } static void View(GHOST_IWindow *window, bool stereo, int eye = 0) { window->activateDrawingContext(); GHOST_Rect bnds; int noOfScanlines = 0, lowerScanline = 0; /* hard coded for testing purposes, display device dependent */ int verticalBlankingInterval = 32; float left, right, bottom, top; float nearplane, farplane, zeroPlane, distance; float eyeSeparation = 0.62f; window->getClientBounds(bnds); // viewport if (stereo) { if (nVidiaWindows) { // handled by nVidia driver so act as normal (explicitly put here since // it -is- stereo) glViewport(0, 0, bnds.getWidth(), bnds.getHeight()); } else { // generic cross platform above-below stereo noOfScanlines = (bnds.getHeight() - verticalBlankingInterval) / 2; switch (eye) { case LEFT_EYE: // upper half of window lowerScanline = bnds.getHeight() - noOfScanlines; break; case RIGHT_EYE: // lower half of window lowerScanline = 0; break; } } } else { noOfScanlines = bnds.getHeight(); lowerScanline = 0; } glViewport(0, lowerScanline, bnds.getWidth(), noOfScanlines); // projection left = -6.0; right = 6.0; bottom = -4.8f; top = 4.8f; nearplane = 5.0; farplane = 60.0; if (stereo) { zeroPlane = 0.0; distance = 14.5; switch (eye) { case LEFT_EYE: StereoProjection(left, right, bottom, top, nearplane, farplane, zeroPlane, distance, -eyeSeparation / 2.0); break; case RIGHT_EYE: StereoProjection(left, right, bottom, top, nearplane, farplane, zeroPlane, distance, eyeSeparation / 2.0); break; } } else { // left = -w; // right = w; // bottom = -h; // top = h; glMatrixMode(GL_PROJECTION); glLoadIdentity(); glFrustum(left, right, bottom, top, 5.0, 60.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glTranslatef(0.0, 0.0, -40.0); } glClearColor(.2f, 0.0f, 0.0f, 0.0f); } void StereoProjection(float left, float right, float bottom, float top, float nearplane, float farplane, float zero_plane, float dist, float eye) /* Perform the perspective projection for one eye's sub-field. * The projection is in the direction of the negative z axis. * * -6.0, 6.0, -4.8, 4.8, * left, right, bottom, top = the coordinate range, in the plane of zero * parallax setting, which will be displayed on the screen. The * ratio between (right-left) and (top-bottom) should equal the aspect * ratio of the display. * * 6.0, -6.0, * near, far = the z-coordinate values of the clipping planes. * * 0.0, * zero_plane = the z-coordinate of the plane of zero parallax setting. * * 14.5, * dist = the distance from the center of projection to the plane * of zero parallax. * * -0.31 * eye = half the eye separation; positive for the right eye sub-field, * negative for the left eye sub-field. */ { float xmid, ymid, clip_near, clip_far, topw, bottomw, leftw, rightw, dx, dy, n_over_d; dx = right - left; dy = top - bottom; xmid = (right + left) / 2.0; ymid = (top + bottom) / 2.0; clip_near = dist + zero_plane - nearplane; clip_far = dist + zero_plane - farplane; n_over_d = clip_near / dist; topw = n_over_d * dy / 2.0; bottomw = -topw; rightw = n_over_d * (dx / 2.0 - eye); leftw = n_over_d * (-dx / 2.0 - eye); /* Need to be in projection mode for this. */ glLoadIdentity(); glFrustum(leftw, rightw, bottomw, topw, clip_near, clip_far); glTranslatef(-xmid - eye, -ymid, -zero_plane - dist); } class Application : public GHOST_IEventConsumer { public: Application(GHOST_ISystem *system); ~Application(void); virtual bool processEvent(GHOST_IEvent *event); GHOST_ISystem *m_system; GHOST_IWindow *m_mainWindow; GHOST_IWindow *m_secondaryWindow; GHOST_IWindow *m_fullScreenWindow; GHOST_ITimerTask *m_gearsTimer, *m_testTimer; GHOST_TStandardCursor m_cursor; bool m_exitRequested; bool stereo; }; Application::Application(GHOST_ISystem *system) : m_system(system), m_mainWindow(0), m_secondaryWindow(0), m_fullScreenWindow(0), m_gearsTimer(0), m_testTimer(0), m_cursor(GHOST_kStandardCursorFirstCursor), m_exitRequested(false), stereo(false) { GHOST_GLSettings glSettings = {0}; fApp = this; // Create the main window m_mainWindow = system->createWindow("gears - main window", 10, 64, 320, 200, GHOST_kWindowStateNormal, GHOST_kDrawingContextTypeOpenGL, glSettings); if (!m_mainWindow) { std::cout << "could not create main window\n"; exit(-1); } // Create a secondary window m_secondaryWindow = system->createWindow("gears - secondary window", 340, 64, 320, 200, GHOST_kWindowStateNormal, GHOST_kDrawingContextTypeOpenGL, glSettings); if (!m_secondaryWindow) { std::cout << "could not create secondary window\n"; exit(-1); } // Install a timer to have the gears running m_gearsTimer = system->installTimer(0 /*delay*/, 20 /*interval*/, gearsTimerProc, m_mainWindow); } Application::~Application(void) { // Dispose windows if (m_system->validWindow(m_mainWindow)) { m_system->disposeWindow(m_mainWindow); } if (m_system->validWindow(m_secondaryWindow)) { m_system->disposeWindow(m_secondaryWindow); } } bool Application::processEvent(GHOST_IEvent *event) { GHOST_IWindow *window = event->getWindow(); bool handled = true; switch (event->getType()) { #if 0 case GHOST_kEventUnknown: break; case GHOST_kEventCursorButton: std::cout << "GHOST_kEventCursorButton"; break; case GHOST_kEventCursorMove: std::cout << "GHOST_kEventCursorMove"; break; #endif case GHOST_kEventWheel: { GHOST_TEventWheelData *wheelData = (GHOST_TEventWheelData *)event->getData(); if (wheelData->z > 0) { view_rotz += 5.f; } else { view_rotz -= 5.f; } } break; case GHOST_kEventKeyUp: break; case GHOST_kEventKeyDown: { GHOST_TEventKeyData *keyData = (GHOST_TEventKeyData *)event->getData(); switch (keyData->key) { case GHOST_kKeyC: { int cursor = m_cursor; cursor++; if (cursor >= GHOST_kStandardCursorNumCursors) { cursor = GHOST_kStandardCursorFirstCursor; } m_cursor = (GHOST_TStandardCursor)cursor; window->setCursorShape(m_cursor); } break; case GHOST_kKeyE: { int x = 200, y = 200; m_system->setCursorPosition(x, y); break; } case GHOST_kKeyF: if (!m_system->getFullScreen()) { // Begin fullscreen mode GHOST_DisplaySetting setting; setting.bpp = 16; setting.frequency = 50; setting.xPixels = 640; setting.yPixels = 480; m_system->beginFullScreen(setting, &m_fullScreenWindow, false /* stereo flag */); } else { m_system->endFullScreen(); m_fullScreenWindow = 0; } break; case GHOST_kKeyH: window->setCursorVisibility(!window->getCursorVisibility()); break; case GHOST_kKeyM: { bool down = false; m_system->getModifierKeyState(GHOST_kModifierKeyLeftShift, down); if (down) { std::cout << "left shift down\n"; } m_system->getModifierKeyState(GHOST_kModifierKeyRightShift, down); if (down) { std::cout << "right shift down\n"; } m_system->getModifierKeyState(GHOST_kModifierKeyLeftAlt, down); if (down) { std::cout << "left Alt down\n"; } m_system->getModifierKeyState(GHOST_kModifierKeyRightAlt, down); if (down) { std::cout << "right Alt down\n"; } m_system->getModifierKeyState(GHOST_kModifierKeyLeftControl, down); if (down) { std::cout << "left control down\n"; } m_system->getModifierKeyState(GHOST_kModifierKeyRightControl, down); if (down) { std::cout << "right control down\n"; } } break; case GHOST_kKeyQ: if (m_system->getFullScreen()) { m_system->endFullScreen(); m_fullScreenWindow = 0; } m_exitRequested = true; break; case GHOST_kKeyS: // toggle mono and stereo if (stereo) { stereo = false; } else { stereo = true; } break; case GHOST_kKeyT: if (!m_testTimer) { m_testTimer = m_system->installTimer(0, 1000, testTimerProc); } else { m_system->removeTimer(m_testTimer); m_testTimer = 0; } break; case GHOST_kKeyW: if (m_mainWindow) { std::string title = m_mainWindow->getTitle(); title += "-"; m_mainWindow->setTitle(title); } break; default: break; } } break; case GHOST_kEventWindowClose: { GHOST_IWindow *window2 = event->getWindow(); if (window2 == m_mainWindow) { m_exitRequested = true; } else { m_system->disposeWindow(window2); } } break; case GHOST_kEventWindowActivate: handled = false; break; case GHOST_kEventWindowDeactivate: handled = false; break; case GHOST_kEventWindowUpdate: { GHOST_IWindow *window2 = event->getWindow(); if (!m_system->validWindow(window2)) break; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); if (stereo) { View(window2, stereo, LEFT_EYE); glPushMatrix(); RenderCamera(); RenderScene(); glPopMatrix(); View(window2, stereo, RIGHT_EYE); glPushMatrix(); RenderCamera(); RenderScene(); glPopMatrix(); } else { View(window2, stereo); glPushMatrix(); RenderCamera(); RenderScene(); glPopMatrix(); } window2->swapBuffers(); } break; default: handled = false; break; } return handled; } int main(int /*argc*/, char ** /*argv*/) { nVidiaWindows = false; // nVidiaWindows = true; #ifdef WIN32 /* Set a couple of settings in the registry for the nVidia detonator driver. * So this is very specific... */ if (nVidiaWindows) { LONG lresult; HKEY hkey = 0; DWORD dwd = 0; // unsigned char buffer[128]; CRegKey regkey; // DWORD keyValue; // lresult = regkey.Open( // HKEY_LOCAL_MACHINE, "SOFTWARE\\NVIDIA Corporation\\Global\\Stereo3D\\StereoEnable"); lresult = regkey.Open(HKEY_LOCAL_MACHINE, "SOFTWARE\\NVIDIA Corporation\\Global\\Stereo3D\\StereoEnable", KEY_ALL_ACCESS); if (lresult == ERROR_SUCCESS) printf("Successfully opened key\n"); # if 0 lresult = regkey.QueryValue(&keyValue, "StereoEnable"); if (lresult == ERROR_SUCCESS) printf("Successfully queried key\n"); # endif lresult = regkey.SetValue( HKEY_LOCAL_MACHINE, "SOFTWARE\\NVIDIA Corporation\\Global\\Stereo3D\\StereoEnable", "1"); if (lresult == ERROR_SUCCESS) printf("Successfully set value for key\n"); regkey.Close(); if (lresult == ERROR_SUCCESS) { printf("Successfully closed key\n"); } // regkey.Write("2"); } #endif // WIN32 // Create the system GHOST_ISystem::createSystem(); fSystem = GHOST_ISystem::getSystem(); if (fSystem) { // Create an application object Application app(fSystem); // Add the application as event consumer fSystem->addEventConsumer(&app); // Enter main loop while (!app.m_exitRequested) { // printf("main: loop\n"); fSystem->processEvents(true); fSystem->dispatchEvents(); } // Remove so ghost doesn't do a double free fSystem->removeEventConsumer(&app); } // Dispose the system GHOST_ISystem::disposeSystem(); return 0; } static void gearsTimerProc(GHOST_ITimerTask *task, uint64_t /*time*/) { fAngle += 2.0; view_roty += 1.0; GHOST_IWindow *window = (GHOST_IWindow *)task->getUserData(); if (fApp->m_fullScreenWindow) { // Running full screen fApp->m_fullScreenWindow->invalidate(); } else { if (fSystem->validWindow(window)) { window->invalidate(); } } }