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