Exemplo n.º 1
0
LONG WINAPI ScreenSaverProc(HWND hwnd,UINT message,WPARAM wparam,LPARAM lparam){
    // Handles screen saver messages
    switch(message)
    {
    case WM_CREATE:
        // Creation of the screen saver window
		hWnd = hwnd;
		SetupOpenGL();
		// Set Timer (Target 60 fps ish (1000/60 = 16)
		uTimer = SetTimer(hwnd, 1, 16, NULL);
        return 0;

    case WM_ERASEBKGND:
        // Erases the screen saver background
		// Not needed with OpenGL
        return 0;

    case WM_TIMER:
        // Handles the timer
		DrawGLScene();
        return 0;

    case WM_DESTROY:
        // Cleans up the screen saver window
		KillTimer(hwnd, uTimer);
		KillGL();
        PostQuitMessage(0);
        return 0;
    }
    return DefScreenSaverProc(hwnd,message,wparam,lparam);
}
Exemplo n.º 2
0
bool VideoOutputOpenGL::Init(int width, int height, float aspect, WId winid,
                             const QRect &win_rect, MythCodecID codec_id)
{
    QMutexLocker locker(&gl_context_lock);

    bool success = true;
    // FIXME Mac OS X overlay does not work with preview
    window.SetAllowPreviewEPG(true);
    gl_parent_win = winid;

    VideoOutput::Init(width, height, aspect, winid, win_rect, codec_id);

    SetProfile();
    InitPictureAttributes();
    success &= SetupContext();
    InitDisplayMeasurements(width, height, false);
    success &= CreateBuffers();
    success &= CreatePauseFrame();
    success &= SetupOpenGL();
    InitOSD();
    MoveResize();

    if (!success)
        TearDown();

    return success;
}
Exemplo n.º 3
0
// Initializes the graphics system
void _Graphics::Init(const _WindowSettings &WindowSettings) {
	this->WindowSize = WindowSettings.Size;
	this->Anisotropy = 0;
	FramesPerSecond = 0;
	FrameCount = 0;
	FrameRateTimer = 0;
	Context = 0;
	Window = 0;
	Enabled = true;
	DirtyState();

	// Set root element
	Element = new _Element();
	Element->Visible = true;
	Element->Size = WindowSize;
	Element->CalculateBounds();

	// Set video flags
	Uint32 VideoFlags = SDL_WINDOW_OPENGL;
	if(WindowSettings.Fullscreen)
		VideoFlags |= SDL_WINDOW_FULLSCREEN_DESKTOP;

	// Set opengl attributes
	SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 1);
	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 1);

	// Set video mode
	Window = SDL_CreateWindow(WindowSettings.WindowTitle.c_str(), WindowSettings.Position.x, WindowSettings.Position.y, WindowSettings.Size.x, WindowSettings.Size.y, VideoFlags);
	if(Window == nullptr)
		throw std::runtime_error("SDL_CreateWindow failed");

	// Set up opengl context
	Context = SDL_GL_CreateContext(Window);
	if(Context == nullptr)
		throw std::runtime_error("SDL_GL_CreateContext failed");

	InitGLFunctions();

	int MajorVersion, MinorVersion;
	SDL_GL_GetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, &MajorVersion);
	SDL_GL_GetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, &MinorVersion);

	// Set vsync
	SDL_GL_SetSwapInterval(WindowSettings.Vsync);

	// Set up OpenGL
	SetupOpenGL();

	// Setup viewport
	ChangeViewport(WindowSize);
	png_init(0, 0);
}
Exemplo n.º 4
0
ERRCODE CRenderer::Initialize()
{
	ERRCODE er;
	if(!(er = SetupWindow()))
	{
		if(!(er = SetupOpenGL()))
		{
			ready = true;

			if(separateThread)
				this->Go();
		}
	}

	return er;
}
Exemplo n.º 5
0
//*********************************************************************************
// Application initialization
//
BOOL InitInstance(HINSTANCE hInstance, int nCmdShow)
{
   g_hAppInstance = hInstance;

   //--- Create window

   g_hAppWnd = CreateWindow(WDCLASS_NAME, "3D scene renderer", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, g_hAppInstance, NULL);
   if (!g_hAppWnd)
      return(FALSE);

   SetupOpenGL();

   //--- Display window

   ShowWindow(g_hAppWnd, nCmdShow);
   UpdateWindow(g_hAppWnd);

   return(TRUE);
}
Exemplo n.º 6
0
bool VideoOutputOpenGL::CreateVideoResources(void)
{
    bool result = SetupOpenGL();
    MoveResize();
    return result;
}
LRESULT WINAPI vmdWindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
  PAINTSTRUCT   ps; /* Paint structure. */

  // XXX this enum has to be replicated here since its otherwise 
  //     private to the DisplayDevice class and children.
  enum EventCodes { WIN_REDRAW, WIN_LEFT, WIN_MIDDLE, WIN_RIGHT,
                    WIN_WHEELUP, WIN_WHEELDOWN, WIN_MOUSEX, WIN_MOUSEY, 
                    WIN_KBD, 
                    WIN_KBD_ESCAPE,
                    WIN_KBD_UP,
                    WIN_KBD_DOWN,
                    WIN_KBD_LEFT,
                    WIN_KBD_RIGHT,
                    WIN_KBD_PAGE_UP,
                    WIN_KBD_PAGE_DOWN,
                    WIN_KBD_HOME,
                    WIN_KBD_END,
                    WIN_KBD_INSERT,
                    WIN_KBD_DELETE,
                    WIN_KBD_F1,  WIN_KBD_F2,  WIN_KBD_F3,  WIN_KBD_F4,
                    WIN_KBD_F5,  WIN_KBD_F6,  WIN_KBD_F7,  WIN_KBD_F8,
                    WIN_KBD_F9,  WIN_KBD_F10, WIN_KBD_F11, WIN_KBD_F12,
                    WIN_NOEVENT };
  wgldata *glwsrv;
  OpenGLDisplayDevice * ogldispdev;

  // Upon first window creation, immediately set our user-data field
  // to store caller-provided handles for this window instance
  if (msg == WM_NCCREATE) {
#if defined(_M_X64) || defined(_WIN64) || defined(_Wp64)
    SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR) (((CREATESTRUCT *) lParam)->lpCreateParams));
#else
    SetWindowLong(hwnd, GWL_USERDATA, (LONG) (((CREATESTRUCT *) lParam)->lpCreateParams));
#endif
  }

  // check to make sure we have a valid window data structure in case
  // it is destroyed while there are still pending messages...
#if defined(_M_X64) || defined(_WIN64) || defined(_Wp64)
  ogldispdev = (OpenGLDisplayDevice *) GetWindowLongPtr(hwnd, GWLP_USERDATA);
#else
  ogldispdev = (OpenGLDisplayDevice *) GetWindowLong(hwnd, GWL_USERDATA);
#endif

  // when VMD destroys its window data structures it is possible that
  // the window could still get messages briefly thereafter, this prevents
  // us from attempting to handle any messages when the VMD state that goes
  // with the window has already been destructed. (most notably when using
  // the spaceball..)  If we have a NULL pointer, let windows handle the
  // event for us using the default window proc.
  if (ogldispdev == NULL)
    return DefWindowProc(hwnd, msg, wParam, lParam);
 
  glwsrv = &ogldispdev->glwsrv;

#ifdef VMDSPACEWARE
  // see if it is a spaceball event, if so do something about it.
  if (vmd_processwin32spaceballevent(glwsrv, msg, wParam, lParam))
    return 0; //
#endif

  switch(msg) {
    case WM_CREATE:
      glwsrv->hWnd = hwnd;
      glwsrv->hRC = SetupOpenGL(glwsrv);
      glwsrv->WEvents = WIN_REDRAW;
      return 0;

    case WM_SIZE:
      wglMakeCurrent(glwsrv->hDC, glwsrv->hRC);
      ogldispdev->xSize = LOWORD(lParam);
      ogldispdev->ySize = HIWORD(lParam);
      ogldispdev->reshape();
      glViewport(0, 0, (GLsizei) ogldispdev->xSize, (GLsizei) ogldispdev->ySize);
      glwsrv->WEvents = WIN_REDRAW;
      return 0;

    case WM_SIZING:
      wglMakeCurrent(glwsrv->hDC, glwsrv->hRC);
      glClear(GL_COLOR_BUFFER_BIT);
      SwapBuffers(glwsrv->hDC);
      glDrawBuffer(GL_BACK);
      return 0;

    case WM_CLOSE:
      PostQuitMessage(0);
      return 0;

    case WM_PAINT:
      BeginPaint(hwnd, &ps);
      EndPaint(hwnd, &ps);
      glwsrv->WEvents = WIN_REDRAW;
      return 0;

    case WM_KEYDOWN:
      glwsrv->KeyFlag = MapVirtualKey((UINT) wParam, 2); // map to ASCII
      glwsrv->WEvents = WIN_KBD;
      if (glwsrv->KeyFlag == 0) {
        unsigned int keysym = wParam;
        switch (keysym) {
          case VK_ESCAPE:    glwsrv->WEvents = WIN_KBD_ESCAPE;    break;
          case VK_UP:        glwsrv->WEvents = WIN_KBD_UP;        break;
          case VK_DOWN:      glwsrv->WEvents = WIN_KBD_DOWN;      break;
          case VK_LEFT:      glwsrv->WEvents = WIN_KBD_LEFT;      break;
          case VK_RIGHT:     glwsrv->WEvents = WIN_KBD_RIGHT;     break;
          case VK_PRIOR:     glwsrv->WEvents = WIN_KBD_PAGE_UP;   break;
          case VK_NEXT:      glwsrv->WEvents = WIN_KBD_PAGE_DOWN; break;
          case VK_HOME:      glwsrv->WEvents = WIN_KBD_HOME;      break;
          case VK_END:       glwsrv->WEvents = WIN_KBD_END;       break;
          case VK_INSERT:    glwsrv->WEvents = WIN_KBD_INSERT;    break;
          case VK_DELETE:    glwsrv->WEvents = WIN_KBD_DELETE;    break;
          case VK_F1:        glwsrv->WEvents = WIN_KBD_F1;        break;
          case VK_F2:        glwsrv->WEvents = WIN_KBD_F2;        break;
          case VK_F3:        glwsrv->WEvents = WIN_KBD_F3;        break;
          case VK_F4:        glwsrv->WEvents = WIN_KBD_F4;        break;
          case VK_F5:        glwsrv->WEvents = WIN_KBD_F5;        break;
          case VK_F6:        glwsrv->WEvents = WIN_KBD_F6;        break;
          case VK_F7:        glwsrv->WEvents = WIN_KBD_F7;        break;
          case VK_F8:        glwsrv->WEvents = WIN_KBD_F8;        break;
          case VK_F9:        glwsrv->WEvents = WIN_KBD_F9;        break;
          case VK_F10:       glwsrv->WEvents = WIN_KBD_F10;       break;
          case VK_F11:       glwsrv->WEvents = WIN_KBD_F11;       break;
          case VK_F12:       glwsrv->WEvents = WIN_KBD_F12;       break;
          default:
            glwsrv->WEvents = WIN_NOEVENT;
            break;
        }
      }
      return 0;

    case WM_MOUSEMOVE:
      vmd_transwin32mouse(ogldispdev, lParam);
      glwsrv->MouseFlags = (long) wParam;
      return 0;

    case WM_MOUSEWHEEL:
      {
        int zDelta = ((short) HIWORD(wParam));
        // XXX
        // zDelta is in positive or negative multiples of WHEEL_DELTA for
        // clicky type scroll wheels on existing mice, may need to
        // recode this for continuous wheels at some future point in time.
        // WHEEL_DELTA is 120 in current versions of Windows.
        // We only activate an event if the user moves the mouse wheel at
        // least half of WHEEL_DELTA, so that they don't do it by accident
        // all the time.
        if (zDelta > (WHEEL_DELTA / 2)) {
          glwsrv->WEvents = WIN_WHEELUP;
        } else if (zDelta < -(WHEEL_DELTA / 2)) {
          glwsrv->WEvents = WIN_WHEELDOWN;
        }
      }
      return 0;

    case WM_LBUTTONDOWN:
      SetCapture(hwnd);
      vmd_transwin32mouse(ogldispdev, lParam);
      glwsrv->MouseFlags = (long) wParam;
      glwsrv->WEvents = WIN_LEFT;
      return 0;

    case WM_LBUTTONUP:
      vmd_transwin32mouse(ogldispdev, lParam);
      glwsrv->MouseFlags = (long) wParam;
      glwsrv->WEvents = WIN_LEFT;
      if (!(glwsrv->MouseFlags & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON))) 
        ReleaseCapture();
      return 0;

    case WM_MBUTTONDOWN:
      SetCapture(hwnd);
      vmd_transwin32mouse(ogldispdev, lParam);
      glwsrv->MouseFlags = (long) wParam;
      glwsrv->WEvents = WIN_MIDDLE;
      return 0;

    case WM_MBUTTONUP:
      vmd_transwin32mouse(ogldispdev, lParam);
      glwsrv->MouseFlags = (long) wParam;
      glwsrv->WEvents = WIN_MIDDLE;
      if (!(glwsrv->MouseFlags & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON))) 
        ReleaseCapture();
      return 0;

    case WM_RBUTTONDOWN:
      SetCapture(hwnd);
      vmd_transwin32mouse(ogldispdev, lParam);
      glwsrv->MouseFlags = (long) wParam;
      glwsrv->WEvents = WIN_RIGHT;
      return 0;

    case WM_RBUTTONUP:
      vmd_transwin32mouse(ogldispdev, lParam);
      glwsrv->MouseFlags = (long) wParam;
      glwsrv->WEvents = WIN_RIGHT;
      if (!(glwsrv->MouseFlags & (MK_LBUTTON | MK_MBUTTON | MK_RBUTTON))) 
        ReleaseCapture();
      return 0;

    case WM_SETCURSOR:
      // We process the mouse cursor hit test codes here, they tell us
      // what part of the window we're over, which helps us set the cursor
      // to the correct style for sizing borders, moves, etc.
      switch (LOWORD(lParam)) {
        case HTBOTTOM:
        case HTTOP:
          SetCursor(LoadCursor(NULL, IDC_SIZENS));
          break;

        case HTLEFT:
        case HTRIGHT:
          SetCursor(LoadCursor(NULL, IDC_SIZEWE));
          break;

        case HTTOPRIGHT:
        case HTBOTTOMLEFT:
          SetCursor(LoadCursor(NULL, IDC_SIZENESW));
          break;

        case HTTOPLEFT:
        case HTBOTTOMRIGHT:
          SetCursor(LoadCursor(NULL, IDC_SIZENWSE));
          break;

        case HTCAPTION:
          SetCursor(LoadCursor(NULL, IDC_ARROW));
          break;
          
        case HTCLIENT:
        default:
          ogldispdev->set_cursor(glwsrv->cursornum);
      }
      return 0;

    // 
    // Handle Windows File Drag and Drop Operations  
    // This code needs to be linked against SHELL32.DLL
    // 
    case WM_DROPFILES: 
      {
        char lpszFile[4096];
        UINT numfiles, fileindex, numc;
        HDROP hDropInfo = (HDROP)wParam;
        
        // Get the number of simultaneous dragged/dropped files.
        numfiles = DragQueryFile(hDropInfo, (DWORD)(-1), (LPSTR)NULL, 0);
  
        msgInfo << "Ignoring Drag and Drop operation, received " 
                << ((int) numfiles) << " files:" << sendmsg;

        FileSpec spec;       
        for (fileindex=0; fileindex<numfiles; fileindex++) {
          // lpszFile: complete pathname with device, colon and backslashes
          numc = DragQueryFile(hDropInfo, fileindex, (char *) &lpszFile, 4096);
  
          // VMD loads the file(s) here, or queues them up in its own
          // list to decide how to cope with them.  Deciding how to deal
          // with these files is definitely the tricky part.
          msgInfo << "  File(" << ((int) fileindex) << "): " << lpszFile 
                  << " (numc=" << ((int) numc) << ")" << sendmsg;

          // attempt to load the file into a new molecule
          ogldispdev->vmdapp->molecule_load(-1, lpszFile, NULL, &spec);
        }  
        DragFinish(hDropInfo); // finish drop operation and release memory
      }
      return 0;

    default:
      return DefWindowProc(hwnd, msg, wParam, lParam);
  }

  return 0;
}
Exemplo n.º 8
0
int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow)
{
    WNDCLASSEX wcex;
    HWND hwnd;
//    HDC hDC;
    HGLRC hRC;
    MSG msg;
    BOOL bQuit = FALSE;

    /* register window class */
    wcex.cbSize = sizeof(WNDCLASSEX);
    wcex.style = 0; // CS_HREDRAW | CS_VREDRAW | CS_OWNDC;
    wcex.lpfnWndProc = WindowProc;
    wcex.cbClsExtra = 0;
    wcex.cbWndExtra = 0;
    wcex.hInstance = hInstance;
    wcex.hIcon = LoadIcon(NULL, IDI_APPLICATION);
    wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
    wcex.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    wcex.lpszMenuName = NULL;
    wcex.lpszClassName = "CubeSolver";
    wcex.hIconSm = LoadIcon(NULL, IDI_APPLICATION);;


    if (!RegisterClassEx(&wcex))
        return 0;

    /* create main window */
    hwnd = CreateWindowEx(0,
                          "CubeSolver",
                          "CubeSolver",
                          WS_OVERLAPPEDWINDOW,
                          CW_USEDEFAULT,
                          CW_USEDEFAULT,
                          window_width,
                          window_height,
                          NULL,
                          NULL,
                          hInstance,
                          NULL);

    ShowWindow(hwnd, nCmdShow);

    /* enable OpenGL for the window */
    EnableOpenGL(hwnd, &hDC, &hRC);
    SetupOpenGL();

    /* program main loop */
    while (!bQuit)
    {
        /* check for messages */
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            /* handle or dispatch messages */
            if (msg.message == WM_QUIT)
            {
                bQuit = TRUE;
            }
            else
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }
        else
        {
            /* OpenGL animation code goes here */
            gCube.tick(0.01);
            Render();
            Sleep (1);
        }
    }

    /* shutdown OpenGL */
    DisableOpenGL(hwnd, hDC, hRC);

    /* destroy the window explicitly */
    DestroyWindow(hwnd);

    return msg.wParam;
}
Exemplo n.º 9
0
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    switch (uMsg)
    {
    case WM_SYSCOMMAND:
    {
        switch (wParam)
        {
        case SC_SCREENSAVE:
        case SC_MONITORPOWER:
            //return 0; // Prevent From Happening
            ;
        default:
            return DefWindowProc(hwnd, uMsg, wParam, lParam);
        }
        break;
    }
    case WM_SIZE:
        window_width = LOWORD(lParam);
        window_height = HIWORD(lParam);
        SetupOpenGL();
        break;
    case WM_PAINT:
    {
        PAINTSTRUCT ps;
        BeginPaint(hwnd, &ps);
        EndPaint(hwnd, &ps);
        Render();
        break;
    }
    case WM_CLOSE:
        PostQuitMessage(0);
    break;

    case WM_DESTROY:
        return 0;

    case WM_KEYDOWN:
    {
        switch (wParam)
        {
        case 'Q':        gCube.move("Fc'");        break;
        case 'W':        gCube.move("B");        break;
        case 'E':        gCube.move("L'");        break;
        case 'R':        gCube.move("l'");        break;
        case 'T':        gCube.move("Lc'");        break;
        case 'A':        gCube.move("Uc'");        break;
        case 'S':        gCube.move("D");        break;
        case 'D':        gCube.move("L");        break;
        case 'F':        gCube.move("U'");        break;
        case 'G':        gCube.move("F'");        break;
        case 'V':        gCube.move("l");        break;
        case 'B':        gCube.move("Lc");        break;

        case 'P':        gCube.move("Fc");        break;
        case 'O':        gCube.move("B'");        break;
        case 'I':        gCube.move("R");        break;
        case 'U':        gCube.move("r");        break;
        case 'Y':        gCube.move("Rc");        break;
        case 192:        gCube.move("Uc");        break;
        case 'L':        gCube.move("D'");        break;
        case 'K':        gCube.move("R'");        break;
        case 'J':        gCube.move("U");        break;
        case 'H':        gCube.move("F");        break;
        case 'M':        gCube.move("r'");        break;
        case 'N':        gCube.move("Rc'");        break;

        case VK_LEFT:    gCube.move("Uc");        break;
        case VK_RIGHT:    gCube.move("Uc'");        break;
        case VK_UP:        gCube.move("Rc");        break;
        case VK_DOWN:    gCube.move("Rc'");        break;

        case VK_SPACE:
            gCube.randomize();
            break;

        case VK_END:
            //gCube.setFaces("BLBBUDFBFRLDRRBDRRLDDLFFURFRULUDDLDBDUUBLFUFFLURRBLUFB");
            //gCube.setFaces("LUDRUDFBRULLRRBFBBUDBFFFLUUFFLDDURDRBURLLLFRDBLURBBDFD");
            gCube.setFaces("BDFBULRDFDDUFRBULDUFLLFRUFBRULUDURUBLDBBLFFBFLRDRBLRRD");
            break;

        case VK_HOME:
            //gCube.move("F L F U' R U F2 L2 U' L' B D' B' L2 U"); // Cube in cube
            gCube.move("F U F2 D B U R F L D R U L U B D2 R F U2 D2"); // 20 face turns
            break;

        case VK_RETURN:
            // Solve the cube
            gCube.solve();
            break;

        case VK_BACK:
            gCube.clean();
            break;
        case VK_ESCAPE:
            PostQuitMessage(0);
            break;
        default:
            printf("Unknown vkey: %d\n", wParam);
            fflush(stdout);
        }
    }
    break;

    default:
        return DefWindowProc(hwnd, uMsg, wParam, lParam);
    }

    return 0;
}
Exemplo n.º 10
0
int
main()
{
    SetupOpenGL();

    std::unique_ptr<btBroadphaseInterface> broadphase { new btDbvtBroadphase };
    std::unique_ptr<btDefaultCollisionConfiguration> collisionConfiguration {
        new btDefaultCollisionConfiguration
    };
    std::unique_ptr<btCollisionDispatcher> dispatcher { new btCollisionDispatcher {
        collisionConfiguration.get()
    } };
    std::unique_ptr<btSequentialImpulseConstraintSolver> solver {
        new btSequentialImpulseConstraintSolver
    };
    std::unique_ptr<btDiscreteDynamicsWorld> dynamicsWorld { new btDiscreteDynamicsWorld {
        dispatcher.get(), broadphase.get(), solver.get(), collisionConfiguration.get()
    } };
    dynamicsWorld->setGravity(btVector3 { 0, -10, 0 });
    std::unique_ptr<btCollisionShape> groundShape {
        new btStaticPlaneShape { btVector3 { 0, 1, 0 }, 1 }
    };
    std::unique_ptr<btSphereShape> fallShape { new btSphereShape { 1 } };
    std::unique_ptr<btDefaultMotionState> groundMotionState { new btDefaultMotionState {
        btTransform { btQuaternion { 0, 0, 0, 1 }, btVector3 { 0, -1, 0 } }
    } };
    btRigidBody::btRigidBodyConstructionInfo groundRigidBodyConstructionInfo {
        0, groundMotionState.get(), groundShape.get(), btVector3 { 0, 0, 0 }
    };
    std::unique_ptr<btRigidBody> groundRigidBody { new btRigidBody {
        groundRigidBodyConstructionInfo
    } };

    dynamicsWorld->addRigidBody(groundRigidBody.get());

    std::unique_ptr<btDefaultMotionState> fallMotionState { new btDefaultMotionState {
        btTransform { btQuaternion { 0, 0, 0, 1 }, btVector3 { 0, 50, 0 } }
    } };
    btScalar mass { 1 };
    btVector3 fallInertia { 0, 0, 0 };

    fallShape->calculateLocalInertia(mass, fallInertia);

    btRigidBody::btRigidBodyConstructionInfo fallRigidBodyConstructionInfo {
        mass, fallMotionState.get(), fallShape.get(), fallInertia
    };
    std::unique_ptr<btRigidBody> fallRigidBody { new btRigidBody {
        fallRigidBodyConstructionInfo
    } };

    dynamicsWorld->addRigidBody(fallRigidBody.get());

    glm::mat4 projectionMatrix = glm::perspective(45.0f, 4.0f / 3.0f, 0.1f, 100.0f);
    glm::mat4 viewMatrix;
    glm::mat4 modelMatrix;
    glm::mat4 mvp;
    glm::vec3 lightPosition { 3, 3, 3 };
    glm::vec3 lightColor { 1, 1, 1 };
    float lightPower { 50.0f };

    btTransform transformation;

    double currentTime, lastTime = glfwGetTime();
    double fps { 0.0 };
    size_t nFrames { 0l };

    do {
        dynamicsWorld->stepSimulation(1 / 60.0f, 10);
        fallRigidBody->getMotionState()->getWorldTransform(transformation);

        ProcessInputs();
        modelMatrix = glm::translate(glm::mat4 { 1.0f }, glm::vec3 {
            transformation.getOrigin().getX(),
            transformation.getOrigin().getY(),
            transformation.getOrigin().getZ()
        });
        viewMatrix = glm::lookAt(
            s_position,
            s_position + s_direction,
            s_up
        );
        mvp = projectionMatrix * viewMatrix * modelMatrix;

        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        glUseProgram(s_programId);
        glUniformMatrix4fv(s_mvpMatrixId, 1, GL_FALSE, &mvp[0][0]);
        glUniformMatrix4fv(s_modelMatrixId, 1, GL_FALSE, &modelMatrix[0][0]);
        glUniformMatrix4fv(s_viewMatrixId, 1, GL_FALSE, &viewMatrix[0][0]);
        glUniform3f(s_lightPositionId, lightPosition.x, lightPosition.y, lightPosition.z);
        glUniform3f(s_lightColorId, lightColor.x, lightColor.y, lightColor.z);
        glUniform1f(s_lightPowerId, lightPower);

        glActiveTexture(GL_TEXTURE0);
        glBindTexture(GL_TEXTURE_2D, s_textureId);
        glUniform1i(s_uniformTextureId, 0);

        glEnableVertexAttribArray(0);
        glBindBuffer(GL_ARRAY_BUFFER, s_vertexBufferId);
        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, nullptr);

        glEnableVertexAttribArray(1);
        glBindBuffer(GL_ARRAY_BUFFER, s_uvCoordBufferId);
        glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, nullptr);

        glEnableVertexAttribArray(2);
        glBindBuffer(GL_ARRAY_BUFFER, s_normalBufferId);
        glVertexAttribPointer(2, 3, GL_FLOAT, GL_FALSE, 0, nullptr);

        glDrawArrays(GL_TRIANGLES, 0, s_model.vertices().size());

        glDisableVertexAttribArray(0);
        glDisableVertexAttribArray(1);
        glDisableVertexAttribArray(2);

        currentTime = glfwGetTime();
        ++nFrames;
        if (currentTime - lastTime > 1.0) {
            fps = 1000.0/nFrames;
            nFrames = 0;
            lastTime += 1.0;
        }
        ShowStatus(transformation.getOrigin().getY(), fps);

        glfwSwapBuffers(s_window);
        glfwPollEvents();
    } while (glfwGetKey(s_window, GLFW_KEY_ESCAPE) != GLFW_PRESS
             && glfwGetKey(s_window, GLFW_KEY_Q) != GLFW_PRESS
             && glfwWindowShouldClose(s_window) == 0);
    dynamicsWorld->removeRigidBody(fallRigidBody.get());
    dynamicsWorld->removeRigidBody(groundRigidBody.get());
    CleanupOpenGL();

    return 0;
}