2 * Copyright (C) 2009 Andy Spencer <spenceal@rose-hulman.edu>
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.
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.
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/>.
20 #include <gdk/gdkkeysyms.h>
22 #include <gtk/gtkgl.h>
27 #include "aweather-gui.h"
28 #include "gis-world.h"
30 #include "gis-opengl.h"
35 G_DEFINE_TYPE(GisOpenGL, gis_opengl, G_TYPE_OBJECT);
36 static void gis_opengl_init(GisOpenGL *self)
38 g_debug("GisOpenGL: init");
40 static GObject *gis_opengl_constructor(GType gtype, guint n_properties,
41 GObjectConstructParam *properties)
43 g_debug("gis_opengl: constructor");
44 GObjectClass *parent_class = G_OBJECT_CLASS(gis_opengl_parent_class);
45 return parent_class->constructor(gtype, n_properties, properties);
47 static void gis_opengl_dispose(GObject *gobject)
49 g_debug("GisOpenGL: dispose");
50 GisOpenGL *self = GIS_OPENGL(gobject);
52 g_object_unref(self->world);
56 g_object_unref(self->view);
60 g_object_unref(self->drawing);
63 G_OBJECT_CLASS(gis_opengl_parent_class)->dispose(gobject);
65 static void gis_opengl_finalize(GObject *gobject)
67 g_debug("GisOpenGL: finalize");
68 G_OBJECT_CLASS(gis_opengl_parent_class)->finalize(gobject);
72 static void gis_opengl_class_init(GisOpenGLClass *klass)
74 g_debug("GisOpenGL: class_init");
75 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
76 gobject_class->constructor = gis_opengl_constructor;
77 gobject_class->dispose = gis_opengl_dispose;
78 gobject_class->finalize = gis_opengl_finalize;
84 gboolean on_button_press(GtkWidget *widget, GdkEventButton *event, GisOpenGL *self)
86 g_debug("GisOpenGL: on_drawing_button_press - Grabbing focus");
87 gtk_widget_grab_focus(GTK_WIDGET(self->drawing));
90 gboolean on_key_press(GtkWidget *widget, GdkEventKey *event, GisOpenGL *self)
92 g_debug("GisOpenGL: on_drawing_key_press - key=%x, state=%x, plus=%x",
93 event->keyval, event->state, GDK_plus);
95 gis_view_get_location(self->view, &x, &y, &z);
96 guint kv = event->keyval;
97 if (kv == GDK_Left || kv == GDK_h) gis_view_pan(self->view, -z/10, 0, 0);
98 else if (kv == GDK_Down || kv == GDK_j) gis_view_pan(self->view, 0, -z/10, 0);
99 else if (kv == GDK_Up || kv == GDK_k) gis_view_pan(self->view, 0, z/10, 0);
100 else if (kv == GDK_Right || kv == GDK_l) gis_view_pan(self->view, z/10, 0, 0);
101 else if (kv == GDK_minus || kv == GDK_o) gis_view_zoom(self->view, 10./9);
102 else if (kv == GDK_plus || kv == GDK_i) gis_view_zoom(self->view, 9./10);
103 else if (kv == GDK_H ) gis_view_rotate(self->view, 0, -10, 0);
104 else if (kv == GDK_J ) gis_view_rotate(self->view, 10, 0, 0);
105 else if (kv == GDK_K ) gis_view_rotate(self->view, -10, 0, 0);
106 else if (kv == GDK_L ) gis_view_rotate(self->view, 0, 10, 0);
111 gboolean on_map(GtkWidget *drawing, GdkEventConfigure *event, GisOpenGL *self)
113 g_debug("GisOpenGL: on_map");
117 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
118 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
120 /* Tessellation, "finding intersecting triangles" */
121 /* http://research.microsoft.com/pubs/70307/tr-2006-81.pdf */
122 /* http://www.opengl.org/wiki/Alpha_Blending */
123 glAlphaFunc(GL_GREATER,0.1);
124 glEnable(GL_ALPHA_TEST);
128 glDepthFunc(GL_LEQUAL);
129 glEnable(GL_DEPTH_TEST);
131 gis_opengl_end(self);
135 gboolean on_configure(GtkWidget *drawing, GdkEventConfigure *event, GisOpenGL *self)
137 g_debug("GisOpenGL: on_confiure");
138 gis_opengl_begin(self);
141 gis_view_get_location(self->view, &x, &y, &z);
143 /* Window is at 500 m from camera */
144 double width = GTK_WIDGET(self->drawing)->allocation.width;
145 double height = GTK_WIDGET(self->drawing)->allocation.height;
147 glViewport(0, 0, width, height);
150 glMatrixMode(GL_PROJECTION);
152 double ang = atan((height/2)/500);
154 //gluPerspective(r2d(ang)*2, width/height, -z-20, -z+20);
155 gluPerspective(r2d(ang)*2, width/height, 1, 500*1000);
157 gis_opengl_end(self);
161 gboolean on_expose(GtkWidget *drawing, GdkEventExpose *event, GisOpenGL *self)
163 g_debug("GisOpenGL: on_expose - begin");
164 gis_opengl_begin(self);
168 gis_view_get_location(self->view, &lx, &ly, &lz);
169 gis_view_get_rotation(self->view, &rx, &ry, &rz);
171 glMatrixMode(GL_MODELVIEW);
173 glTranslatef(lx, ly, lz);
174 glRotatef(rx, 1, 0, 0);
175 glRotatef(ry, 0, 1, 0);
176 glRotatef(rz, 0, 0, 1);
179 /* TODO: Figure out how to handle plugins:
180 * - Do they belong to AWeatherGui, GisOpenGL, etc?
182 for (GList *cur = self->plugins; cur; cur = cur->next) {
183 AWeatherPlugin *plugin = AWEATHER_PLUGIN(cur->data);
184 aweather_plugin_expose(plugin);
187 gis_opengl_end(self);
188 gis_opengl_flush(self);
189 g_debug("GisOpenGL: on_expose - end\n");
193 void on_state_changed(GisView *view,
194 gdouble x, gdouble y, gdouble z, GisOpenGL *self)
196 /* Reset clipping area and redraw */
197 on_configure(NULL, NULL, self);
198 gis_opengl_redraw(self);
204 GisOpenGL *gis_opengl_new(GisWorld *world, GisView *view, GtkDrawingArea *drawing)
206 g_debug("GisOpenGL: new");
207 GisOpenGL *self = g_object_new(GIS_TYPE_OPENGL, NULL);
210 self->drawing = drawing;
213 g_object_ref(drawing);
216 GdkGLConfig *glconfig = gdk_gl_config_new_by_mode(
217 GDK_GL_MODE_RGBA | GDK_GL_MODE_DEPTH |
218 GDK_GL_MODE_DOUBLE | GDK_GL_MODE_ALPHA);
220 g_error("Failed to create glconfig");
221 if (!gtk_widget_set_gl_capability(GTK_WIDGET(self->drawing),
222 glconfig, NULL, TRUE, GDK_GL_RGBA_TYPE))
223 g_error("GL lacks required capabilities");
225 g_signal_connect(self->view, "location-changed", G_CALLBACK(on_state_changed), self);
226 g_signal_connect(self->view, "rotation-changed", G_CALLBACK(on_state_changed), self);
228 g_signal_connect(self->drawing, "map-event", G_CALLBACK(on_map), self);
229 g_signal_connect(self->drawing, "configure-event", G_CALLBACK(on_configure), self);
230 g_signal_connect(self->drawing, "expose-event", G_CALLBACK(on_expose), self);
232 g_signal_connect(self->drawing, "button-press-event", G_CALLBACK(on_button_press), self);
233 g_signal_connect(self->drawing, "enter-notify-event", G_CALLBACK(on_button_press), self);
234 g_signal_connect(self->drawing, "key-press-event", G_CALLBACK(on_key_press), self);
238 void gis_opengl_redraw(GisOpenGL *self)
240 g_debug("GisOpenGL: gl_redraw");
241 gtk_widget_queue_draw(GTK_WIDGET(self->drawing));
243 void gis_opengl_begin(GisOpenGL *self)
245 g_assert(GIS_IS_OPENGL(self));
247 GdkGLContext *glcontext = gtk_widget_get_gl_context(GTK_WIDGET(self->drawing));
248 GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable(GTK_WIDGET(self->drawing));
250 if (!gdk_gl_drawable_gl_begin(gldrawable, glcontext))
251 g_assert_not_reached();
253 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
255 void gis_opengl_end(GisOpenGL *self)
257 g_assert(GIS_IS_OPENGL(self));
258 GdkGLDrawable *gldrawable = gdk_gl_drawable_get_current();
259 gdk_gl_drawable_gl_end(gldrawable);
261 void gis_opengl_flush(GisOpenGL *self)
263 g_assert(GIS_IS_OPENGL(self));
264 GdkGLDrawable *gldrawable = gdk_gl_drawable_get_current();
265 if (gdk_gl_drawable_is_double_buffered(gldrawable))
266 gdk_gl_drawable_swap_buffers(gldrawable);
269 gdk_gl_drawable_gl_end(gldrawable);