]> Pileus Git - grits/blobdiff - src/gis-opengl.c
Add z-index to tiles
[grits] / src / gis-opengl.c
index ad0b5f4a2cabb56a13745356a3c2b56c648d689a..57cfb471e901894d3b510c9f0e3b99aabbf50b10 100644 (file)
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-/* Tessellation, "finding intersecting triangles" */
-/* http://research.microsoft.com/pubs/70307/tr-2006-81.pdf */
-/* http://www.opengl.org/wiki/Alpha_Blending */
+/**
+ * SECTION:gis-opengl
+ * @short_description: OpenGL based virtual globe
+ *
+ * #GisOpenGL is the core rendering engine used by libgis. Theoretically other
+ * renderers could be writte, but they have not been. GisOpenGL uses the ROAM
+ * algorithm for updating surface mesh the planet. The only thing GisOpenGL can
+ * actually render on it's own is a wireframe of a sphere.
+ *
+ * GisOpenGL relies on #GtkGlExt and requires (at least) OpenGL 2.0.
+ */
 
 #include <config.h>
 #include <math.h>
@@ -107,19 +115,19 @@ static void _set_visuals(GisOpenGL *opengl)
        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
        //glShadeModel(GL_FLAT);
 
+       g_mutex_lock(opengl->sphere_lock);
        roam_sphere_update_view(opengl->sphere);
+       g_mutex_unlock(opengl->sphere_lock);
 }
 
 
 /********************
  * Object handleing *
  ********************/
-static void _draw_tile(GisOpenGL *opengl, GisTile *tile)
+static void _draw_tile(GisOpenGL *opengl, GisTile *tile, GList *triangles)
 {
        if (!tile || !tile->data)
                return;
-       GList *triangles = roam_sphere_get_intersect(opengl->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);
@@ -170,7 +178,9 @@ static void _draw_tile(GisOpenGL *opengl, GisTile *tile)
                if (lat[2] == 90 || lat[2] == -90) xy[2][0] = 0.5;
 
                glEnable(GL_TEXTURE_2D);
+               glEnable(GL_POLYGON_OFFSET_FILL);
                glBindTexture(GL_TEXTURE_2D, *(guint*)tile->data);
+               glPolygonOffset(0, -tile->zindex);
                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);
@@ -183,18 +193,41 @@ static void _draw_tile(GisOpenGL *opengl, GisTile *tile)
 static void _draw_tiles(GisOpenGL *opengl, GisTile *tile)
 {
        /* Only draw children if possible */
-       gboolean has_children = TRUE;
+       gboolean has_children = FALSE;
        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(opengl, child);
-       else
-               /* No children, draw this tile */
-               _draw_tile(opengl, tile);
+               if (child && child->data)
+                       has_children = TRUE;
+
+       GList *triangles = NULL;
+       if (has_children) {
+               /* TODO: simplify this */
+               const gdouble rows = G_N_ELEMENTS(tile->children);
+               const gdouble cols = G_N_ELEMENTS(tile->children[0]);
+               const gdouble lat_dist = tile->edge.n - tile->edge.s;
+               const gdouble lon_dist = tile->edge.e - tile->edge.w;
+               const gdouble lat_step = lat_dist / rows;
+               const gdouble lon_step = lon_dist / cols;
+               int row, col;
+               gis_tile_foreach_index(tile, row, col) {
+                       GisTile *child = tile->children[row][col];
+                       if (child && child->data) {
+                               _draw_tiles(opengl, child);
+                       } else {
+                               const gdouble n = tile->edge.n-(lat_step*(row+0));
+                               const gdouble s = tile->edge.n-(lat_step*(row+1));
+                               const gdouble e = tile->edge.w+(lon_step*(col+1));
+                               const gdouble w = tile->edge.w+(lon_step*(col+0));
+                               GList *these = roam_sphere_get_intersect(opengl->sphere, FALSE, n, s, e, w);
+                               triangles = g_list_concat(triangles, these);
+                       }
+               }
+       } else {
+               triangles = roam_sphere_get_intersect(opengl->sphere, FALSE,
+                               tile->edge.n, tile->edge.s, tile->edge.e, tile->edge.w);
+       }
+       if (triangles)
+               _draw_tile(opengl, tile, triangles);
 }
 
 static void _draw_marker(GisOpenGL *opengl, GisMarker *marker)
@@ -204,6 +237,10 @@ static void _draw_marker(GisOpenGL *opengl, GisMarker *marker)
        gis_viewer_project(GIS_VIEWER(opengl),
                        point->lat, point->lon, point->elev,
                        &px, &py, &pz);
+
+       gint win_width  = GTK_WIDGET(opengl)->allocation.width;
+       gint win_height = GTK_WIDGET(opengl)->allocation.height;
+       py = win_height - py;
        if (pz > 1)
                return;
 
@@ -215,8 +252,7 @@ static void _draw_marker(GisOpenGL *opengl, GisMarker *marker)
 
        glMatrixMode(GL_PROJECTION); glLoadIdentity();
        glMatrixMode(GL_MODELVIEW);  glLoadIdentity();
-       glOrtho(0, GTK_WIDGET(opengl)->allocation.width,
-               0, GTK_WIDGET(opengl)->allocation.height, -1, 1);
+       glOrtho(0, win_width, win_height, 0, -1, 1);
        glTranslated(px - marker->xoff,
                     py - marker->yoff, 0);
 
@@ -225,11 +261,12 @@ static void _draw_marker(GisOpenGL *opengl, GisMarker *marker)
        glDisable(GL_DEPTH_TEST);
        glEnable(GL_TEXTURE_2D);
        glBindTexture(GL_TEXTURE_2D, marker->tex);
+       glDisable(GL_CULL_FACE);
        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);
+       glTexCoord2f(1, 0); glVertex3f(width, 0     , 0);
+       glTexCoord2f(1, 1); glVertex3f(width, height, 0);
+       glTexCoord2f(0, 1); glVertex3f(0    , height, 0);
+       glTexCoord2f(0, 0); glVertex3f(0    , 0     , 0);
        glEnd();
 }
 
@@ -243,14 +280,23 @@ static void _draw_object(GisOpenGL *opengl, GisObject *object)
        //g_debug("GisOpenGL: draw_object");
        /* Skip out of range objects */
        if (object->lod > 0) {
+               /* LOD test */
                gdouble eye[3], obj[3];
                gis_viewer_get_location(GIS_VIEWER(opengl), &eye[0], &eye[1], &eye[2]);
+               gdouble elev = 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;
+
+               /* Horizon testing */
+               gdouble c = EARTH_R+elev;
+               gdouble a = EARTH_R;
+               gdouble horizon = sqrt(c*c - a*a);
+               if (dist > horizon)
+                       return;
        }
 
        /* Draw */
@@ -262,7 +308,11 @@ static void _draw_object(GisOpenGL *opengl, GisObject *object)
        } else if (GIS_IS_CALLBACK(object)) {
                _draw_callback(opengl, GIS_CALLBACK(object));
        } else if (GIS_IS_TILE(object)) {
+               glEnable(GL_DEPTH_TEST);
+               glDepthFunc(GL_LESS);
+               g_mutex_lock(opengl->sphere_lock);
                _draw_tiles(opengl, GIS_TILE(object));
+               g_mutex_unlock(opengl->sphere_lock);
        }
        glPopAttrib();
        glMatrixMode(GL_PROJECTION); glPopMatrix();
@@ -313,21 +363,6 @@ struct RenderLevel {
        GList sorted;
 };
 
-static void on_realize(GisOpenGL *opengl, gpointer _)
-{
-       g_debug("GisOpenGL: on_realize");
-
-       GdkGLContext   *glcontext  = gtk_widget_get_gl_context(GTK_WIDGET(opengl));
-       GdkGLDrawable  *gldrawable = gtk_widget_get_gl_drawable(GTK_WIDGET(opengl));
-       if (!gdk_gl_drawable_gl_begin(gldrawable, glcontext))
-               g_assert_not_reached();
-
-       _set_visuals(opengl);
-       g_mutex_lock(opengl->sphere_lock);
-       roam_sphere_update_errors(opengl->sphere);
-       g_mutex_unlock(opengl->sphere_lock);
-}
-
 static gboolean on_configure(GisOpenGL *opengl, GdkEventConfigure *event, gpointer _)
 {
        g_debug("GisOpenGL: on_configure");
@@ -351,6 +386,19 @@ static gboolean on_configure(GisOpenGL *opengl, GdkEventConfigure *event, gpoint
        return FALSE;
 }
 
+static void on_realize(GisOpenGL *opengl, gpointer _)
+{
+       g_debug("GisOpenGL: on_realize");
+
+       GdkGLContext   *glcontext  = gtk_widget_get_gl_context(GTK_WIDGET(opengl));
+       GdkGLDrawable  *gldrawable = gtk_widget_get_gl_drawable(GTK_WIDGET(opengl));
+       if (!gdk_gl_drawable_gl_begin(gldrawable, glcontext))
+               g_assert_not_reached();
+
+       _set_visuals(opengl);
+       on_configure(opengl, NULL, NULL);
+}
+
 static gboolean _draw_level(gpointer key, gpointer value, gpointer user_data)
 {
        g_debug("GisOpenGL: _draw_level - level=%-4d", (int)key);
@@ -433,9 +481,13 @@ static gboolean on_key_press(GisOpenGL *opengl, GdkEventKey *event, gpointer _)
        return FALSE;
 }
 
-static gboolean _update_errors_cb(gpointer sphere)
+static gboolean _update_errors_cb(gpointer _opengl)
 {
-       roam_sphere_update_errors(sphere);
+       GisOpenGL *opengl = _opengl;
+       g_mutex_lock(opengl->sphere_lock);
+       roam_sphere_update_errors(opengl->sphere);
+       g_mutex_unlock(opengl->sphere_lock);
+       opengl->ue_source = 0;
        return FALSE;
 }
 static void on_view_changed(GisOpenGL *opengl,
@@ -444,8 +496,9 @@ static void on_view_changed(GisOpenGL *opengl,
        g_debug("GisOpenGL: on_view_changed");
        _set_visuals(opengl);
 #ifndef ROAM_DEBUG
-       opengl->ue_source = g_idle_add_full(G_PRIORITY_HIGH_IDLE+30,
-                       _update_errors_cb, opengl->sphere, NULL);
+       if (!opengl->ue_source)
+               opengl->ue_source = g_idle_add_full(G_PRIORITY_HIGH_IDLE+30,
+                               _update_errors_cb, opengl, NULL);
        //roam_sphere_update_errors(opengl->sphere);
 #endif
 }
@@ -466,6 +519,15 @@ static gboolean on_idle(GisOpenGL *opengl)
 /*********************
  * GisViewer methods *
  *********************/
+/**
+ * gis_opengl_new:
+ * @plugins: the plugins store to use
+ * @prefs:   the preferences object to use
+ *
+ * Create a new OpenGL renderer.
+ *
+ * Returns: the new #GisOpenGL
+ */
 GisViewer *gis_opengl_new(GisPlugins *plugins, GisPrefs *prefs)
 {
        g_debug("GisOpenGL: new");