X-Git-Url: http://pileus.org/git/?p=grits;a=blobdiff_plain;f=src%2Fgis-opengl.c;h=65821d74038872e2f04d09d51d3dd77466b88e25;hp=e18a309cbfee65c635bc599f4f6acd3e50e75393;hb=fc67f407d6493f57b5ab0ed32c4902cbf304e96b;hpb=b92087ed5280da4c84ea927a73a8d7bb461e7d47 diff --git a/src/gis-opengl.c b/src/gis-opengl.c index e18a309..65821d7 100644 --- a/src/gis-opengl.c +++ b/src/gis-opengl.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2009 Andy Spencer + * Copyright (C) 2009-2010 Andy Spencer * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -30,9 +30,12 @@ #include "gis-opengl.h" #include "gis-util.h" -#include "gis-object.h" #include "roam.h" +#include "objects/gis-object.h" +#include "objects/gis-marker.h" +#include "objects/gis-callback.h" + #define FOV_DIST 2000.0 #define MPPX(dist) (4*dist/FOV_DIST) @@ -41,24 +44,6 @@ /*********** * Helpers * ***********/ -static void _gis_opengl_begin(GisOpenGL *self) -{ - g_assert(GIS_IS_OPENGL(self)); - - GdkGLContext *glcontext = gtk_widget_get_gl_context(GTK_WIDGET(self)); - GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable(GTK_WIDGET(self)); - - if (!gdk_gl_drawable_gl_begin(gldrawable, glcontext)) - g_assert_not_reached(); -} - -static void _gis_opengl_end(GisOpenGL *self) -{ - g_assert(GIS_IS_OPENGL(self)); - GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable(GTK_WIDGET(self)); - gdk_gl_drawable_gl_end(gldrawable); -} - static void _set_visuals(GisOpenGL *self) { glMatrixMode(GL_MODELVIEW); @@ -128,6 +113,89 @@ static void _set_visuals(GisOpenGL *self) /******************** * Object handleing * ********************/ +static void _draw_tile(GisOpenGL *self, GisTile *tile) +{ + if (!tile || !tile->data) + return; + GList *triangles = roam_sphere_get_intersect(self->sphere, FALSE, + tile->edge.n, tile->edge.s, tile->edge.e, tile->edge.w); + if (!triangles) + g_warning("GisOpenGL: _draw_tiles - No triangles to draw: edges=%f,%f,%f,%f", + tile->edge.n, tile->edge.s, tile->edge.e, tile->edge.w); + //g_message("drawing %4d triangles for tile edges=%7.2f,%7.2f,%7.2f,%7.2f", + // g_list_length(triangles), tile->edge.n, tile->edge.s, tile->edge.e, tile->edge.w); + for (GList *cur = triangles; cur; cur = cur->next) { + RoamTriangle *tri = cur->data; + + gdouble lat[3] = {tri->p.r->lat, tri->p.m->lat, tri->p.l->lat}; + gdouble lon[3] = {tri->p.r->lon, tri->p.m->lon, tri->p.l->lon}; + + if (lon[0] < -90 || lon[1] < -90 || lon[2] < -90) { + if (lon[0] > 90) lon[0] -= 360; + if (lon[1] > 90) lon[1] -= 360; + if (lon[2] > 90) lon[2] -= 360; + } + + gdouble n = tile->edge.n; + gdouble s = tile->edge.s; + gdouble e = tile->edge.e; + gdouble w = tile->edge.w; + + gdouble londist = e - w; + gdouble latdist = n - s; + + gdouble xy[3][2] = { + {(lon[0]-w)/londist, 1-(lat[0]-s)/latdist}, + {(lon[1]-w)/londist, 1-(lat[1]-s)/latdist}, + {(lon[2]-w)/londist, 1-(lat[2]-s)/latdist}, + }; + + //if ((lat[0] == 90 && (xy[0][0] < 0 || xy[0][0] > 1)) || + // (lat[1] == 90 && (xy[1][0] < 0 || xy[1][0] > 1)) || + // (lat[2] == 90 && (xy[2][0] < 0 || xy[2][0] > 1))) + // g_message("w,e=%4.f,%4.f " + // "lat,lon,x,y=" + // "%4.1f,%4.0f,%4.2f,%4.2f " + // "%4.1f,%4.0f,%4.2f,%4.2f " + // "%4.1f,%4.0f,%4.2f,%4.2f ", + // w,e, + // lat[0], lon[0], xy[0][0], xy[0][1], + // lat[1], lon[1], xy[1][0], xy[1][1], + // lat[2], lon[2], xy[2][0], xy[2][1]); + + /* Fix poles */ + if (lat[0] == 90 || lat[0] == -90) xy[0][0] = 0.5; + if (lat[1] == 90 || lat[1] == -90) xy[1][0] = 0.5; + if (lat[2] == 90 || lat[2] == -90) xy[2][0] = 0.5; + + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, *(guint*)tile->data); + glBegin(GL_TRIANGLES); + glNormal3dv(tri->p.r->norm); glTexCoord2dv(xy[0]); glVertex3dv((double*)tri->p.r); + glNormal3dv(tri->p.m->norm); glTexCoord2dv(xy[1]); glVertex3dv((double*)tri->p.m); + glNormal3dv(tri->p.l->norm); glTexCoord2dv(xy[2]); glVertex3dv((double*)tri->p.l); + glEnd(); + } + g_list_free(triangles); +} + +static void _draw_tiles(GisOpenGL *self, GisTile *tile) +{ + /* Only draw children if possible */ + gboolean has_children = TRUE; + GisTile *child; + gis_tile_foreach(tile, child) + if (!child || !child->data) + has_children = FALSE; + if (has_children) + /* Only draw children */ + gis_tile_foreach(tile, child) + _draw_tiles(self, child); + else + /* No children, draw this tile */ + _draw_tile(self, tile); +} + static void _draw_marker(GisOpenGL *self, GisMarker *marker) { GisPoint *point = gis_object_center(GIS_OBJECT(marker)); @@ -188,15 +256,12 @@ static void _draw_object(GisOpenGL *self, GisObject *object) glMatrixMode(GL_PROJECTION); glPushMatrix(); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glPushAttrib(GL_ALL_ATTRIB_BITS); - switch (object->type) { - case GIS_TYPE_MARKER: + if (GIS_IS_MARKER(object)) { _draw_marker(self, GIS_MARKER(object)); - break; - case GIS_TYPE_CALLBACK: + } else if (GIS_IS_CALLBACK(object)) { _draw_callback(self, GIS_CALLBACK(object)); - break; - default: - break; + } else if (GIS_IS_TILE(object)) { + _draw_tiles(self, GIS_TILE(object)); } glPopAttrib(); glMatrixMode(GL_PROJECTION); glPopMatrix(); @@ -206,14 +271,12 @@ static void _draw_object(GisOpenGL *self, GisObject *object) static void _load_object(GisOpenGL *self, GisObject *object) { g_debug("GisOpenGL: load_object"); - switch (object->type) { - case GIS_TYPE_MARKER: { + if (GIS_IS_MARKER(object)) { GisMarker *marker = GIS_MARKER(object); cairo_surface_t *surface = cairo_get_target(marker->cairo); gdouble width = cairo_image_surface_get_width(surface); gdouble height = cairo_image_surface_get_height(surface); - _gis_opengl_begin(self); glEnable(GL_TEXTURE_2D); glGenTextures(1, &marker->tex); glBindTexture(GL_TEXTURE_2D, marker->tex); @@ -225,26 +288,15 @@ static void _load_object(GisOpenGL *self, GisObject *object) glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); g_debug("load_texture: %d", marker->tex); - _gis_opengl_end(self); - break; - } - default: - break; } } static void _unload_object(GisOpenGL *self, GisObject *object) { g_debug("GisOpenGL: unload_object"); - switch (object->type) { - case GIS_TYPE_MARKER: { + if (GIS_IS_MARKER(object)) { GisMarker *marker = GIS_MARKER(object); - g_debug("delete_texture: %d", marker->tex); glDeleteTextures(1, &marker->tex); - break; - } - default: - break; } } @@ -263,15 +315,21 @@ struct RenderLevel { static void on_realize(GisOpenGL *self, gpointer _) { g_debug("GisOpenGL: on_realize"); + + GdkGLContext *glcontext = gtk_widget_get_gl_context(GTK_WIDGET(self)); + GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable(GTK_WIDGET(self)); + if (!gdk_gl_drawable_gl_begin(gldrawable, glcontext)) + g_assert_not_reached(); + _set_visuals(self); g_mutex_lock(self->sphere_lock); roam_sphere_update_errors(self->sphere); g_mutex_unlock(self->sphere_lock); } + static gboolean on_configure(GisOpenGL *self, GdkEventConfigure *event, gpointer _) { g_debug("GisOpenGL: on_configure"); - _gis_opengl_begin(self); double width = GTK_WIDGET(self)->allocation.width; double height = GTK_WIDGET(self)->allocation.height; @@ -289,7 +347,6 @@ static gboolean on_configure(GisOpenGL *self, GdkEventConfigure *event, gpointer g_mutex_unlock(self->sphere_lock); #endif - _gis_opengl_end(self); return FALSE; } @@ -324,7 +381,6 @@ static gboolean _draw_level(gpointer key, gpointer value, gpointer user_data) static gboolean on_expose(GisOpenGL *self, GdkEventExpose *event, gpointer _) { g_debug("GisOpenGL: on_expose - begin"); - _gis_opengl_begin(self); glClear(GL_COLOR_BUFFER_BIT); @@ -338,6 +394,7 @@ static gboolean on_expose(GisOpenGL *self, GdkEventExpose *event, gpointer _) #else g_tree_foreach(self->objects, _draw_level, self); if (self->wireframe) { + glClear(GL_DEPTH_BUFFER_BIT); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); roam_sphere_draw(self->sphere); } @@ -346,7 +403,6 @@ static gboolean on_expose(GisOpenGL *self, GdkEventExpose *event, gpointer _) GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable(GTK_WIDGET(self)); gdk_gl_drawable_swap_buffers(gldrawable); - _gis_opengl_end(self); g_debug("GisOpenGL: on_expose - end\n"); return FALSE; } @@ -386,13 +442,12 @@ static void on_view_changed(GisOpenGL *self, { g_debug("GisOpenGL: on_view_changed"); gdk_threads_enter(); - _gis_opengl_begin(self); _set_visuals(self); #ifndef ROAM_DEBUG - g_idle_add_full(G_PRIORITY_HIGH_IDLE+30, _update_errors_cb, self->sphere, NULL); + self->ue_source = g_idle_add_full(G_PRIORITY_HIGH_IDLE+30, + _update_errors_cb, self->sphere, NULL); //roam_sphere_update_errors(self->sphere); #endif - _gis_opengl_end(self); gdk_threads_leave(); } @@ -400,12 +455,10 @@ static gboolean on_idle(GisOpenGL *self) { //g_debug("GisOpenGL: on_idle"); gdk_threads_enter(); - _gis_opengl_begin(self); g_mutex_lock(self->sphere_lock); if (roam_sphere_split_merge(self->sphere)) gtk_widget_queue_draw(GTK_WIDGET(self)); g_mutex_unlock(self->sphere_lock); - _gis_opengl_end(self); gdk_threads_leave(); return TRUE; } @@ -414,11 +467,11 @@ static gboolean on_idle(GisOpenGL *self) /********************* * GisViewer methods * *********************/ -GisViewer *gis_opengl_new(GisPlugins *plugins) +GisViewer *gis_opengl_new(GisPlugins *plugins, GisPrefs *prefs) { g_debug("GisOpenGL: new"); GisViewer *self = g_object_new(GIS_TYPE_OPENGL, NULL); - self->plugins = plugins; + gis_viewer_setup(self, plugins, prefs); return self; } @@ -444,91 +497,6 @@ static void gis_opengl_project(GisViewer *_self, px, py, pz); } -static void gis_opengl_render_tile(GisViewer *_self, GisTile *tile) -{ - GisOpenGL *self = GIS_OPENGL(_self); - if (!tile || !tile->data) - return; - GList *triangles = roam_sphere_get_intersect(self->sphere, FALSE, - tile->edge.n, tile->edge.s, tile->edge.e, tile->edge.w); - if (!triangles) - g_warning("GisOpenGL: render_tiles - No triangles to draw: edges=%f,%f,%f,%f", - tile->edge.n, tile->edge.s, tile->edge.e, tile->edge.w); - //g_message("rendering %4d triangles for tile edges=%7.2f,%7.2f,%7.2f,%7.2f", - // g_list_length(triangles), tile->edge.n, tile->edge.s, tile->edge.e, tile->edge.w); - for (GList *cur = triangles; cur; cur = cur->next) { - RoamTriangle *tri = cur->data; - - gdouble lat[3] = {tri->p.r->lat, tri->p.m->lat, tri->p.l->lat}; - gdouble lon[3] = {tri->p.r->lon, tri->p.m->lon, tri->p.l->lon}; - - if (lon[0] < -90 || lon[1] < -90 || lon[2] < -90) { - if (lon[0] > 90) lon[0] -= 360; - if (lon[1] > 90) lon[1] -= 360; - if (lon[2] > 90) lon[2] -= 360; - } - - gdouble n = tile->edge.n; - gdouble s = tile->edge.s; - gdouble e = tile->edge.e; - gdouble w = tile->edge.w; - - gdouble londist = e - w; - gdouble latdist = n - s; - - gdouble xy[3][2] = { - {(lon[0]-w)/londist, 1-(lat[0]-s)/latdist}, - {(lon[1]-w)/londist, 1-(lat[1]-s)/latdist}, - {(lon[2]-w)/londist, 1-(lat[2]-s)/latdist}, - }; - - //if ((lat[0] == 90 && (xy[0][0] < 0 || xy[0][0] > 1)) || - // (lat[1] == 90 && (xy[1][0] < 0 || xy[1][0] > 1)) || - // (lat[2] == 90 && (xy[2][0] < 0 || xy[2][0] > 1))) - // g_message("w,e=%4.f,%4.f " - // "lat,lon,x,y=" - // "%4.1f,%4.0f,%4.2f,%4.2f " - // "%4.1f,%4.0f,%4.2f,%4.2f " - // "%4.1f,%4.0f,%4.2f,%4.2f ", - // w,e, - // lat[0], lon[0], xy[0][0], xy[0][1], - // lat[1], lon[1], xy[1][0], xy[1][1], - // lat[2], lon[2], xy[2][0], xy[2][1]); - - /* Fix poles */ - if (lat[0] == 90 || lat[0] == -90) xy[0][0] = 0.5; - if (lat[1] == 90 || lat[1] == -90) xy[1][0] = 0.5; - if (lat[2] == 90 || lat[2] == -90) xy[2][0] = 0.5; - - glEnable(GL_TEXTURE_2D); - glBindTexture(GL_TEXTURE_2D, *(guint*)tile->data); - glBegin(GL_TRIANGLES); - glNormal3dv(tri->p.r->norm); glTexCoord2dv(xy[0]); glVertex3dv((double*)tri->p.r); - glNormal3dv(tri->p.m->norm); glTexCoord2dv(xy[1]); glVertex3dv((double*)tri->p.m); - glNormal3dv(tri->p.l->norm); glTexCoord2dv(xy[2]); glVertex3dv((double*)tri->p.l); - glEnd(); - } - g_list_free(triangles); -} - -static void gis_opengl_render_tiles(GisViewer *_self, GisTile *tile) -{ - GisOpenGL *self = GIS_OPENGL(_self); - /* Only render children if possible */ - gboolean has_children = TRUE; - GisTile *child; - gis_tile_foreach(tile, child) - if (!child || !child->data) - has_children = FALSE; - if (has_children) - /* Only render children */ - gis_tile_foreach(tile, child) - gis_opengl_render_tiles(_self, child); - else - /* No children, render this tile */ - gis_opengl_render_tile(_self, tile); -} - static void gis_opengl_set_height_func(GisViewer *_self, GisTile *tile, RoamHeightFunc height_func, gpointer user_data, gboolean update) { @@ -576,18 +544,6 @@ static void gis_opengl_clear_height_func(GisViewer *_self) _gis_opengl_clear_height_func_rec(self->sphere->roots[i]); } -static void gis_opengl_begin(GisViewer *_self) -{ - g_assert(GIS_IS_OPENGL(_self)); - _gis_opengl_begin(GIS_OPENGL(_self)); -} - -static void gis_opengl_end(GisViewer *_self) -{ - g_assert(GIS_IS_OPENGL(_self)); - _gis_opengl_end(GIS_OPENGL(_self)); -} - static gpointer gis_opengl_add(GisViewer *_self, GisObject *object, gint key, gboolean sort) { @@ -600,29 +556,49 @@ static gpointer gis_opengl_add(GisViewer *_self, GisObject *object, g_tree_insert(self->objects, (gpointer)key, level); } GList *list = sort ? &level->sorted : &level->unsorted; - list->next = g_list_prepend(list->next, object); - return list->next; + /* Put the link in the list */ + GList *next = g_new0(GList, 1); + next->data = object; + next->prev = list; + next->next = list->next; + list->next = next; + return next; } -static void gis_opengl_remove(GisViewer *_self, gpointer _link) +static GisObject *gis_opengl_remove(GisViewer *_self, gpointer _link) { g_assert(GIS_IS_OPENGL(_self)); - GList *link = _link; GisOpenGL *self = GIS_OPENGL(_self); - _unload_object(self, link->data); - /* Just unlink and free it (blowup link to avoid warnings) */ - link = g_list_delete_link(NULL, link); + GList *link = _link; + GisObject *object = link->data; + _unload_object(self, object); + /* Just unlink and free it, link->prev is assured */ + link->prev->next = link->next; + if (link->next) + link->next->prev = link->prev; + g_free(link); + g_object_unref(object); + return object; } /**************** * GObject code * ****************/ -static int _objects_cmp(gconstpointer _a, gconstpointer _b) +static int _objects_cmp(gconstpointer _a, gconstpointer _b, gpointer _) { gint a = (int)_a, b = (int)_b; return a < b ? -1 : a > b ? 1 : 0; } +static void _objects_free(gpointer value) +{ + struct RenderLevel *level = value; + if (level->sorted.next) + g_list_free(level->sorted.next); + if (level->unsorted.next) + g_list_free(level->unsorted.next); + g_free(level); +} G_DEFINE_TYPE(GisOpenGL, gis_opengl, GIS_TYPE_VIEWER); static void gis_opengl_init(GisOpenGL *self) @@ -646,7 +622,7 @@ static void gis_opengl_init(GisOpenGL *self) GDK_KEY_PRESS_MASK); g_object_set(self, "can-focus", TRUE, NULL); - self->objects = g_tree_new(_objects_cmp); + self->objects = g_tree_new_full(_objects_cmp, NULL, NULL, _objects_free); self->sphere = roam_sphere_new(self); self->sphere_lock = g_mutex_new(); @@ -676,14 +652,18 @@ static void gis_opengl_dispose(GObject *_self) g_source_remove(self->sm_source[1]); self->sm_source[1] = 0; } - /* TODO: Cleanup/free objects tree */ + if (self->ue_source) { + g_source_remove(self->ue_source); + self->ue_source = 0; + } G_OBJECT_CLASS(gis_opengl_parent_class)->dispose(_self); } static void gis_opengl_finalize(GObject *_self) { - g_debug("GisViewer: finalize"); + g_debug("GisOpenGL: finalize"); GisOpenGL *self = GIS_OPENGL(_self); roam_sphere_free(self->sphere); + g_tree_destroy(self->objects); g_mutex_free(self->sphere_lock); G_OBJECT_CLASS(gis_opengl_parent_class)->finalize(_self); } @@ -691,6 +671,7 @@ static void gis_opengl_class_init(GisOpenGLClass *klass) { g_debug("GisOpenGL: class_init"); GObjectClass *gobject_class = G_OBJECT_CLASS(klass); + gobject_class->finalize = gis_opengl_finalize; gobject_class->dispose = gis_opengl_dispose; GisViewerClass *viewer_class = GIS_VIEWER_CLASS(klass); @@ -698,10 +679,6 @@ static void gis_opengl_class_init(GisOpenGLClass *klass) viewer_class->project = gis_opengl_project; viewer_class->clear_height_func = gis_opengl_clear_height_func; viewer_class->set_height_func = gis_opengl_set_height_func; - viewer_class->render_tile = gis_opengl_render_tile; - viewer_class->render_tiles = gis_opengl_render_tiles; - viewer_class->begin = gis_opengl_begin; - viewer_class->end = gis_opengl_end; viewer_class->add = gis_opengl_add; viewer_class->remove = gis_opengl_remove; }