X-Git-Url: http://pileus.org/git/?a=blobdiff_plain;f=src%2Fgis-opengl.c;h=b07820612d1d6f7b25d53b2e7ee8d7278a0f74e9;hb=01ca3d15f0981393709f25efc6b4a0e067e0c815;hp=ec703371ada11008f5d027f6020d6b7c175cff41;hpb=a15a1829ffbf0176930601ee9ac7cc0c07fa2d06;p=grits diff --git a/src/gis-opengl.c b/src/gis-opengl.c index ec70337..b078206 100644 --- a/src/gis-opengl.c +++ b/src/gis-opengl.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -29,6 +30,7 @@ #include "gis-opengl.h" #include "gis-util.h" +#include "gis-object.h" #include "roam.h" #define FOV_DIST 2000.0 @@ -57,9 +59,6 @@ static void _gis_opengl_end(GisOpenGL *self) gdk_gl_drawable_gl_end(gldrawable); } -/************* - * ROAM Code * - *************/ static void _set_visuals(GisOpenGL *self) { glMatrixMode(GL_MODELVIEW); @@ -132,9 +131,141 @@ static void _set_visuals(GisOpenGL *self) } +/******************** + * Object handleing * + ********************/ +static void _draw_marker(GisOpenGL *self, GisMarker *marker) +{ + GisPoint *point = gis_object_center(GIS_OBJECT(marker)); + gdouble px, py, pz; + gis_viewer_project(GIS_VIEWER(self), + point->lat, point->lon, point->elev, + &px, &py, &pz); + if (pz > 1) + return; + + //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); + gdouble height = cairo_image_surface_get_height(surface); + + glMatrixMode(GL_PROJECTION); glLoadIdentity(); + glMatrixMode(GL_MODELVIEW); glLoadIdentity(); + glOrtho(0, GTK_WIDGET(self)->allocation.width, + 0, GTK_WIDGET(self)->allocation.height, -1, 1); + glTranslated(px - marker->xoff, + py - marker->yoff, 0); + + glDisable(GL_LIGHTING); + glDisable(GL_COLOR_MATERIAL); + glDisable(GL_DEPTH_TEST); + glEnable(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, marker->tex); + glBegin(GL_QUADS); + glTexCoord2f(1, 1); glVertex3f(width, 0 , 0); + glTexCoord2f(1, 0); glVertex3f(width, height, 0); + glTexCoord2f(0, 0); glVertex3f(0 , height, 0); + glTexCoord2f(0, 1); glVertex3f(0 , 0 , 0); + glEnd(); +} + +static void _draw_callback(GisOpenGL *self, GisCallback *callback) +{ + callback->callback(callback, callback->user_data); +} + +static void _draw_object(GisOpenGL *self, GisObject *object) +{ + //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); + switch (object->type) { + case GIS_TYPE_MARKER: + _draw_marker(self, GIS_MARKER(object)); + break; + case GIS_TYPE_CALLBACK: + _draw_callback(self, GIS_CALLBACK(object)); + break; + default: + break; + } + 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: { + 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); + + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glTexImage2D(GL_TEXTURE_2D, 0, 4, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, + cairo_image_surface_get_data(surface)); + 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: { + GisMarker *marker = GIS_MARKER(object); + g_debug("delete_texture: %d", marker->tex); + glDeleteTextures(1, &marker->tex); + break; + } + default: + break; + } +} + + /************* * 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"); @@ -148,12 +279,13 @@ static gboolean on_configure(GisOpenGL *self, GdkEventConfigure *event, gpointer double width = GTK_WIDGET(self)->allocation.width; double height = GTK_WIDGET(self)->allocation.height; - glViewport(0, 0, width, height); + /* Setup OpenGL Window */ + glViewport(0, 0, width, height); 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 roam_sphere_update_errors(self->sphere); @@ -163,38 +295,53 @@ static gboolean on_configure(GisOpenGL *self, GdkEventConfigure *event, gpointer 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); + 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); - -#ifndef ROAM_DEBUG - gis_plugins_foreach(GIS_VIEWER(self)->plugins, G_CALLBACK(on_expose_plugin), self); + glClear(GL_COLOR_BUFFER_BIT); - 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)); @@ -227,8 +374,7 @@ static gboolean on_key_press(GisOpenGL *self, GdkEventKey *event, gpointer _) #else gdk_threads_enter(); #endif - - return TRUE; + return FALSE; } static gboolean _update_errors_cb(gpointer sphere) @@ -302,7 +448,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", @@ -371,15 +517,18 @@ 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, + 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); @@ -418,9 +567,42 @@ static void gis_opengl_end(GisViewer *_self) _gis_opengl_end(GIS_OPENGL(_self)); } +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); + 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, 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); +} + /**************** * 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) { @@ -443,6 +625,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->sphere = roam_sphere_new(self); #ifndef ROAM_DEBUG @@ -450,14 +633,14 @@ static void gis_opengl_init(GisOpenGL *self) self->sm_source[1] = g_timeout_add_full(G_PRIORITY_HIGH_IDLE+10, 500, (GSourceFunc)on_idle, self, NULL); #endif - 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); + 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); - g_signal_connect(self, "key-press-event", G_CALLBACK(on_key_press), NULL); + g_signal_connect(self, "key-press-event", G_CALLBACK(on_key_press), NULL); - g_signal_connect(self, "location-changed", G_CALLBACK(on_view_changed), NULL); - g_signal_connect(self, "rotation-changed", G_CALLBACK(on_view_changed), NULL); + g_signal_connect(self, "location-changed", G_CALLBACK(on_view_changed), NULL); + g_signal_connect(self, "rotation-changed", G_CALLBACK(on_view_changed), NULL); } static void gis_opengl_dispose(GObject *_self) { @@ -475,8 +658,15 @@ static void gis_opengl_dispose(GObject *_self) 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); + G_OBJECT_CLASS(gis_opengl_parent_class)->finalize(_self); +} static void gis_opengl_class_init(GisOpenGLClass *klass) { g_debug("GisOpenGL: class_init"); @@ -492,4 +682,6 @@ static void gis_opengl_class_init(GisOpenGLClass *klass) 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; }