| 1 | /*!
|
|---|
| 2 | \file lib/nviz/render.c
|
|---|
| 3 |
|
|---|
| 4 | \brief Nviz library -- GLX context manipulation
|
|---|
| 5 |
|
|---|
| 6 | Based on visualization/nviz/src/togl.c
|
|---|
| 7 |
|
|---|
| 8 | (C) 2008, 2010, 2018 by the GRASS Development Team
|
|---|
| 9 | This program is free software under the GNU General Public License
|
|---|
| 10 | (>=v2). Read the file COPYING that comes with GRASS for details.
|
|---|
| 11 |
|
|---|
| 12 | \author Updated/modified by Martin Landa <landa.martin gmail.com> (Google SoC 2008/2010)
|
|---|
| 13 | \author Support for framebuffer objects by Huidae Cho <grass4u gmail.com> (July 2018)
|
|---|
| 14 | */
|
|---|
| 15 |
|
|---|
| 16 | #include <grass/glocale.h>
|
|---|
| 17 | #include <grass/nviz.h>
|
|---|
| 18 |
|
|---|
| 19 | #if defined(OPENGL_WINDOWS) && defined(OPENGL_FBO)
|
|---|
| 20 | static int gl_funcs_found = 0;
|
|---|
| 21 | static PFNGLGENFRAMEBUFFERSPROC glGenFramebuffers;
|
|---|
| 22 | static PFNGLBINDFRAMEBUFFERPROC glBindFramebuffer;
|
|---|
| 23 | static PFNGLGENRENDERBUFFERSPROC glGenRenderbuffers;
|
|---|
| 24 | static PFNGLBINDRENDERBUFFERPROC glBindRenderbuffer;
|
|---|
| 25 | static PFNGLRENDERBUFFERSTORAGEPROC glRenderbufferStorage;
|
|---|
| 26 | static PFNGLFRAMEBUFFERRENDERBUFFERPROC glFramebufferRenderbuffer;
|
|---|
| 27 | static PFNGLCHECKFRAMEBUFFERSTATUSPROC glCheckFramebufferStatus;
|
|---|
| 28 |
|
|---|
| 29 | /* https://www.khronos.org/opengl/wiki/Load_OpenGL_Functions */
|
|---|
| 30 | static void *GetAnyGLFuncAddress(const char *name)
|
|---|
| 31 | {
|
|---|
| 32 | void *p = (void *)wglGetProcAddress(name);
|
|---|
| 33 | if (p == 0 || p == (void*)0x1 || p == (void*)0x2 || p == (void*)0x3 ||
|
|---|
| 34 | p == (void*)-1) {
|
|---|
| 35 | HMODULE module = LoadLibraryA("opengl32.dll");
|
|---|
| 36 | p = (void *)GetProcAddress(module, name);
|
|---|
| 37 | }
|
|---|
| 38 | if (!p)
|
|---|
| 39 | G_fatal_error(_("Unable to get function address for %s"), name);
|
|---|
| 40 | return p;
|
|---|
| 41 | }
|
|---|
| 42 |
|
|---|
| 43 | static void find_gl_funcs()
|
|---|
| 44 | {
|
|---|
| 45 | if (gl_funcs_found)
|
|---|
| 46 | return;
|
|---|
| 47 |
|
|---|
| 48 | glGenFramebuffers = (PFNGLGENFRAMEBUFFERSPROC)GetAnyGLFuncAddress("glGenFramebuffers");
|
|---|
| 49 | glBindFramebuffer = (PFNGLBINDFRAMEBUFFERPROC)GetAnyGLFuncAddress("glBindFramebuffer");
|
|---|
| 50 | glGenRenderbuffers = (PFNGLGENRENDERBUFFERSPROC)GetAnyGLFuncAddress("glGenRenderbuffers");
|
|---|
| 51 | glBindRenderbuffer = (PFNGLBINDRENDERBUFFERPROC)GetAnyGLFuncAddress("glBindRenderbuffer");
|
|---|
| 52 | glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC)GetAnyGLFuncAddress("glRenderbufferStorage");
|
|---|
| 53 | glFramebufferRenderbuffer = (PFNGLFRAMEBUFFERRENDERBUFFERPROC)GetAnyGLFuncAddress("glFramebufferRenderbuffer");
|
|---|
| 54 | glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSPROC)GetAnyGLFuncAddress("glCheckFramebufferStatus");
|
|---|
| 55 |
|
|---|
| 56 | gl_funcs_found = 1;
|
|---|
| 57 | }
|
|---|
| 58 | #endif
|
|---|
| 59 |
|
|---|
| 60 | /*!
|
|---|
| 61 | \brief Allocate memory for render window
|
|---|
| 62 |
|
|---|
| 63 | \return pointer to render_window struct
|
|---|
| 64 | \return NULL on failure
|
|---|
| 65 | */
|
|---|
| 66 | struct render_window *Nviz_new_render_window(void)
|
|---|
| 67 | {
|
|---|
| 68 | struct render_window *rwin;
|
|---|
| 69 |
|
|---|
| 70 | /* G_malloc() calls G_fatal_error() on failure */
|
|---|
| 71 | rwin = (struct render_window *)G_malloc(sizeof(struct render_window));
|
|---|
| 72 |
|
|---|
| 73 | return rwin;
|
|---|
| 74 | }
|
|---|
| 75 |
|
|---|
| 76 | /*!
|
|---|
| 77 | \brief Initialize render window
|
|---|
| 78 |
|
|---|
| 79 | \param win pointer to render_window struct
|
|---|
| 80 | */
|
|---|
| 81 | void Nviz_init_render_window(struct render_window *rwin)
|
|---|
| 82 | {
|
|---|
| 83 | #if defined(OPENGL_X11)
|
|---|
| 84 | rwin->displayId = NULL;
|
|---|
| 85 | rwin->contextId = NULL;
|
|---|
| 86 | rwin->pixmap = 0;
|
|---|
| 87 | rwin->windowId = 0;
|
|---|
| 88 | #elif defined(OPENGL_AQUA)
|
|---|
| 89 | #if defined(OPENGL_AGL)
|
|---|
| 90 | rwin->pixelFmtId = NULL;
|
|---|
| 91 | rwin->contextId = NULL;
|
|---|
| 92 | rwin->windowId = NULL;
|
|---|
| 93 | #else
|
|---|
| 94 | rwin->contextId = NULL;
|
|---|
| 95 | #endif
|
|---|
| 96 | #elif defined(OPENGL_WINDOWS)
|
|---|
| 97 | rwin->displayId = NULL;
|
|---|
| 98 | rwin->contextId = NULL;
|
|---|
| 99 | #endif
|
|---|
| 100 |
|
|---|
| 101 | rwin->width = 0;
|
|---|
| 102 | rwin->height = 0;
|
|---|
| 103 | }
|
|---|
| 104 |
|
|---|
| 105 | /*!
|
|---|
| 106 | \brief Free render window
|
|---|
| 107 |
|
|---|
| 108 | \param win pointer to render_window struct
|
|---|
| 109 | */
|
|---|
| 110 | void Nviz_destroy_render_window(struct render_window *rwin)
|
|---|
| 111 | {
|
|---|
| 112 | #if defined(OPENGL_X11)
|
|---|
| 113 | glXDestroyGLXPixmap(rwin->displayId, rwin->windowId);
|
|---|
| 114 | XFreePixmap(rwin->displayId, rwin->pixmap);
|
|---|
| 115 | glXDestroyContext(rwin->displayId, rwin->contextId);
|
|---|
| 116 | XCloseDisplay(rwin->displayId);
|
|---|
| 117 | #elif defined(OPENGL_AQUA)
|
|---|
| 118 | #if defined(OPENGL_AGL)
|
|---|
| 119 | aglDestroyPixelFormat(rwin->pixelFmtId);
|
|---|
| 120 | aglDestroyContext(rwin->contextId);
|
|---|
| 121 | aglDestroyPBuffer(rwin->windowId);
|
|---|
| 122 | #else
|
|---|
| 123 | CGLDestroyContext(rwin->contextId);
|
|---|
| 124 | #endif
|
|---|
| 125 | #elif defined(OPENGL_WINDOWS)
|
|---|
| 126 | wglDeleteContext(rwin->contextId);
|
|---|
| 127 | DeleteDC(rwin->displayId);
|
|---|
| 128 | #endif
|
|---|
| 129 |
|
|---|
| 130 | G_free((void *)rwin);
|
|---|
| 131 | }
|
|---|
| 132 |
|
|---|
| 133 | /*!
|
|---|
| 134 | \brief Create render window
|
|---|
| 135 |
|
|---|
| 136 | \param rwin pointer to render_window struct
|
|---|
| 137 | \param display display instance (NULL for offscreen)
|
|---|
| 138 | \param width window width
|
|---|
| 139 | \param height window height
|
|---|
| 140 |
|
|---|
| 141 | \return 0 on success
|
|---|
| 142 | \return -1 on error
|
|---|
| 143 | */
|
|---|
| 144 | int Nviz_create_render_window(struct render_window *rwin, void *display,
|
|---|
| 145 | int width, int height)
|
|---|
| 146 | {
|
|---|
| 147 | #if defined(OPENGL_X11)
|
|---|
| 148 | int attributeList[] = {
|
|---|
| 149 | GLX_RGBA,
|
|---|
| 150 | GLX_RED_SIZE, 1,
|
|---|
| 151 | GLX_GREEN_SIZE, 1,
|
|---|
| 152 | GLX_BLUE_SIZE, 1,
|
|---|
| 153 | GLX_DEPTH_SIZE, 1,
|
|---|
| 154 | #if !defined(OPENGL_FBO)
|
|---|
| 155 | GLX_DOUBLEBUFFER,
|
|---|
| 156 | #endif
|
|---|
| 157 | None
|
|---|
| 158 | };
|
|---|
| 159 | XVisualInfo *v;
|
|---|
| 160 |
|
|---|
| 161 | rwin->displayId = XOpenDisplay((char *)display);
|
|---|
| 162 | if (!rwin->displayId) {
|
|---|
| 163 | G_fatal_error(_("Bad server connection"));
|
|---|
| 164 | }
|
|---|
| 165 |
|
|---|
| 166 | v = glXChooseVisual(rwin->displayId,
|
|---|
| 167 | DefaultScreen(rwin->displayId), attributeList);
|
|---|
| 168 | if (!v) {
|
|---|
| 169 | G_warning(_("Unable to get visual info"));
|
|---|
| 170 | return -1;
|
|---|
| 171 | }
|
|---|
| 172 |
|
|---|
| 173 | rwin->contextId = glXCreateContext(rwin->displayId, v, NULL, GL_TRUE);
|
|---|
| 174 |
|
|---|
| 175 | if (!rwin->contextId) {
|
|---|
| 176 | G_warning(_("Unable to create rendering context"));
|
|---|
| 177 | return -1;
|
|---|
| 178 | }
|
|---|
| 179 |
|
|---|
| 180 | /* create win pixmap to render to (same depth as RootWindow) */
|
|---|
| 181 | rwin->pixmap = XCreatePixmap(rwin->displayId,
|
|---|
| 182 | RootWindow(rwin->displayId, v->screen),
|
|---|
| 183 | width, height, v->depth);
|
|---|
| 184 |
|
|---|
| 185 | /* create an off-screen GLX rendering area */
|
|---|
| 186 | rwin->windowId = glXCreateGLXPixmap(rwin->displayId, v, rwin->pixmap);
|
|---|
| 187 |
|
|---|
| 188 | XFree(v);
|
|---|
| 189 | #elif defined(OPENGL_AQUA)
|
|---|
| 190 | #if defined(OPENGL_AGL)
|
|---|
| 191 | int attributeList[] = {
|
|---|
| 192 | AGL_RGBA,
|
|---|
| 193 | AGL_RED_SIZE, 1,
|
|---|
| 194 | AGL_GREEN_SIZE, 1,
|
|---|
| 195 | AGL_BLUE_SIZE, 1,
|
|---|
| 196 | AGL_DEPTH_SIZE, 1,
|
|---|
| 197 | #if !defined(OPENGL_FBO)
|
|---|
| 198 | AGL_DOUBLEBUFFER,
|
|---|
| 199 | #endif
|
|---|
| 200 | AGL_NONE
|
|---|
| 201 | };
|
|---|
| 202 |
|
|---|
| 203 | /* TODO: open mac display */
|
|---|
| 204 |
|
|---|
| 205 | /* TODO: dev = NULL, ndev = 0 ? */
|
|---|
| 206 | rwin->pixelFmtId = aglChoosePixelFormat(NULL, 0, attributeList);
|
|---|
| 207 |
|
|---|
| 208 | rwin->contextId = aglCreateContext(rwin->pixelFmtId, NULL);
|
|---|
| 209 |
|
|---|
| 210 | /* create an off-screen AGL rendering area */
|
|---|
| 211 | aglCreatePBuffer(width, height, GL_TEXTURE_2D, GL_RGBA, 0, &(rwin->windowId));
|
|---|
| 212 | aglSetPBuffer(rwin->contextId, rwin->windowId, 0, 0, 0);
|
|---|
| 213 | #else
|
|---|
| 214 | CGLPixelFormatAttribute attributeList[] = {
|
|---|
| 215 | kCGLPFAColorSize, 24,
|
|---|
| 216 | kCGLPFADepthSize, 32,
|
|---|
| 217 | (CGLPixelFormatAttribute) 0
|
|---|
| 218 | };
|
|---|
| 219 | CGLPixelFormatObj pix;
|
|---|
| 220 | GLint nvirt;
|
|---|
| 221 | CGLError error;
|
|---|
| 222 |
|
|---|
| 223 | error = CGLChoosePixelFormat(attributeList, &pix, &nvirt);
|
|---|
| 224 | if (error) {
|
|---|
| 225 | G_warning(_("Unable to choose pixel format (CGL error = %d)"), error);
|
|---|
| 226 | return -1;
|
|---|
| 227 | }
|
|---|
| 228 |
|
|---|
| 229 | error = CGLCreateContext(pix, NULL, &rwin->contextId);
|
|---|
| 230 | if (error) {
|
|---|
| 231 | G_warning(_("Unable to create context (CGL error = %d)"), error);
|
|---|
| 232 | return -1;
|
|---|
| 233 | }
|
|---|
| 234 |
|
|---|
| 235 | CGLDestroyPixelFormat(pix);
|
|---|
| 236 | #endif
|
|---|
| 237 | #elif defined(OPENGL_WINDOWS)
|
|---|
| 238 | WNDCLASS wc = {0};
|
|---|
| 239 | HWND hWnd;
|
|---|
| 240 | PIXELFORMATDESCRIPTOR pfd = {
|
|---|
| 241 | sizeof(PIXELFORMATDESCRIPTOR), /* size of this pfd */
|
|---|
| 242 | 1, /* version number */
|
|---|
| 243 | PFD_DRAW_TO_WINDOW | /* support window */
|
|---|
| 244 | PFD_SUPPORT_OPENGL | /* support OpenGL */
|
|---|
| 245 | PFD_DOUBLEBUFFER, /* double buffered */
|
|---|
| 246 | PFD_TYPE_RGBA, /* RGBA type */
|
|---|
| 247 | 24, /* 24-bit color depth */
|
|---|
| 248 | 0, 0, 0, 0, 0, 0, /* color bits ignored */
|
|---|
| 249 | 0, /* no alpha buffer */
|
|---|
| 250 | 0, /* shift bit ignored */
|
|---|
| 251 | 0, /* no accumulation buffer */
|
|---|
| 252 | 0, 0, 0, 0, /* accum bits ignored */
|
|---|
| 253 | 32, /* 32-bit z-buffer */
|
|---|
| 254 | 0, /* no stencil buffer */
|
|---|
| 255 | 0, /* no auxiliary buffer */
|
|---|
| 256 | PFD_MAIN_PLANE, /* main layer */
|
|---|
| 257 | 0, /* reserved */
|
|---|
| 258 | 0, 0, 0 /* layer masks ignored */
|
|---|
| 259 | };
|
|---|
| 260 | int iPixelFormat;
|
|---|
| 261 |
|
|---|
| 262 | wc.lpfnWndProc = DefWindowProc;
|
|---|
| 263 | wc.lpszClassName = "nviz";
|
|---|
| 264 |
|
|---|
| 265 | if (!RegisterClass(&wc)) {
|
|---|
| 266 | G_warning(_("Unable to register window class"));
|
|---|
| 267 | return -1;
|
|---|
| 268 | }
|
|---|
| 269 |
|
|---|
| 270 | hWnd = CreateWindow(wc.lpszClassName, wc.lpszClassName, WS_POPUP,
|
|---|
| 271 | CW_USEDEFAULT, CW_USEDEFAULT, width, height,
|
|---|
| 272 | NULL, NULL, wc.hInstance, NULL);
|
|---|
| 273 |
|
|---|
| 274 | if (!hWnd) {
|
|---|
| 275 | G_warning(_("Unable to create window"));
|
|---|
| 276 | return -1;
|
|---|
| 277 | }
|
|---|
| 278 |
|
|---|
| 279 | rwin->displayId = GetDC(hWnd);
|
|---|
| 280 | iPixelFormat = ChoosePixelFormat(rwin->displayId, &pfd);
|
|---|
| 281 | SetPixelFormat(rwin->displayId, iPixelFormat, &pfd);
|
|---|
| 282 | rwin->contextId = wglCreateContext(rwin->displayId);
|
|---|
| 283 | #endif
|
|---|
| 284 |
|
|---|
| 285 | rwin->width = width;
|
|---|
| 286 | rwin->height = height;
|
|---|
| 287 |
|
|---|
| 288 | return 0;
|
|---|
| 289 | }
|
|---|
| 290 |
|
|---|
| 291 | /*!
|
|---|
| 292 | \brief Make window current for rendering
|
|---|
| 293 |
|
|---|
| 294 | \param win pointer to render_window struct
|
|---|
| 295 |
|
|---|
| 296 | \return 1 on success
|
|---|
| 297 | \return 0 on failure
|
|---|
| 298 | */
|
|---|
| 299 | int Nviz_make_current_render_window(const struct render_window *rwin)
|
|---|
| 300 | {
|
|---|
| 301 | #if defined(OPENGL_X11)
|
|---|
| 302 | if (!rwin->displayId || !rwin->contextId)
|
|---|
| 303 | return 0;
|
|---|
| 304 |
|
|---|
| 305 | if (rwin->contextId == glXGetCurrentContext())
|
|---|
| 306 | return 1;
|
|---|
| 307 |
|
|---|
| 308 | glXMakeCurrent(rwin->displayId, rwin->windowId, rwin->contextId);
|
|---|
| 309 | #elif defined(OPENGL_AQUA)
|
|---|
| 310 | #if defined(OPENGL_AGL)
|
|---|
| 311 | if (!rwin->contextId)
|
|---|
| 312 | return 0;
|
|---|
| 313 |
|
|---|
| 314 | if (rwin->contextId == aglGetCurrentContext())
|
|---|
| 315 | return 1;
|
|---|
| 316 |
|
|---|
| 317 | aglSetCurrentContext(rwin->contextId);
|
|---|
| 318 | #else
|
|---|
| 319 | CGLError error;
|
|---|
| 320 |
|
|---|
| 321 | error = CGLSetCurrentContext(rwin->contextId);
|
|---|
| 322 | if (error) {
|
|---|
| 323 | G_warning(_("Unable to set current context (CGL error = %d)"), error);
|
|---|
| 324 | return 0;
|
|---|
| 325 | }
|
|---|
| 326 | #endif
|
|---|
| 327 | #elif defined(OPENGL_WINDOWS)
|
|---|
| 328 | if (!rwin->displayId || !rwin->contextId)
|
|---|
| 329 | return 0;
|
|---|
| 330 |
|
|---|
| 331 | wglMakeCurrent(rwin->displayId, rwin->contextId);
|
|---|
| 332 | #endif
|
|---|
| 333 |
|
|---|
| 334 | #if defined(OPENGL_FBO)
|
|---|
| 335 | #if defined(OPENGL_WINDOWS)
|
|---|
| 336 | find_gl_funcs();
|
|---|
| 337 | #endif
|
|---|
| 338 |
|
|---|
| 339 | GLuint framebuf, renderbuf, depthbuf;
|
|---|
| 340 | GLenum status;
|
|---|
| 341 |
|
|---|
| 342 | glGenFramebuffers(1, &framebuf);
|
|---|
| 343 | glBindFramebuffer(GL_FRAMEBUFFER, framebuf);
|
|---|
| 344 |
|
|---|
| 345 | glGenRenderbuffers(1, &renderbuf);
|
|---|
| 346 | glBindRenderbuffer(GL_RENDERBUFFER, renderbuf);
|
|---|
| 347 | glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8,
|
|---|
| 348 | rwin->width, rwin->height);
|
|---|
| 349 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
|---|
| 350 | GL_RENDERBUFFER, renderbuf);
|
|---|
| 351 |
|
|---|
| 352 | glGenRenderbuffers(1, &depthbuf);
|
|---|
| 353 | glBindRenderbuffer(GL_RENDERBUFFER, depthbuf);
|
|---|
| 354 | glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24,
|
|---|
| 355 | rwin->width, rwin->height);
|
|---|
| 356 | glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT,
|
|---|
| 357 | GL_RENDERBUFFER, depthbuf);
|
|---|
| 358 |
|
|---|
| 359 | status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
|---|
| 360 | if (status != GL_FRAMEBUFFER_COMPLETE) {
|
|---|
| 361 | G_warning(_("Incomplete framebuffer status (status = %d)"), status);
|
|---|
| 362 | return 0;
|
|---|
| 363 | }
|
|---|
| 364 | #endif
|
|---|
| 365 |
|
|---|
| 366 | glViewport(0, 0, rwin->width, rwin->height);
|
|---|
| 367 |
|
|---|
| 368 | return 1;
|
|---|
| 369 | }
|
|---|