[libgis] Move OpenGL code from GisOpenGL to objects



commit 72643f9bd6c87e08670eb1a0aad2da5573b99774
Author: Andy Spencer <andy753421 gmail com>
Date:   Sun Oct 24 11:57:09 2010 +0000

    Move OpenGL code from GisOpenGL to objects
    
    Add draw functions to GisObjectClass which should be set by subclasses.
    This allows outside object types to be written in the normal way
    (instead of always using GisCallback).
    
    This also moves a lot of drawing code out of GisOpenGL which should
    prevent it from becoming too big.
    
    Unfortunately, there are some pesky dependency problems between
    GisObject/GisViewer/GisOpenGL/GTK now. For example, Wms pulls in
    GisTile, which pulls in GisViewer/GisViewer, which pulls in all of GTK.

 examples/plugin/teapot.c   |    2 +-
 src/data/Makefile.am       |    2 +-
 src/gis-opengl.c           |  256 +-------------------------------------------
 src/gis-viewer.h           |   18 +++
 src/objects/Makefile.am    |    2 +-
 src/objects/gis-callback.c |   37 +++++--
 src/objects/gis-callback.h |    6 +-
 src/objects/gis-marker.c   |   71 +++++++++++-
 src/objects/gis-object.c   |   60 ++++++++++
 src/objects/gis-object.h   |    7 ++
 src/objects/gis-tile.c     |  130 ++++++++++++++++++++++
 src/plugins/env.c          |    2 +-
 12 files changed, 314 insertions(+), 279 deletions(-)
---
diff --git a/examples/plugin/teapot.c b/examples/plugin/teapot.c
index 7916ede..bafcae3 100644
--- a/examples/plugin/teapot.c
+++ b/examples/plugin/teapot.c
@@ -35,7 +35,7 @@ static gboolean rotate(gpointer _teapot)
 	return TRUE;
 }
 
-static void expose(GisCallback *callback, gpointer _teapot)
+static void expose(GisCallback *callback, GisOpenGL *opengl, gpointer _teapot)
 {
 	GisPluginTeapot *teapot = GIS_PLUGIN_TEAPOT(_teapot);
 	g_debug("GisPluginTeapot: expose");
diff --git a/src/data/Makefile.am b/src/data/Makefile.am
index e6109ee..cee1794 100644
--- a/src/data/Makefile.am
+++ b/src/data/Makefile.am
@@ -1,5 +1,5 @@
 AM_CFLAGS  = -Wall --std=gnu99 -I$(top_srcdir)/src
-AM_CFLAGS += $(GLIB_CFLAGS) $(SOUP_CFLAGS)
+AM_CFLAGS += $(GLIB_CFLAGS) $(GTK_CFLAGS) $(SOUP_CFLAGS)
 if NOTWIN32
 AM_CFLAGS += -fPIC
 endif
diff --git a/src/gis-opengl.c b/src/gis-opengl.c
index 8b71142..9709842 100644
--- a/src/gis-opengl.c
+++ b/src/gis-opengl.c
@@ -40,11 +40,6 @@
 #include "gis-util.h"
 #include "roam.h"
 
-#include "objects/gis-object.h"
-#include "objects/gis-tile.h"
-#include "objects/gis-marker.h"
-#include "objects/gis-callback.h"
-
 // #define ROAM_DEBUG
 
 /* Tessellation, "finding intersecting triangles" */
@@ -122,251 +117,6 @@ static void _set_visuals(GisOpenGL *opengl)
 }
 
 
-/********************
- * Object handleing *
- ********************/
-static void _draw_tile(GisOpenGL *opengl, GisTile *tile, GList *triangles)
-{
-	if (!tile || !tile->data)
-		return;
-	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);
-	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 xscale = tile->coords.e - tile->coords.w;
-	gdouble yscale = tile->coords.s - tile->coords.n;
-
-	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 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;
-
-		/* Scale to tile coords */
-		for (int i = 0; i < 3; i++) {
-			xy[i][0] = tile->coords.w + xy[i][0]*xscale;
-			xy[i][1] = tile->coords.n + xy[i][1]*yscale;
-		}
-
-		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);
-		glNormal3dv(tri->p.l->norm); glTexCoord2dv(xy[2]); glVertex3dv((double*)tri->p.l);
-		glEnd();
-	}
-}
-
-static void _draw_tiles(GisOpenGL *opengl, GisTile *tile)
-{
-	/* Only draw children if possible */
-	gboolean has_children = FALSE;
-	GisTile *child;
-	gis_tile_foreach(tile, child)
-		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);
-	g_list_free(triangles);
-}
-
-static void _draw_marker(GisOpenGL *opengl, GisMarker *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;
-
-	//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, win_width, win_height, 0, -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);
-	glDisable(GL_CULL_FACE);
-	glBegin(GL_QUADS);
-	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();
-}
-
-static void _draw_callback(GisOpenGL *opengl, GisCallback *callback)
-{
-	callback->callback(callback, callback->user_data);
-}
-
-static void _draw_object(GisOpenGL *opengl, GisObject *object)
-{
-	//g_debug("GisOpenGL: draw_object");
-	/* Skip hidden objects */
-	if (object->hidden)
-		return;
-
-	/* 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 */
-	glMatrixMode(GL_PROJECTION); glPushMatrix();
-	glMatrixMode(GL_MODELVIEW);  glPushMatrix();
-	glPushAttrib(GL_ALL_ATTRIB_BITS);
-	if (GIS_IS_MARKER(object)) {
-		_draw_marker(opengl, GIS_MARKER(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();
-	glMatrixMode(GL_MODELVIEW);  glPopMatrix();
-}
-
-static void _load_object(GisOpenGL *opengl, GisObject *object)
-{
-	//g_debug("GisOpenGL: load_object");
-	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);
-
-		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);
-	}
-}
-
-static void _unload_object(GisOpenGL *opengl, GisObject *object)
-{
-	//g_debug("GisOpenGL: unload_object");
-	if (GIS_IS_MARKER(object)) {
-		GisMarker *marker = GIS_MARKER(object);
-		glDeleteTextures(1, &marker->tex);
-	}
-}
-
-
 /*************
  * Callbacks *
  *************/
@@ -426,14 +176,14 @@ static gboolean _draw_level(gpointer key, gpointer value, gpointer user_data)
 	glDepthMask(TRUE);
 	glClear(GL_DEPTH_BUFFER_BIT);
 	for (cur = level->unsorted.next; cur; cur = cur->next, nunsorted++)
-		_draw_object(opengl, GIS_OBJECT(cur->data));
+		gis_object_draw( GIS_OBJECT(cur->data), opengl);
 
 	/* 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(opengl, GIS_OBJECT(cur->data));
+		gis_object_draw(GIS_OBJECT(cur->data), opengl);
 
 	/* TODO: Prune empty levels */
 
@@ -619,7 +369,6 @@ static gpointer gis_opengl_add(GisViewer *_opengl, GisObject *object,
 	g_assert(GIS_IS_OPENGL(_opengl));
 	GisOpenGL *opengl = GIS_OPENGL(_opengl);
 	g_mutex_lock(opengl->objects_lock);
-	_load_object(opengl, object);
 	struct RenderLevel *level = g_tree_lookup(opengl->objects, (gpointer)key);
 	if (!level) {
 		level = g_new0(struct RenderLevel, 1);
@@ -645,7 +394,6 @@ static GisObject *gis_opengl_remove(GisViewer *_opengl, gpointer _link)
 	g_mutex_lock(opengl->objects_lock);
 	GList *link = _link;
 	GisObject *object = link->data;
-	_unload_object(opengl, object);
 	/* Just unlink and free it, link->prev is assured */
 	link->prev->next = link->next;
 	if (link->next)
diff --git a/src/gis-viewer.h b/src/gis-viewer.h
index 8482068..a4da4fc 100644
--- a/src/gis-viewer.h
+++ b/src/gis-viewer.h
@@ -15,6 +15,24 @@
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
+/**
+ * Hack alert: gis-opengl.h needs to be included before gis-viewer
+ *   - GisViewer depends on GisObject for add/remove functions
+ *   - GisObject depends on GisOpenGL for load/unload functions
+ *   - GisOpenGL depends on GisViewer for inheritance
+ *
+ * The problem here is that GisOpenGL needs the GisViewer definition
+ * but GisViewer only needs the typedefs (through GisObject),
+ * so GisViewer needs to be included after the GisOpenGL typedefs but
+ * before the GisOpenGL definition. This is handled internally by
+ * gis-opengl.h
+ *
+ * This should probably be fixed, but making a GisGLObject interface
+ * seems like too much work. Merging GisViewer and GisOpenGL would also work,
+ * but I like the separate that that's provided by having two.
+ */
+#include "gis-opengl.h"
+
 #ifndef __GIS_VIEWER_H__
 #define __GIS_VIEWER_H__
 
diff --git a/src/objects/Makefile.am b/src/objects/Makefile.am
index 5f7c256..844352f 100644
--- a/src/objects/Makefile.am
+++ b/src/objects/Makefile.am
@@ -1,5 +1,5 @@
 AM_CFLAGS  = -Wall --std=gnu99 -I$(top_srcdir)/src
-AM_CFLAGS += $(GLIB_CFLAGS) $(CAIRO_CFLAGS)
+AM_CFLAGS += $(GLIB_CFLAGS) $(GTK_CFLAGS) $(CAIRO_CFLAGS)
 if NOTWIN32
 AM_CFLAGS += -fPIC
 endif
diff --git a/src/objects/gis-callback.c b/src/objects/gis-callback.c
index 9e63737..89dada4 100644
--- a/src/objects/gis-callback.c
+++ b/src/objects/gis-callback.c
@@ -22,21 +22,14 @@
  * #GisCallback objects are used for custom drawing functions. A common example
  * of this would be to render something which does not easily fit into a normal
  * object. For instance, a Heads-Up-Display overlay.
+ *
+ * Callbacks are an alternate to extending GisObject with a new class and
+ * should be used when only once instance of the object will be needed.
  */
 
 #include <config.h>
 #include "gis-callback.h"
 
-/* GisCallback */
-G_DEFINE_TYPE(GisCallback, gis_callback, GIS_TYPE_OBJECT);
-static void gis_callback_init(GisCallback *cb)
-{
-}
-
-static void gis_callback_class_init(GisCallbackClass *klass)
-{
-}
-
 /**
  * gis_callback_new:
  * @callback:  the function to call to draw the object
@@ -46,10 +39,30 @@ static void gis_callback_class_init(GisCallbackClass *klass)
  *
  * Returns: the new #GisCallback
  */
-GisCallback *gis_callback_new(GisCallbackFunc callback, gpointer user_data)
+GisCallback *gis_callback_new(GisCallbackFunc draw_cb, gpointer user_data)
 {
 	GisCallback *cb = g_object_new(GIS_TYPE_CALLBACK, NULL);
-	cb->callback  = callback;
+	cb->draw      = draw_cb;
 	cb->user_data = user_data;
 	return cb;
 }
+
+/* Proxy class methods to per-object methods */
+static void proxy_draw(GisObject *_cb, GisOpenGL *opengl)
+{
+	GisCallback *cb = GIS_CALLBACK(_cb);
+	if (cb->draw)
+		cb->draw(cb, opengl, cb->user_data);
+}
+
+/* GisCallback */
+G_DEFINE_TYPE(GisCallback, gis_callback, GIS_TYPE_OBJECT);
+static void gis_callback_init(GisCallback *cb)
+{
+}
+
+static void gis_callback_class_init(GisCallbackClass *klass)
+{
+	GisObjectClass *object_class = GIS_OBJECT_CLASS(klass);
+	object_class->draw = proxy_draw;
+}
diff --git a/src/objects/gis-callback.h b/src/objects/gis-callback.h
index b9c77da..db82739 100644
--- a/src/objects/gis-callback.h
+++ b/src/objects/gis-callback.h
@@ -40,11 +40,11 @@ typedef struct _GisCallbackClass GisCallbackClass;
  *
  * A function to be called when the callback object is being rendered
  */
-typedef void (*GisCallbackFunc)(GisCallback *callback, gpointer user_data);
+typedef void (*GisCallbackFunc)(GisCallback *callback, GisOpenGL *opengl, gpointer user_data);
 
 struct _GisCallback {
 	GisObject       parent;
-	GisCallbackFunc callback;
+	GisCallbackFunc draw;
 	gpointer        user_data;
 };
 
@@ -54,6 +54,6 @@ struct _GisCallbackClass {
 
 GType gis_callback_get_type(void);
 
-GisCallback *gis_callback_new(GisCallbackFunc callback, gpointer user_data);
+GisCallback *gis_callback_new(GisCallbackFunc draw_cb, gpointer user_data);
 
 #endif
diff --git a/src/objects/gis-marker.c b/src/objects/gis-marker.c
index f02da47..3073b0f 100644
--- a/src/objects/gis-marker.c
+++ b/src/objects/gis-marker.c
@@ -31,11 +31,9 @@
  */
 
 #include <config.h>
+#include <GL/gl.h>
 #include "gis-marker.h"
 
-/*************
- * GisMarker *
- *************/
 /**
  * gis_marker_new:
  * @label: a short description of the marker
@@ -57,7 +55,7 @@ GisMarker *gis_marker_new(const gchar *label)
 	marker->yoff  = HEIGHT-(RADIUS+OUTLINE);
 	marker->label = g_strdup(label);
 	marker->cairo = cairo_create(cairo_image_surface_create(
-				CAIRO_FORMAT_ARGB32, WIDTH, HEIGHT));
+			CAIRO_FORMAT_ARGB32, WIDTH, HEIGHT));
 
 	cairo_select_font_face(marker->cairo, "sans-serif",
 			CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
@@ -82,9 +80,64 @@ GisMarker *gis_marker_new(const gchar *label)
 
 	cairo_move_to(marker->cairo, marker->xoff+4, marker->yoff-8);
 	cairo_show_text(marker->cairo, marker->label);
+
+	/* Load GL texture */
+	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(cairo_get_target(marker->cairo)));
+	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+
 	return marker;
 }
 
+/* Drawing */
+static void gis_marker_draw(GisObject *_marker, GisOpenGL *opengl)
+{
+	GisMarker *marker = GIS_MARKER(_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;
+
+	//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, win_width, win_height, 0, -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);
+	glDisable(GL_CULL_FACE);
+	glBegin(GL_QUADS);
+	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();
+}
+
+/* GObject code */
 G_DEFINE_TYPE(GisMarker, gis_marker, GIS_TYPE_OBJECT);
 static void gis_marker_init(GisMarker *marker)
 {
@@ -92,8 +145,9 @@ static void gis_marker_init(GisMarker *marker)
 
 static void gis_marker_finalize(GObject *_marker)
 {
-	GisMarker *marker = GIS_MARKER(_marker);
 	//g_debug("GisMarker: finalize - %s", marker->label);
+	GisMarker *marker = GIS_MARKER(_marker);
+	glDeleteTextures(1, &marker->tex);
 	cairo_surface_t *surface = cairo_get_target(marker->cairo);
 	cairo_surface_destroy(surface);
 	cairo_destroy(marker->cairo);
@@ -102,5 +156,10 @@ static void gis_marker_finalize(GObject *_marker)
 
 static void gis_marker_class_init(GisMarkerClass *klass)
 {
-	G_OBJECT_CLASS(klass)->finalize = gis_marker_finalize;
+	g_debug("GisMarker: class_init");
+	GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
+	gobject_class->finalize = gis_marker_finalize;
+
+	GisObjectClass *object_class = GIS_OBJECT_CLASS(klass);
+	object_class->draw = gis_marker_draw;
 }
diff --git a/src/objects/gis-object.c b/src/objects/gis-object.c
index 9eff935..735e62e 100644
--- a/src/objects/gis-object.c
+++ b/src/objects/gis-object.c
@@ -29,12 +29,72 @@
  */
 
 #include <config.h>
+#include <math.h>
+#include <GL/gl.h>
+
 #include "gis-object.h"
 
 
 /*************
  * GisObject *
  *************/
+/**
+ * gis_object_draw:
+ * @object: the object
+ * @opengl: the viewer the object is being displayed in
+ *
+ * Perform any OpenGL commands necessasairy to draw the object.
+ *
+ * The GL_PROJECTION and GL_MODELVIEW matricies and GL_ALL_ATTRIB_BITS will be
+ * restored to the default state after the call to draw.
+ */
+void gis_object_draw(GisObject *object, GisOpenGL *opengl)
+{
+	GisObjectClass *klass = GIS_OBJECT_GET_CLASS(object);
+	if (!klass->draw) {
+		g_warning("GisObject: draw - Unimplemented");
+		return;
+	}
+
+	/* Skip hidden objects */
+	if (object->hidden)
+		return;
+
+	/* 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;
+	}
+
+	/* Save state, draw, restore state */
+	g_mutex_lock(opengl->sphere_lock);
+	glPushAttrib(GL_ALL_ATTRIB_BITS);
+	glMatrixMode(GL_PROJECTION); glPushMatrix();
+	glMatrixMode(GL_MODELVIEW);  glPushMatrix();
+
+	klass->draw(object, opengl);
+
+	glPopAttrib();
+	glMatrixMode(GL_PROJECTION); glPopMatrix();
+	glMatrixMode(GL_MODELVIEW);  glPopMatrix();
+	g_mutex_unlock(opengl->sphere_lock);
+}
+
 /* GObject stuff */
 G_DEFINE_ABSTRACT_TYPE(GisObject, gis_object, G_TYPE_OBJECT);
 static void gis_object_init(GisObject *object)
diff --git a/src/objects/gis-object.h b/src/objects/gis-object.h
index 6763a5f..3060c64 100644
--- a/src/objects/gis-object.h
+++ b/src/objects/gis-object.h
@@ -40,12 +40,19 @@ struct _GisObject {
 	gdouble  lod;
 };
 
+#include "gis-opengl.h"
 struct _GisObjectClass {
 	GObjectClass parent_class;
+
+	/* Move some of these to GObject? */
+	void (*draw) (GisObject *object, GisOpenGL *opengl);
 };
 
 GType gis_object_get_type(void);
 
+/* Implemented by sub-classes */
+void gis_object_draw(GisObject *object, GisOpenGL *opengl);
+
 /**
  * gis_object_center:
  * @object: The #GisObject to get the center of
diff --git a/src/objects/gis-tile.c b/src/objects/gis-tile.c
index 9ee747b..14e699d 100644
--- a/src/objects/gis-tile.c
+++ b/src/objects/gis-tile.c
@@ -33,6 +33,7 @@
  */
 
 #include <config.h>
+#include <GL/gl.h>
 #include "gis-tile.h"
 
 gchar *gis_tile_path_table[2][2] = {
@@ -280,6 +281,132 @@ void gis_tile_free(GisTile *root, GisTileFreeFunc free_func, gpointer user_data)
 	g_object_unref(root);
 }
 
+/* Draw a single tile */
+static void gis_tile_draw_one(GisTile *tile, GisOpenGL *opengl, GList *triangles)
+{
+	if (!tile || !tile->data)
+		return;
+	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);
+	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 xscale = tile->coords.e - tile->coords.w;
+	gdouble yscale = tile->coords.s - tile->coords.n;
+
+	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 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;
+
+		/* Scale to tile coords */
+		for (int i = 0; i < 3; i++) {
+			xy[i][0] = tile->coords.w + xy[i][0]*xscale;
+			xy[i][1] = tile->coords.n + xy[i][1]*yscale;
+		}
+
+		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);
+		glNormal3dv(tri->p.l->norm); glTexCoord2dv(xy[2]); glVertex3dv((double*)tri->p.l);
+		glEnd();
+	}
+}
+
+/* Draw the tile */
+static void gis_tile_draw_rec(GisTile *tile, GisOpenGL *opengl)
+{
+	/* Only draw children if possible */
+	gboolean has_children = FALSE;
+	GisTile *child;
+	gis_tile_foreach(tile, child)
+		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) {
+				gis_tile_draw_rec(child, opengl);
+			} 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)
+		gis_tile_draw_one(tile, opengl, triangles);
+	g_list_free(triangles);
+}
+
+static void gis_tile_draw(GisObject *tile, GisOpenGL *opengl)
+{
+	glEnable(GL_DEPTH_TEST);
+	glDepthFunc(GL_LESS);
+	gis_tile_draw_rec(GIS_TILE(tile), opengl);
+}
+
+
 /* GObject code */
 G_DEFINE_TYPE(GisTile, gis_tile, GIS_TYPE_OBJECT);
 static void gis_tile_init(GisTile *tile)
@@ -288,4 +415,7 @@ static void gis_tile_init(GisTile *tile)
 
 static void gis_tile_class_init(GisTileClass *klass)
 {
+	g_debug("GisTile: class_init");
+	GisObjectClass *object_class = GIS_OBJECT_CLASS(klass);
+	object_class->draw = gis_tile_draw;
 }
diff --git a/src/plugins/env.c b/src/plugins/env.c
index 75ba570..1a0110c 100644
--- a/src/plugins/env.c
+++ b/src/plugins/env.c
@@ -35,7 +35,7 @@
 /***********
  * Helpers *
  ***********/
-static void expose(GisCallback *callback, gpointer _env)
+static void expose(GisCallback *callback, GisOpenGL *opengl, gpointer _env)
 {
 	GisPluginEnv *env = GIS_PLUGIN_ENV(_env);
 	g_debug("GisPluginEnv: expose");



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]