X-Git-Url: http://pileus.org/git/?p=grits;a=blobdiff_plain;f=src%2Fgis-opengl.c;h=7263ef4e7ad646aa2c109d10969a8f9beefdc00e;hp=b1886c2b2ce0c36b321f348ba1de50835a0a3013;hb=72ae2b9bd30e518fa1539424ad0d994aa8d8a6de;hpb=a0a503f0fd257c18eaff3a5a1ae0a037f351f187 diff --git a/src/gis-opengl.c b/src/gis-opengl.c index b1886c2..7263ef4 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 @@ -59,6 +59,71 @@ static void _gis_opengl_end(GisOpenGL *self) gdk_gl_drawable_gl_end(gldrawable); } +static void _set_visuals(GisOpenGL *self) +{ + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + /* Camera 1 */ + double lat, lon, elev, rx, ry, rz; + gis_viewer_get_location(GIS_VIEWER(self), &lat, &lon, &elev); + gis_viewer_get_rotation(GIS_VIEWER(self), &rx, &ry, &rz); + glRotatef(rx, 1, 0, 0); + glRotatef(rz, 0, 0, 1); + + /* Lighting */ +#ifdef ROAM_DEBUG + float light_ambient[] = {0.7f, 0.7f, 0.7f, 1.0f}; + float light_diffuse[] = {2.0f, 2.0f, 2.0f, 1.0f}; +#else + float light_ambient[] = {0.2f, 0.2f, 0.2f, 1.0f}; + float light_diffuse[] = {5.0f, 5.0f, 5.0f, 1.0f}; +#endif + float light_position[] = {-13*EARTH_R, 1*EARTH_R, 3*EARTH_R, 1.0f}; + glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient); + glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse); + glLightfv(GL_LIGHT0, GL_POSITION, light_position); + glEnable(GL_LIGHT0); + glEnable(GL_LIGHTING); + + 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.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); + glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, material_specular); + glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, material_emission); + glDisable(GL_TEXTURE_2D); + glDisable(GL_COLOR_MATERIAL); + + /* Camera 2 */ + glTranslatef(0, 0, -elev2rad(elev)); + glRotatef(lat, 1, 0, 0); + glRotatef(-lon, 0, 1, 0); + + glDisable(GL_ALPHA_TEST); + + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + +#ifndef ROAM_DEBUG + glCullFace(GL_BACK); + glEnable(GL_CULL_FACE); +#endif + + glClearDepth(1.0); + glDepthFunc(GL_LEQUAL); + glEnable(GL_DEPTH_TEST); + + glEnable(GL_LINE_SMOOTH); + + glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + //glShadeModel(GL_FLAT); + + roam_sphere_update_view(self->sphere); +} + /******************** * Object handleing * @@ -70,8 +135,10 @@ static void _draw_marker(GisOpenGL *self, GisMarker *marker) gis_viewer_project(GIS_VIEWER(self), point->lat, point->lon, point->elev, &px, &py, &pz); + if (pz > 1) + return; - g_debug("GisOpenGL: draw_marker - texture=%d", marker->tex); + //g_debug("GisOpenGL: draw_marker - %s pz=%f ", marker->label, pz); cairo_surface_t *surface = cairo_get_target(marker->cairo); gdouble width = cairo_image_surface_get_width(surface); @@ -89,7 +156,6 @@ static void _draw_marker(GisOpenGL *self, GisMarker *marker) glDisable(GL_DEPTH_TEST); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, marker->tex); - g_debug("bind_texture: %d", marker->tex); glBegin(GL_QUADS); glTexCoord2f(1, 1); glVertex3f(width, 0 , 0); glTexCoord2f(1, 0); glVertex3f(width, height, 0); @@ -98,32 +164,44 @@ static void _draw_marker(GisOpenGL *self, GisMarker *marker) glEnd(); } +static void _draw_callback(GisOpenGL *self, GisCallback *callback) +{ + callback->callback(callback, callback->user_data); +} -static void _draw_objects(GisOpenGL *self) +static void _draw_object(GisOpenGL *self, GisObject *object) { - g_debug("GisOpenGL: draw_objects"); - /* Draw objects */ - for (GList *cur = self->objects; cur; cur = cur->next) { - glMatrixMode(GL_PROJECTION); glPushMatrix(); - glMatrixMode(GL_MODELVIEW); glPushMatrix(); - GisObject *object = cur->data; - switch (object->type) { - case GIS_TYPE_MARKER: - _draw_marker(self, GIS_MARKER(object)); - break; - default: - break; - } - glMatrixMode(GL_PROJECTION); glPopMatrix(); - glMatrixMode(GL_MODELVIEW); glPopMatrix(); + //g_debug("GisOpenGL: draw_object"); + /* Skip out of range objects */ + if (object->lod > 0) { + gdouble eye[3], obj[3]; + gis_viewer_get_location(GIS_VIEWER(self), &eye[0], &eye[1], &eye[2]); + lle2xyz(eye[0], eye[1], eye[2], &eye[0], &eye[1], &eye[2]); + lle2xyz(object->center.lat, object->center.lon, object->center.elev, + &obj[0], &obj[1], &obj[2]); + gdouble dist = distd(obj, eye); + if (object->lod < dist) + return; } + + /* Draw */ + glMatrixMode(GL_PROJECTION); glPushMatrix(); + glMatrixMode(GL_MODELVIEW); glPushMatrix(); + glPushAttrib(GL_ALL_ATTRIB_BITS); + if (GIS_IS_MARKER(object)) { + _draw_marker(self, GIS_MARKER(object)); + } else if (GIS_IS_CALLBACK(object)) { + _draw_callback(self, GIS_CALLBACK(object)); + } + glPopAttrib(); + glMatrixMode(GL_PROJECTION); glPopMatrix(); + glMatrixMode(GL_MODELVIEW); glPopMatrix(); } 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); @@ -142,111 +220,38 @@ static void _load_object(GisOpenGL *self, GisObject *object) 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 _free_object(GisOpenGL *self, GisObject *object) +static void _unload_object(GisOpenGL *self, GisObject *object) { - g_debug("GisOpenGL: free_object"); - switch (object->type) { - case GIS_TYPE_MARKER: { + g_debug("GisOpenGL: unload_object"); + if (GIS_IS_MARKER(object)) { GisMarker *marker = GIS_MARKER(object); g_debug("delete_texture: %d", marker->tex); glDeleteTextures(1, &marker->tex); - break; - } - default: - break; } } -/************* - * ROAM Code * - *************/ -static void _set_visuals(GisOpenGL *self) -{ - glMatrixMode(GL_MODELVIEW); - glLoadIdentity(); - - /* Camera 1 */ - double lat, lon, elev, rx, ry, rz; - gis_viewer_get_location(GIS_VIEWER(self), &lat, &lon, &elev); - gis_viewer_get_rotation(GIS_VIEWER(self), &rx, &ry, &rz); - glRotatef(rx, 1, 0, 0); - glRotatef(rz, 0, 0, 1); - - /* Lighting */ -#ifdef ROAM_DEBUG - float light_ambient[] = {0.7f, 0.7f, 0.7f, 1.0f}; - float light_diffuse[] = {2.0f, 2.0f, 2.0f, 1.0f}; -#else - float light_ambient[] = {0.2f, 0.2f, 0.2f, 1.0f}; - float light_diffuse[] = {5.0f, 5.0f, 5.0f, 1.0f}; -#endif - float light_position[] = {-13*EARTH_R, 1*EARTH_R, 3*EARTH_R, 1.0f}; - glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient); - glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse); - glLightfv(GL_LIGHT0, GL_POSITION, light_position); - glEnable(GL_LIGHT0); - glEnable(GL_LIGHTING); - - 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_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); - glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, material_specular); - glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, material_emission); - glDisable(GL_TEXTURE_2D); - glDisable(GL_COLOR_MATERIAL); - - /* Camera 2 */ - glTranslatef(0, 0, -elev2rad(elev)); - 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); - glEnable(GL_BLEND); - -#ifndef ROAM_DEBUG - glCullFace(GL_BACK); - glEnable(GL_CULL_FACE); -#endif - - glClearDepth(1.0); - glDepthFunc(GL_LEQUAL); - glEnable(GL_DEPTH_TEST); - - glEnable(GL_LINE_SMOOTH); - - glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); - //glShadeModel(GL_FLAT); - - roam_sphere_update_view(self->sphere); -} - /************* * Callbacks * *************/ +/* The unsorted/sroted GLists are blank head nodes, + * This way us we can remove objects from the level just by fixing up links + * I.e. we don't need to do a lookup to remove an object if we have its GList */ +struct RenderLevel { + GList unsorted; + GList sorted; +}; + static void on_realize(GisOpenGL *self, gpointer _) { g_debug("GisOpenGL: on_realize"); _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 _) { @@ -261,48 +266,66 @@ static gboolean on_configure(GisOpenGL *self, GdkEventConfigure *event, gpointer glMatrixMode(GL_PROJECTION); glLoadIdentity(); double ang = atan(height/FOV_DIST); - gluPerspective(rad2deg(ang)*2, width/height, 1, 20*EARTH_R); + 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; } -static void _on_expose_plugin(GisPlugin *plugin, gchar *name, GisOpenGL *self) +static gboolean _draw_level(gpointer key, gpointer value, gpointer user_data) { - _set_visuals(self); - glMatrixMode(GL_PROJECTION); glPushMatrix(); - glMatrixMode(GL_MODELVIEW); glPushMatrix(); - gis_plugin_expose(plugin); - glMatrixMode(GL_PROJECTION); glPopMatrix(); - glMatrixMode(GL_MODELVIEW); glPopMatrix(); + g_debug("GisOpenGL: _draw_level - level=%-4d", (int)key); + GisOpenGL *self = user_data; + struct RenderLevel *level = value; + int nsorted = 0, nunsorted = 0; + GList *cur = NULL; + + /* Draw opaque objects without sorting */ + glDepthMask(TRUE); + glClear(GL_DEPTH_BUFFER_BIT); + for (cur = level->unsorted.next; cur; cur = cur->next, nunsorted++) + _draw_object(self, GIS_OBJECT(cur->data)); + + /* Freeze depth buffer and draw transparent objects sorted */ + /* TODO: sorting */ + //glDepthMask(FALSE); + glAlphaFunc(GL_GREATER, 0.1); + for (cur = level->sorted.next; cur; cur = cur->next, nsorted++) + _draw_object(self, GIS_OBJECT(cur->data)); + + /* TODO: Prune empty levels */ + + g_debug("GisOpenGL: _draw_level - drew %d,%d objects", + nunsorted, nsorted); + return FALSE; } + static gboolean on_expose(GisOpenGL *self, GdkEventExpose *event, gpointer _) { g_debug("GisOpenGL: on_expose - begin"); _gis_opengl_begin(self); - glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glClear(GL_COLOR_BUFFER_BIT); -#ifndef ROAM_DEBUG - gis_plugins_foreach(GIS_VIEWER(self)->plugins, - G_CALLBACK(_on_expose_plugin), self); - _draw_objects(self); - if (self->wireframe) { - _set_visuals(self); - glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); - roam_sphere_draw(self->sphere); - } -#else _set_visuals(self); +#ifdef ROAM_DEBUG glColor4f(0.0, 0.0, 9.0, 0.6); glDisable(GL_TEXTURE_2D); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); roam_sphere_draw(self->sphere); //roam_sphere_draw_normals(self->sphere); +#else + g_tree_foreach(self->objects, _draw_level, self); + if (self->wireframe) { + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); + roam_sphere_draw(self->sphere); + } #endif GdkGLDrawable *gldrawable = gtk_widget_get_gl_drawable(GTK_WIDGET(self)); @@ -363,8 +386,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; @@ -374,11 +399,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; } @@ -409,7 +434,7 @@ 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, + 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", @@ -436,12 +461,30 @@ static void gis_opengl_render_tile(GisViewer *_self, GisTile *tile) gdouble londist = e - w; gdouble latdist = n - s; - gdouble xy[][3] = { + 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); @@ -478,18 +521,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) @@ -525,25 +573,42 @@ static void gis_opengl_end(GisViewer *_self) _gis_opengl_end(GIS_OPENGL(_self)); } -static void gis_opengl_add(GisViewer *_self, GisObject *object) +static gpointer gis_opengl_add(GisViewer *_self, GisObject *object, + gint key, gboolean sort) { g_assert(GIS_IS_OPENGL(_self)); GisOpenGL *self = GIS_OPENGL(_self); _load_object(self, object); - self->objects = g_list_prepend(self->objects, object); + struct RenderLevel *level = g_tree_lookup(self->objects, (gpointer)key); + if (!level) { + level = g_new0(struct RenderLevel, 1); + 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; } -static void gis_opengl_remove(GisViewer *_self, GisObject *object) +static void gis_opengl_remove(GisViewer *_self, gpointer _link) { g_assert(GIS_IS_OPENGL(_self)); + GList *link = _link; GisOpenGL *self = GIS_OPENGL(_self); - _free_object(self, object); - self->objects = g_list_remove(self->objects, object); + _unload_object(self, link->data); + /* Just unlink and free it (blowup link to avoid warnings) */ + link = g_list_delete_link(NULL, link); } /**************** * GObject code * ****************/ +static int _objects_cmp(gconstpointer _a, gconstpointer _b) +{ + gint a = (int)_a, b = (int)_b; + return a < b ? -1 : + a > b ? 1 : 0; +} + G_DEFINE_TYPE(GisOpenGL, gis_opengl, GIS_TYPE_VIEWER); static void gis_opengl_init(GisOpenGL *self) { @@ -566,7 +631,9 @@ 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->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); @@ -594,23 +661,22 @@ 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; - } + /* 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"); GisOpenGL *self = GIS_OPENGL(_self); + roam_sphere_free(self->sphere); + 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->dispose = gis_opengl_dispose; GisViewerClass *viewer_class = GIS_VIEWER_CLASS(klass); viewer_class->center_position = gis_opengl_center_position;