]> Pileus Git - grits/blob - src/gis-opengl.c
Splitting out libgis
[grits] / src / gis-opengl.c
1 /*
2  * Copyright (C) 2009 Andy Spencer <spenceal@rose-hulman.edu>
3  * 
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  * 
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  * 
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 /* Tessellation, "finding intersecting triangles" */
19 /* http://research.microsoft.com/pubs/70307/tr-2006-81.pdf */
20 /* http://www.opengl.org/wiki/Alpha_Blending */
21
22 #include <config.h>
23 #include <math.h>
24 #include <gdk/gdkkeysyms.h>
25 #include <gtk/gtk.h>
26 #include <gtk/gtkgl.h>
27 #include <GL/gl.h>
28 #include <GL/glu.h>
29
30 #include "gis-opengl.h"
31 #include "roam.h"
32 #include "wms.h"
33
34 #define FOV_DIST   2000.0
35 #define MPPX(dist) (4*dist/FOV_DIST)
36
37 // #define ROAM_DEBUG
38
39 /*************
40  * ROAM Code *
41  *************/
42 void roam_queue_draw(WmsCacheNode *node, gpointer _self)
43 {
44         gtk_widget_queue_draw(GTK_WIDGET(_self));
45 }
46
47 void roam_height_func(RoamPoint *point, gpointer _self)
48 {
49         GisOpenGL *self = _self;
50
51         gdouble lat, lon, elev;
52         xyz2lle(point->x, point->y, point->z, &lat, &lon, &elev);
53
54 #ifdef ROAM_DEBUG
55         lle2xyz(lat, lon, 0, &point->x, &point->y, &point->z);
56         return;
57 #endif
58
59         gdouble cam_lle[3], cam_xyz[3];
60         gis_view_get_location(self->view, &cam_lle[0], &cam_lle[1], &cam_lle[2]);
61         lle2xyz(cam_lle[0], cam_lle[1], cam_lle[2], &cam_xyz[0], &cam_xyz[1], &cam_xyz[2]);
62
63         gdouble res = MPPX(distd(cam_xyz, (double*)point));
64         //g_message("lat=%f, lon=%f, res=%f", lat, lon, res);
65
66         point->node = wms_info_fetch_cache(self->srtm, point->node,
67                         res, lat, lon, NULL, roam_queue_draw, self);
68
69         if (point->node) {
70                 WmsBil *bil = point->node->data;
71
72                 gint w = bil->width;
73                 gint h = bil->height;
74
75                 gdouble xmin  = point->node->latlon[0];
76                 gdouble ymin  = point->node->latlon[1];
77                 gdouble xmax  = point->node->latlon[2];
78                 gdouble ymax  = point->node->latlon[3];
79
80                 gdouble xdist = xmax - xmin;
81                 gdouble ydist = ymax - ymin;
82
83                 gdouble x =    (lon-xmin)/xdist  * w;
84                 gdouble y = (1-(lat-ymin)/ydist) * h;
85
86                 gdouble x_rem = x - (int)x;
87                 gdouble y_rem = y - (int)y;
88                 guint x_flr = (int)x;
89                 guint y_flr = (int)y;
90
91                 /* TODO: Fix interpolation at edges:
92                  *   - Pad these at the edges instead of wrapping/truncating
93                  *   - Figure out which pixels to index (is 0,0 edge, center, etc) */
94                 gint16 px00 = bil->data[MIN((y_flr  ),h-1)*w + MIN((x_flr  ),w-1)];
95                 gint16 px10 = bil->data[MIN((y_flr  ),h-1)*w + MIN((x_flr+1),w-1)];
96                 gint16 px01 = bil->data[MIN((y_flr+1),h-1)*w + MIN((x_flr  ),w-1)];
97                 gint16 px11 = bil->data[MIN((y_flr+1),h-1)*w + MIN((x_flr+1),w-1)];
98
99                 elev =  px00 * (1-x_rem) * (1-y_rem) +
100                         px10 * (  x_rem) * (1-y_rem) +
101                         px01 * (1-x_rem) * (  y_rem) +
102                         px11 * (  x_rem) * (  y_rem);
103                 //g_message("elev=%f -- %hd %hd %hd %hd",
104                 //      elev, px00, px10, px01, px11);
105         } else {
106                 elev = 0;
107         }
108
109         lle2xyz(lat, lon, elev, &point->x, &point->y, &point->z);
110 }
111
112 void roam_tri_func(RoamTriangle *tri, gpointer _self)
113 {
114 #ifdef ROAM_DEBUG
115         glBegin(GL_TRIANGLES);
116         glNormal3dv(tri->p.r->norm); glVertex3dv((double*)tri->p.r); 
117         glNormal3dv(tri->p.m->norm); glVertex3dv((double*)tri->p.m); 
118         glNormal3dv(tri->p.l->norm); glVertex3dv((double*)tri->p.l); 
119         glEnd();
120         return;
121 #endif
122
123         GisOpenGL *self = _self;
124         if (tri->error < 0) return;
125
126         /* Get lat-lon min and maxes for the triangle */
127         gdouble lat[3], lon[3], elev[3];
128         xyz2lle(tri->p.r->x, tri->p.r->y, tri->p.r->z, &lat[0], &lon[0], &elev[0]);
129         xyz2lle(tri->p.m->x, tri->p.m->y, tri->p.m->z, &lat[1], &lon[1], &elev[1]);
130         xyz2lle(tri->p.l->x, tri->p.l->y, tri->p.l->z, &lat[2], &lon[2], &elev[2]);
131         gdouble lat_max = MAX(MAX(lat[0], lat[1]), lat[2]);
132         gdouble lat_min = MIN(MIN(lat[0], lat[1]), lat[2]);
133         gdouble lat_avg = (lat_min+lat_max)/2;
134         gdouble lon_max = MAX(MAX(lon[0], lon[1]), lon[2]);
135         gdouble lon_min = MIN(MIN(lon[0], lon[1]), lon[2]);
136         gdouble lon_avg = (lon_min+lon_max)/2;
137
138         /* Get target resolution */
139         gdouble cam_lle[3], cam_xyz[3];
140         gis_view_get_location(self->view, &cam_lle[0], &cam_lle[1], &cam_lle[2]);
141         lle2xyz(cam_lle[0], cam_lle[1], cam_lle[2], &cam_xyz[0], &cam_xyz[1], &cam_xyz[2]);
142         gdouble distr = distd(cam_xyz, (double*)tri->p.r);
143         gdouble distm = distd(cam_xyz, (double*)tri->p.m);
144         gdouble distl = distd(cam_xyz, (double*)tri->p.l);
145         double res = MPPX(MIN(MIN(distr, distm), distl));
146
147         /* TODO: 
148          *   - Fetch needed textures, not all corners
149          *   - Also fetch center textures that aren't touched by a corner
150          *   - Idea: send {lat,lon}{min,max} to fetch_cache and handle it in the recursion */
151         /* Fetch textures */
152         tri->nodes[0] = wms_info_fetch_cache(self->bmng, tri->nodes[0], res, lat_min, lon_min, NULL, roam_queue_draw, self);
153         tri->nodes[1] = wms_info_fetch_cache(self->bmng, tri->nodes[1], res, lat_max, lon_min, NULL, roam_queue_draw, self);
154         tri->nodes[2] = wms_info_fetch_cache(self->bmng, tri->nodes[2], res, lat_min, lon_max, NULL, roam_queue_draw, self);
155         tri->nodes[3] = wms_info_fetch_cache(self->bmng, tri->nodes[3], res, lat_max, lon_max, NULL, roam_queue_draw, self);
156         tri->nodes[4] = wms_info_fetch_cache(self->bmng, tri->nodes[4], res, lat_avg, lon_avg, NULL, roam_queue_draw, self);
157         /* Hopefully get all textures at the same resolution to prevent overlaps */
158         //gdouble maxres = 0;
159         //for (int i = 0; i < 5; i++)
160         //      if (tri->nodes[i] && tri->nodes[i]->res > maxres)
161         //              maxres = tri->nodes[i]->res;
162         //if (maxres != 0) {
163         //      tri->nodes[0] = wms_info_fetch_cache(self->bmng, tri->nodes[0], maxres, lat_min, lon_min, NULL, roam_queue_draw, self);
164         //      tri->nodes[1] = wms_info_fetch_cache(self->bmng, tri->nodes[1], maxres, lat_max, lon_min, NULL, roam_queue_draw, self);
165         //      tri->nodes[2] = wms_info_fetch_cache(self->bmng, tri->nodes[2], maxres, lat_min, lon_max, NULL, roam_queue_draw, self);
166         //      tri->nodes[3] = wms_info_fetch_cache(self->bmng, tri->nodes[3], maxres, lat_max, lon_max, NULL, roam_queue_draw, self);
167         //      tri->nodes[4] = wms_info_fetch_cache(self->bmng, tri->nodes[4], maxres, lat_avg, lon_avg, NULL, roam_queue_draw, self);
168         //}
169
170         /* Vertex color for hieght map viewing, 8848m == Everest */
171         gfloat colors[] = {
172                 (elev[0]-EARTH_R)/8848,
173                 (elev[1]-EARTH_R)/8848,
174                 (elev[2]-EARTH_R)/8848,
175         };
176
177         /* Draw each texture */
178         /* TODO: Prevent double exposure when of hi-res textures on top of
179          * low-res textures when some high-res textures are not yet loaded. */
180         glBlendFunc(GL_ONE, GL_ZERO);
181         for (int i = 0; i < 5; i++) {
182                 /* Skip missing textures */
183                 if (tri->nodes[i] == NULL)
184                         continue;
185                 /* Skip already drawn textures */
186                 switch (i) {
187                 case 4: if (tri->nodes[i] == tri->nodes[3]) continue;
188                 case 3: if (tri->nodes[i] == tri->nodes[2]) continue;
189                 case 2: if (tri->nodes[i] == tri->nodes[1]) continue;
190                 case 1: if (tri->nodes[i] == tri->nodes[0]) continue;
191                 }
192
193                 WmsCacheNode *node = tri->nodes[i];
194
195                 if (node->latlon[0] == -180) {
196                         if (lon[0] < -90 || lon[1] < -90 || lon[2] < -90) {
197                                 if (lon[0] > 90) lon[0] -= 360;
198                                 if (lon[1] > 90) lon[1] -= 360;
199                                 if (lon[2] > 90) lon[2] -= 360;
200                         }
201                 } else if (node->latlon[2] == 180.0) {
202                         if (lon[0] < -90) lon[0] += 360;
203                         if (lon[1] < -90) lon[1] += 360;
204                         if (lon[2] < -90) lon[2] += 360;
205                 }
206
207                 gdouble xmin  = node->latlon[0];
208                 gdouble ymin  = node->latlon[1];
209                 gdouble xmax  = node->latlon[2];
210                 gdouble ymax  = node->latlon[3];
211
212                 gdouble xdist = xmax - xmin;
213                 gdouble ydist = ymax - ymin;
214
215                 gdouble xy[][3] = {
216                         {(lon[0]-xmin)/xdist, 1-(lat[0]-ymin)/ydist},
217                         {(lon[1]-xmin)/xdist, 1-(lat[1]-ymin)/ydist},
218                         {(lon[2]-xmin)/xdist, 1-(lat[2]-ymin)/ydist},
219                 };
220
221                 glBindTexture(GL_TEXTURE_2D, *(guint*)node->data);
222
223                 glBegin(GL_TRIANGLES);
224                 glColor3fv(colors); glNormal3dv(tri->p.r->norm); glTexCoord2dv(xy[0]); glVertex3dv((double*)tri->p.r); 
225                 glColor3fv(colors); glNormal3dv(tri->p.m->norm); glTexCoord2dv(xy[1]); glVertex3dv((double*)tri->p.m); 
226                 glColor3fv(colors); glNormal3dv(tri->p.l->norm); glTexCoord2dv(xy[2]); glVertex3dv((double*)tri->p.l); 
227                 glEnd();
228                 glBlendFunc(GL_ONE, GL_ONE);
229         }
230 }
231
232 static void set_camera(GisOpenGL *self)
233 {
234         glMatrixMode(GL_MODELVIEW);
235         glLoadIdentity();
236         double lat, lon, elev, rx, ry, rz;
237         gis_view_get_location(self->view, &lat, &lon, &elev);
238         gis_view_get_rotation(self->view, &rx, &ry, &rz);
239         glRotatef(rx, 1, 0, 0);
240         glRotatef(rz, 0, 0, 1);
241         glTranslatef(0, 0, -elev2rad(elev));
242         glRotatef(lat, 1, 0, 0);
243         glRotatef(-lon, 0, 1, 0);
244 }
245
246 static void set_visuals(GisOpenGL *self)
247 {
248         /* Lighting */
249         glMatrixMode(GL_MODELVIEW);
250         glLoadIdentity();
251 #ifdef ROAM_DEBUG
252         float light_ambient[]  = {0.7f, 0.7f, 0.7f, 1.0f};
253         float light_diffuse[]  = {2.0f, 2.0f, 2.0f, 1.0f};
254 #else
255         float light_ambient[]  = {0.2f, 0.2f, 0.2f, 1.0f};
256         float light_diffuse[]  = {5.0f, 5.0f, 5.0f, 1.0f};
257 #endif
258         float light_position[] = {-13*EARTH_R, 1*EARTH_R, 3*EARTH_R, 1.0f};
259         glLightfv(GL_LIGHT0, GL_AMBIENT,  light_ambient);
260         glLightfv(GL_LIGHT0, GL_DIFFUSE,  light_diffuse);
261         glLightfv(GL_LIGHT0, GL_POSITION, light_position);
262         glEnable(GL_LIGHT0);
263         glEnable(GL_LIGHTING);
264
265         float material_ambient[]  = {0.2, 0.2, 0.2, 1.0};
266         float material_diffuse[]  = {0.8, 0.8, 0.8, 1.0};
267         float material_specular[] = {0.0, 0.0, 0.0, 1.0};
268         float material_emission[] = {0.0, 0.0, 0.0, 1.0};
269         glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT,  material_ambient);
270         glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE,  material_diffuse);
271         glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, material_specular);
272         glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, material_emission);
273         glDisable(GL_TEXTURE_2D);
274         glDisable(GL_COLOR_MATERIAL);
275
276         /* Camera */
277         set_camera(self);
278
279         /* Misc */
280         gdouble lat, lon, elev;
281         gis_view_get_location(self->view, &lat, &lon, &elev);
282         gdouble rg   = MAX(0, 1-(elev/20000));
283         gdouble blue = MAX(0, 1-(elev/50000));
284         glClearColor(MIN(0.65,rg), MIN(0.65,rg), MIN(1,blue), 1.0f);
285
286         glDisable(GL_ALPHA_TEST);
287
288         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
289         glEnable(GL_BLEND);
290
291 #ifndef ROAM_DEBUG
292         glCullFace(GL_BACK);
293         glEnable(GL_CULL_FACE);
294 #endif
295
296         glClearDepth(1.0);
297         glDepthFunc(GL_LEQUAL);
298         glEnable(GL_DEPTH_TEST);
299
300         glEnable(GL_LINE_SMOOTH);
301
302         glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
303         //glShadeModel(GL_FLAT);
304 }
305
306
307 /*************
308  * Callbacks *
309  *************/
310 static void on_realize(GisOpenGL *self, gpointer _)
311 {
312         set_visuals(self);
313 }
314 static gboolean on_configure(GisOpenGL *self, GdkEventConfigure *event, gpointer _)
315 {
316         g_debug("GisOpenGL: on_confiure");
317         gis_opengl_begin(self);
318
319         double width  = GTK_WIDGET(self)->allocation.width;
320         double height = GTK_WIDGET(self)->allocation.height;
321         glViewport(0, 0, width, height);
322
323         glMatrixMode(GL_PROJECTION);
324         glLoadIdentity();
325         double ang = atan(height/FOV_DIST);
326         gluPerspective(rad2deg(ang)*2, width/height, 1, 20*EARTH_R);
327
328 #ifndef ROAM_DEBUG
329         roam_sphere_update(self->sphere);
330 #endif
331
332         gis_opengl_end(self);
333         return FALSE;
334 }
335
336 static void on_expose_plugin(GisPlugin *plugin, gchar *name, GisOpenGL *self)
337 {
338         set_visuals(self);
339         glMatrixMode(GL_PROJECTION); glPushMatrix();
340         glMatrixMode(GL_MODELVIEW);  glPushMatrix();
341         gis_plugin_expose(plugin);
342         glMatrixMode(GL_PROJECTION); glPopMatrix();
343         glMatrixMode(GL_MODELVIEW);  glPopMatrix();
344 }
345 static gboolean on_expose(GisOpenGL *self, GdkEventExpose *event, gpointer _)
346 {
347         g_debug("GisOpenGL: on_expose - begin");
348         gis_opengl_begin(self);
349
350         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
351
352 #ifndef ROAM_DEBUG
353         set_visuals(self);
354         glEnable(GL_TEXTURE_2D);
355         roam_sphere_draw(self->sphere);
356 #endif
357
358 #ifdef  ROAM_DEBUG
359         set_visuals(self);
360         glColor4f(0.0, 0.0, 9.0, 0.6);
361         glDisable(GL_TEXTURE_2D);
362         glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
363         roam_sphere_draw(self->sphere);
364 #endif
365
366         //glDisable(GL_TEXTURE_2D);
367         //glEnable(GL_COLOR_MATERIAL);
368         //glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
369         //roam_sphere_draw(self->sphere);
370
371         gis_plugins_foreach(self->plugins, G_CALLBACK(on_expose_plugin), self);
372
373         set_visuals(self);
374         gis_opengl_end(self);
375         gis_opengl_flush(self);
376         g_debug("GisOpenGL: on_expose - end\n");
377         return FALSE;
378 }
379
380 static gboolean on_button_press(GisOpenGL *self, GdkEventButton *event, gpointer _)
381 {
382         g_debug("GisOpenGL: on_button_press - Grabbing focus");
383         gtk_widget_grab_focus(GTK_WIDGET(self));
384         return TRUE;
385 }
386
387 static gboolean on_key_press(GisOpenGL *self, GdkEventKey *event, gpointer _)
388 {
389         g_debug("GisOpenGL: on_key_press - key=%x, state=%x, plus=%x",
390                         event->keyval, event->state, GDK_plus);
391
392         double lat, lon, elev, pan;
393         gis_view_get_location(self->view, &lat, &lon, &elev);
394         pan = MIN(elev/(EARTH_R/2), 30);
395         guint kv = event->keyval;
396         if      (kv == GDK_Left  || kv == GDK_h) gis_view_pan(self->view,  0,  -pan, 0);
397         else if (kv == GDK_Down  || kv == GDK_j) gis_view_pan(self->view, -pan, 0,   0);
398         else if (kv == GDK_Up    || kv == GDK_k) gis_view_pan(self->view,  pan, 0,   0);
399         else if (kv == GDK_Right || kv == GDK_l) gis_view_pan(self->view,  0,   pan, 0);
400         else if (kv == GDK_minus || kv == GDK_o) gis_view_zoom(self->view, 10./9);
401         else if (kv == GDK_plus  || kv == GDK_i) gis_view_zoom(self->view, 9./10);
402         else if (kv == GDK_H) gis_view_rotate(self->view,  0,  0, -10);
403         else if (kv == GDK_J) gis_view_rotate(self->view,  10, 0,  0);
404         else if (kv == GDK_K) gis_view_rotate(self->view, -10, 0,  0);
405         else if (kv == GDK_L) gis_view_rotate(self->view,  0,  0,  10);
406
407         /* Testing */
408 #ifdef ROAM_DEBUG
409         else if (kv == GDK_n) roam_sphere_split_one(self->sphere);
410         else if (kv == GDK_p) roam_sphere_merge_one(self->sphere);
411         else if (kv == GDK_r) roam_sphere_split_merge(self->sphere);
412         else if (kv == GDK_u) roam_sphere_update(self->sphere);
413         gtk_widget_queue_draw(GTK_WIDGET(self));
414 #endif
415
416         return TRUE;
417 }
418
419 static void on_view_changed(GisView *view,
420                 gdouble _1, gdouble _2, gdouble _3, GisOpenGL *self)
421 {
422         gis_opengl_begin(self);
423         set_visuals(self);
424 #ifndef ROAM_DEBUG
425         roam_sphere_update(self->sphere);
426 #endif
427         gis_opengl_redraw(self);
428         gis_opengl_end(self);
429 }
430
431 static gboolean on_idle(GisOpenGL *self)
432 {
433         gis_opengl_begin(self);
434         if (roam_sphere_split_merge(self->sphere))
435                 gis_opengl_redraw(self);
436         gis_opengl_end(self);
437         return TRUE;
438 }
439
440
441 /***********
442  * Methods *
443  ***********/
444 GisOpenGL *gis_opengl_new(GisWorld *world, GisView *view, GisPlugins *plugins)
445 {
446         g_debug("GisOpenGL: new");
447         GisOpenGL *self = g_object_new(GIS_TYPE_OPENGL, NULL);
448         self->world   = world;
449         self->view    = view;
450         self->plugins = plugins;
451         g_object_ref(world);
452         g_object_ref(view);
453
454         g_signal_connect(self->view, "location-changed", G_CALLBACK(on_view_changed), self);
455         g_signal_connect(self->view, "rotation-changed", G_CALLBACK(on_view_changed), self);
456
457         /* TODO: update point eights sometime later so we have heigh-res heights for them */
458         self->sphere = roam_sphere_new(roam_tri_func, roam_height_func, self);
459
460         return g_object_ref(self);
461 }
462
463 void gis_opengl_center_position(GisOpenGL *self, gdouble lat, gdouble lon, gdouble elev)
464 {
465         set_camera(self);
466         glRotatef(lon, 0, 1, 0);
467         glRotatef(-lat, 1, 0, 0);
468         glTranslatef(0, 0, elev2rad(elev));
469 }
470
471 void gis_opengl_redraw(GisOpenGL *self)
472 {
473         g_debug("GisOpenGL: gl_redraw");
474         gtk_widget_queue_draw(GTK_WIDGET(self));
475 }
476 void gis_opengl_begin(GisOpenGL *self)
477 {
478         g_assert(GIS_IS_OPENGL(self));
479
480         GdkGLContext   *glcontext  = gtk_widget_get_gl_context(GTK_WIDGET(self));
481         GdkGLDrawable  *gldrawable = gtk_widget_get_gl_drawable(GTK_WIDGET(self));
482
483         if (!gdk_gl_drawable_gl_begin(gldrawable, glcontext))
484                 g_assert_not_reached();
485 }
486 void gis_opengl_end(GisOpenGL *self)
487 {
488         g_assert(GIS_IS_OPENGL(self));
489         GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable(GTK_WIDGET(self));
490         gdk_gl_drawable_gl_end(gldrawable);
491 }
492 void gis_opengl_flush(GisOpenGL *self)
493 {
494         g_assert(GIS_IS_OPENGL(self));
495         GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable(GTK_WIDGET(self));
496         if (gdk_gl_drawable_is_double_buffered(gldrawable))
497                 gdk_gl_drawable_swap_buffers(gldrawable);
498         else
499                 glFlush();
500         gdk_gl_drawable_gl_end(gldrawable);
501 }
502
503
504 /****************
505  * GObject code *
506  ****************/
507 G_DEFINE_TYPE(GisOpenGL, gis_opengl, GTK_TYPE_DRAWING_AREA);
508 static void gis_opengl_init(GisOpenGL *self)
509 {
510         g_debug("GisOpenGL: init");
511         self->bmng = wms_info_new_for_bmng(NULL, NULL);
512         self->srtm = wms_info_new_for_srtm(NULL, NULL);
513
514         /* OpenGL setup */
515         GdkGLConfig *glconfig = gdk_gl_config_new_by_mode(
516                         GDK_GL_MODE_RGBA   | GDK_GL_MODE_DEPTH |
517                         GDK_GL_MODE_DOUBLE | GDK_GL_MODE_ALPHA);
518         if (!glconfig)
519                 g_error("Failed to create glconfig");
520         if (!gtk_widget_set_gl_capability(GTK_WIDGET(self),
521                                 glconfig, NULL, TRUE, GDK_GL_RGBA_TYPE))
522                 g_error("GL lacks required capabilities");
523         g_object_unref(glconfig);
524
525         gtk_widget_set_size_request(GTK_WIDGET(self), 600, 550);
526         gtk_widget_set_events(GTK_WIDGET(self),
527                         GDK_BUTTON_PRESS_MASK |
528                         GDK_ENTER_NOTIFY_MASK |
529                         GDK_KEY_PRESS_MASK);
530         g_object_set(self, "can-focus", TRUE, NULL);
531
532 #ifndef ROAM_DEBUG
533         self->sm_source = g_timeout_add(10, (GSourceFunc)on_idle, self);
534 #endif
535
536         g_signal_connect(self, "realize",            G_CALLBACK(on_realize),      NULL);
537         g_signal_connect(self, "configure-event",    G_CALLBACK(on_configure),    NULL);
538         g_signal_connect(self, "expose-event",       G_CALLBACK(on_expose),       NULL);
539
540         g_signal_connect(self, "button-press-event", G_CALLBACK(on_button_press), NULL);
541         g_signal_connect(self, "enter-notify-event", G_CALLBACK(on_button_press), NULL);
542         g_signal_connect(self, "key-press-event",    G_CALLBACK(on_key_press),    NULL);
543 }
544 static GObject *gis_opengl_constructor(GType gtype, guint n_properties,
545                 GObjectConstructParam *properties)
546 {
547         g_debug("GisOpengl: constructor");
548         GObjectClass *parent_class = G_OBJECT_CLASS(gis_opengl_parent_class);
549         return parent_class->constructor(gtype, n_properties, properties);
550 }
551 static void gis_opengl_dispose(GObject *_self)
552 {
553         g_debug("GisOpenGL: dispose");
554         GisOpenGL *self = GIS_OPENGL(_self);
555         if (self->sm_source) {
556                 g_source_remove(self->sm_source);
557                 self->sm_source = 0;
558         }
559         if (self->sphere) {
560                 roam_sphere_free(self->sphere);
561                 self->sphere = NULL;
562         }
563         if (self->world) {
564                 g_object_unref(self->world);
565                 self->world = NULL;
566         }
567         if (self->view) {
568                 g_object_unref(self->view);
569                 self->view = NULL;
570         }
571         G_OBJECT_CLASS(gis_opengl_parent_class)->dispose(_self);
572 }
573 static void gis_opengl_finalize(GObject *_self)
574 {
575         g_debug("GisOpenGL: finalize");
576         GisOpenGL *self = GIS_OPENGL(_self);
577         wms_info_free(self->bmng);
578         wms_info_free(self->srtm);
579         G_OBJECT_CLASS(gis_opengl_parent_class)->finalize(_self);
580 }
581 static void gis_opengl_class_init(GisOpenGLClass *klass)
582 {
583         g_debug("GisOpenGL: class_init");
584         GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
585         gobject_class->constructor  = gis_opengl_constructor;
586         gobject_class->dispose      = gis_opengl_dispose;
587         gobject_class->finalize     = gis_opengl_finalize;
588 }