| 1 | // OpenGL implementation in Qt |
| 2 | // Parts of this are blantantly ripped off from BSNES (thanks Byuu!) |
| 3 | // |
| 4 | // by James Hammons |
| 5 | // (C) 2010 Underground Software |
| 6 | // |
| 7 | // JLH = James Hammons <jlhamm@acm.org> |
| 8 | // JPM = Jean-Paul Mari <djipi.mari@gmail.com> |
| 9 | // |
| 10 | // Who When What |
| 11 | // --- ---------- ------------------------------------------------------------- |
| 12 | // JLH 01/14/2010 Created this file |
| 13 | // JLH 02/03/2013 Added "centered" fullscreen mode with correct aspect ratio |
| 14 | // JPM 06/06/2016 Visual Studio support |
| 15 | // |
| 16 | |
| 17 | #include "glwidget.h" |
| 18 | |
| 19 | #include "jaguar.h" |
| 20 | #include "settings.h" |
| 21 | #include "tom.h" |
| 22 | |
| 23 | #if defined(__GCCWIN32__) || defined(_MSC_VER) |
| 24 | #if defined(_MSC_VER) |
| 25 | #include <GL/gl.h> |
| 26 | #endif |
| 27 | // Apparently on win32, various OpenGL constants aren't pulled in. |
| 28 | #include <GL/glext.h> |
| 29 | #endif |
| 30 | |
| 31 | |
| 32 | GLWidget::GLWidget(QWidget * parent/*= 0*/): QGLWidget(parent), texture(0), |
| 33 | textureWidth(0), textureHeight(0), buffer(0), rasterWidth(326), rasterHeight(240), |
| 34 | offset(0), hideMouseTimeout(60) |
| 35 | { |
| 36 | // Screen pitch has to be the texture width (in 32-bit pixels)... |
| 37 | JaguarSetScreenPitch(1024); |
| 38 | setMouseTracking(true); |
| 39 | } |
| 40 | |
| 41 | |
| 42 | GLWidget::~GLWidget() |
| 43 | { |
| 44 | if (buffer) |
| 45 | delete[] buffer; |
| 46 | } |
| 47 | |
| 48 | |
| 49 | void GLWidget::initializeGL() |
| 50 | { |
| 51 | format().setDoubleBuffer(true); |
| 52 | resizeGL(rasterWidth, rasterHeight); |
| 53 | |
| 54 | glDisable(GL_ALPHA_TEST); |
| 55 | glDisable(GL_BLEND); |
| 56 | glDisable(GL_DEPTH_TEST); |
| 57 | glDisable(GL_POLYGON_SMOOTH); |
| 58 | glDisable(GL_STENCIL_TEST); |
| 59 | glEnable(GL_DITHER); |
| 60 | glEnable(GL_TEXTURE_2D); |
| 61 | glClearColor(0.0, 0.0, 0.0, 0.0); |
| 62 | |
| 63 | CreateTextures(); |
| 64 | } |
| 65 | |
| 66 | |
| 67 | void GLWidget::paintGL() |
| 68 | { |
| 69 | // If we're in fullscreen mode, we take the value of the screen width as |
| 70 | // set by MainWin, since it may be wider than what our aspect ratio allows. |
| 71 | // In that case, we adjust the viewport over so that it's centered on the |
| 72 | // screen. Otherwise, we simply take the width from our width() funtion |
| 73 | // which will always be correct in windowed mode. |
| 74 | |
| 75 | if (!fullscreen) |
| 76 | outputWidth = width(); |
| 77 | |
| 78 | // Bit 0 in VP is interlace flag. 0 = interlace, 1 = non-interlaced |
| 79 | double multiplier = (TOMGetVP() & 0x0001 ? 1.0 : 2.0); |
| 80 | unsigned outputHeight = height(); |
| 81 | |
| 82 | glMatrixMode(GL_PROJECTION); |
| 83 | glLoadIdentity(); |
| 84 | glOrtho(0, outputWidth, 0, outputHeight, -1.0, 1.0); |
| 85 | glViewport(0 + offset, 0, outputWidth, outputHeight); |
| 86 | |
| 87 | glMatrixMode(GL_MODELVIEW); |
| 88 | glLoadIdentity(); |
| 89 | |
| 90 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (vjs.glFilter ? GL_LINEAR : GL_NEAREST)); |
| 91 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (vjs.glFilter ? GL_LINEAR : GL_NEAREST)); |
| 92 | glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, TOMGetVideoModeWidth(), rasterHeight * multiplier, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, buffer); |
| 93 | |
| 94 | double w = (double)TOMGetVideoModeWidth() / (double)textureWidth; |
| 95 | double h = ((double)rasterHeight * multiplier) / (double)textureHeight; |
| 96 | unsigned u = outputWidth; |
| 97 | unsigned v = outputHeight; |
| 98 | |
| 99 | glBegin(GL_TRIANGLE_STRIP); |
| 100 | glTexCoord2f(0, 0); glVertex3i(0, v, 0); |
| 101 | glTexCoord2f(w, 0); glVertex3i(u, v, 0); |
| 102 | glTexCoord2f(0, h); glVertex3i(0, 0, 0); |
| 103 | glTexCoord2f(w, h); glVertex3i(u, 0, 0); |
| 104 | glEnd(); |
| 105 | } |
| 106 | |
| 107 | |
| 108 | void GLWidget::resizeGL(int /*width*/, int /*height*/) |
| 109 | { |
| 110 | //kludge [No, this is where it belongs!] |
| 111 | rasterHeight = (vjs.hardwareTypeNTSC ? VIRTUAL_SCREEN_HEIGHT_NTSC : VIRTUAL_SCREEN_HEIGHT_PAL); |
| 112 | |
| 113 | return; |
| 114 | } |
| 115 | |
| 116 | |
| 117 | // At some point, we'll have to create more than one texture to handle |
| 118 | // cases like Doom. Or have another go at TV type rendering; it will |
| 119 | // require a 2048x512 texture though. (Note that 512 is the correct height for |
| 120 | // interlaced screens; we won't have to change much here to support it.) |
| 121 | void GLWidget::CreateTextures(void) |
| 122 | { |
| 123 | // Seems that power of 2 sizes are still mandatory... |
| 124 | textureWidth = 1024; |
| 125 | textureHeight = 512; |
| 126 | buffer = new uint32_t[textureWidth * textureHeight]; |
| 127 | JaguarSetScreenBuffer(buffer); |
| 128 | |
| 129 | glGenTextures(1, &texture); |
| 130 | glBindTexture(GL_TEXTURE_2D, texture); |
| 131 | glPixelStorei(GL_UNPACK_ROW_LENGTH, textureWidth); |
| 132 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); |
| 133 | glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); |
| 134 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, textureWidth, textureHeight, 0, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8, NULL); |
| 135 | } |
| 136 | |
| 137 | |
| 138 | void GLWidget::HandleMouseHiding(void) |
| 139 | { |
| 140 | // Mouse watchdog timer handling. Basically, if the timeout value is |
| 141 | // greater than zero, decrement it. Otherwise, check for zero, if so, then |
| 142 | // hide the mouse and set the hideMouseTimeout value to -1 to signal that |
| 143 | // the mouse has been hidden. |
| 144 | if (hideMouseTimeout > 0) |
| 145 | hideMouseTimeout--; |
| 146 | else if (hideMouseTimeout == 0) |
| 147 | { |
| 148 | hideMouseTimeout--; |
| 149 | setCursor(Qt::BlankCursor); |
| 150 | } |
| 151 | } |
| 152 | |
| 153 | |
| 154 | // We use this as part of a watchdog system for hiding/unhiding the mouse. This |
| 155 | // part shows the mouse (if hidden) and resets the watchdog timer. |
| 156 | void GLWidget::CheckAndRestoreMouseCursor(void) |
| 157 | { |
| 158 | // Has the mouse been hidden? (-1 means mouse was hidden) |
| 159 | if (hideMouseTimeout == -1) |
| 160 | setCursor(Qt::ArrowCursor); |
| 161 | |
| 162 | hideMouseTimeout = 60; |
| 163 | } |
| 164 | |
| 165 | |
| 166 | // We check here for mouse movement; if there is any, show the mouse and reset |
| 167 | // the watchdog timer. |
| 168 | void GLWidget::mouseMoveEvent(QMouseEvent * /*event*/) |
| 169 | { |
| 170 | CheckAndRestoreMouseCursor(); |
| 171 | } |
| 172 | |
| 173 | |
| 174 | #if 0 |
| 175 | class RubyGLWidget: public QGLWidget |
| 176 | { |
| 177 | public: |
| 178 | GLuint texture; |
| 179 | unsigned textureWidth, textureHeight; |
| 180 | |
| 181 | uint32_t * buffer; |
| 182 | unsigned rasterWidth, rasterHeight; |
| 183 | |
| 184 | bool synchronize; |
| 185 | unsigned filter; |
| 186 | |
| 187 | void updateSynchronization() { |
| 188 | #ifdef __APPLE__ |
| 189 | makeCurrent(); |
| 190 | CGLContextObj context = CGLGetCurrentContext(); |
| 191 | GLint value = synchronize; //0 = draw immediately (no vsync), 1 = draw once per frame (vsync) |
| 192 | CGLSetParameter(context, kCGLCPSwapInterval, &value); |
| 193 | #endif |
| 194 | } |
| 195 | } * widget; |
| 196 | #endif |