X-Git-Url: http://pileus.org/git/?p=grits;a=blobdiff_plain;f=src%2Fgis-opengl.c;h=9501781f586336dea6faa1e262edef008a5eb6ad;hp=08babe50b8617e77365cf45df64e91d5a42160b5;hb=5e97f4c0721a5481afbb2d005538e896ac8baf5a;hpb=bd716715f1d13a8df514fcfa53fd82aebdfda770
diff --git a/src/gis-opengl.c b/src/gis-opengl.c
index 08babe5..9501781 100644
--- a/src/gis-opengl.c
+++ b/src/gis-opengl.c
@@ -15,9 +15,17 @@
* along with this program. If not, see .
*/
-/* 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
#include
@@ -36,11 +44,12 @@
#include "objects/gis-marker.h"
#include "objects/gis-callback.h"
-#define FOV_DIST 2000.0
-#define MPPX(dist) (4*dist/FOV_DIST)
-
// #define ROAM_DEBUG
+/* Tessellation, "finding intersecting triangles" */
+/* http://research.microsoft.com/pubs/70307/tr-2006-81.pdf */
+/* http://www.opengl.org/wiki/Alpha_Blending */
+
/***********
* Helpers *
***********/
@@ -106,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);
@@ -169,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);
@@ -182,27 +193,54 @@ 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)
{
- GisPoint *point = gis_object_center(GIS_OBJECT(marker));
+ GisPoint *point = gis_object_center(marker);
gdouble px, py, pz;
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;
@@ -214,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);
@@ -224,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();
}
@@ -242,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 */
@@ -261,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();
@@ -312,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");
@@ -350,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);
@@ -432,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,
@@ -443,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
}
@@ -465,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");
@@ -548,6 +611,7 @@ static gpointer gis_opengl_add(GisViewer *_opengl, GisObject *object,
g_assert(GIS_IS_OPENGL(_opengl));
GisOpenGL *opengl = GIS_OPENGL(_opengl);
_load_object(opengl, object);
+ g_mutex_lock(opengl->objects_lock);
struct RenderLevel *level = g_tree_lookup(opengl->objects, (gpointer)key);
if (!level) {
level = g_new0(struct RenderLevel, 1);
@@ -560,6 +624,7 @@ static gpointer gis_opengl_add(GisViewer *_opengl, GisObject *object,
next->prev = list;
next->next = list->next;
list->next = next;
+ g_mutex_unlock(opengl->objects_lock);
return next;
}
@@ -570,12 +635,14 @@ static GisObject *gis_opengl_remove(GisViewer *_opengl, gpointer _link)
GList *link = _link;
GisObject *object = link->data;
_unload_object(opengl, object);
+ g_mutex_lock(opengl->objects_lock);
/* 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);
+ g_mutex_unlock(opengl->objects_lock);
return object;
}
@@ -614,6 +681,7 @@ static void gis_opengl_init(GisOpenGL *opengl)
g_object_unref(glconfig);
opengl->objects = g_tree_new_full(_objects_cmp, NULL, NULL, _objects_free);
+ opengl->objects_lock = g_mutex_new();
opengl->sphere = roam_sphere_new(opengl);
opengl->sphere_lock = g_mutex_new();
@@ -656,6 +724,7 @@ static void gis_opengl_finalize(GObject *_opengl)
GisOpenGL *opengl = GIS_OPENGL(_opengl);
roam_sphere_free(opengl->sphere);
g_tree_destroy(opengl->objects);
+ g_mutex_free(opengl->objects_lock);
g_mutex_free(opengl->sphere_lock);
G_OBJECT_CLASS(gis_opengl_parent_class)->finalize(_opengl);
}