X-Git-Url: http://pileus.org/git/?p=grits;a=blobdiff_plain;f=src%2Fgis-opengl.c;h=c1d6fd5e03729a8b7211645f8ef7de5d89485c99;hp=dc26348c3dccce5cd8bb9eb02ee2040dff1345c6;hb=64b6476b923d6f779bfad1d9a608f925943b39b9;hpb=72daafe41e061d981a69bc26c656f3e7da1ededa diff --git a/src/gis-opengl.c b/src/gis-opengl.c index dc26348..c1d6fd5 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); @@ -88,7 +73,7 @@ static void _set_visuals(GisOpenGL *self) float material_ambient[] = {0.2, 0.2, 0.2, 1.0}; float material_diffuse[] = {0.8, 0.8, 0.8, 1.0}; - float material_specular[] = {0.0, 0.0, 0.0, 1.0}; + float material_specular[] = {0.1, 0.1, 0.1, 1.0}; float material_emission[] = {0.0, 0.0, 0.0, 1.0}; glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, material_ambient); glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, material_diffuse); @@ -102,12 +87,6 @@ static void _set_visuals(GisOpenGL *self) glRotatef(lat, 1, 0, 0); glRotatef(-lon, 0, 1, 0); - /* Misc */ - gdouble rg = MAX(0, 1-(elev/20000)); - gdouble blue = MAX(0, 1-(elev/50000)); - glClearColor(MIN(0.65,rg), MIN(0.65,rg), MIN(1,blue), 1.0f); - glColor4f(1, 1, 1, 1); - glDisable(GL_ALPHA_TEST); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); @@ -134,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)); @@ -194,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(); @@ -212,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); @@ -231,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; } } @@ -269,13 +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; @@ -288,10 +342,11 @@ static gboolean on_configure(GisOpenGL *self, GdkEventConfigure *event, gpointer gluPerspective(rad2deg(ang)*2, width/height, 1, 10*EARTH_R); #ifndef ROAM_DEBUG + g_mutex_lock(self->sphere_lock); roam_sphere_update_errors(self->sphere); + g_mutex_unlock(self->sphere_lock); #endif - _gis_opengl_end(self); return FALSE; } @@ -311,7 +366,8 @@ static gboolean _draw_level(gpointer key, gpointer value, gpointer user_data) /* Freeze depth buffer and draw transparent objects sorted */ /* TODO: sorting */ - glDepthMask(FALSE); + //glDepthMask(FALSE); + glAlphaFunc(GL_GREATER, 0.1); for (cur = level->sorted.next; cur; cur = cur->next, nsorted++) _draw_object(self, GIS_OBJECT(cur->data)); @@ -325,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); @@ -339,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); } @@ -347,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,25 +441,22 @@ static void on_view_changed(GisOpenGL *self, gdouble _1, gdouble _2, gdouble _3) { 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(); } 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)); - _gis_opengl_end(self); + g_mutex_unlock(self->sphere_lock); gdk_threads_leave(); return TRUE; } @@ -413,11 +465,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; } @@ -443,73 +495,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, - 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] = { - {(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}, - }; - - 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) { @@ -517,18 +502,23 @@ static void gis_opengl_set_height_func(GisViewer *_self, GisTile *tile, if (!tile) return; /* TODO: get points? */ - GList *triangles = roam_sphere_get_intersect(self->sphere, + g_mutex_lock(self->sphere_lock); + GList *triangles = roam_sphere_get_intersect(self->sphere, TRUE, tile->edge.n, tile->edge.s, tile->edge.e, tile->edge.w); for (GList *cur = triangles; cur; cur = cur->next) { RoamTriangle *tri = cur->data; RoamPoint *points[] = {tri->p.l, tri->p.m, tri->p.r, tri->split}; for (int i = 0; i < G_N_ELEMENTS(points); i++) { - points[i]->height_func = height_func; - points[i]->height_data = user_data; - roam_point_update_height(points[i]); + if (tile->edge.n >= points[i]->lat && points[i]->lat >= tile->edge.s && + tile->edge.e >= points[i]->lon && points[i]->lon >= tile->edge.w) { + points[i]->height_func = height_func; + points[i]->height_data = user_data; + roam_point_update_height(points[i]); + } } } g_list_free(triangles); + g_mutex_unlock(self->sphere_lock); } static void _gis_opengl_clear_height_func_rec(RoamTriangle *root) @@ -552,18 +542,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) { @@ -576,29 +554,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) @@ -615,21 +613,16 @@ static void gis_opengl_init(GisOpenGL *self) g_error("GL lacks required capabilities"); g_object_unref(glconfig); - gtk_widget_set_size_request(GTK_WIDGET(self), 600, 550); - gtk_widget_set_events(GTK_WIDGET(self), - GDK_BUTTON_PRESS_MASK | - GDK_ENTER_NOTIFY_MASK | - 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(); #ifndef ROAM_DEBUG self->sm_source[0] = g_timeout_add_full(G_PRIORITY_HIGH_IDLE+30, 33, (GSourceFunc)on_idle, self, NULL); self->sm_source[1] = g_timeout_add_full(G_PRIORITY_HIGH_IDLE+10, 500, (GSourceFunc)on_idle, self, NULL); #endif + gtk_widget_add_events(GTK_WIDGET(self), GDK_KEY_PRESS_MASK); g_signal_connect(self, "realize", G_CALLBACK(on_realize), NULL); g_signal_connect(self, "configure-event", G_CALLBACK(on_configure), NULL); g_signal_connect(self, "expose-event", G_CALLBACK(on_expose), NULL); @@ -651,34 +644,33 @@ static void gis_opengl_dispose(GObject *_self) g_source_remove(self->sm_source[1]); self->sm_source[1] = 0; } - if (self->sphere) { - roam_sphere_free(self->sphere); - self->sphere = NULL; + if (self->ue_source) { + g_source_remove(self->ue_source); + self->ue_source = 0; } - /* TODO: Cleanup/free objects tree */ 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); } static void gis_opengl_class_init(GisOpenGLClass *klass) { g_debug("GisOpenGL: class_init"); GObjectClass *gobject_class = G_OBJECT_CLASS(klass); - gobject_class->dispose = gis_opengl_dispose; + gobject_class->finalize = gis_opengl_finalize; + gobject_class->dispose = gis_opengl_dispose; GisViewerClass *viewer_class = GIS_VIEWER_CLASS(klass); viewer_class->center_position = gis_opengl_center_position; 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; }