From 72643f9bd6c87e08670eb1a0aad2da5573b99774 Mon Sep 17 00:00:00 2001 From: Andy Spencer Date: Sun, 24 Oct 2010 11:57:09 +0000 Subject: [PATCH] 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 . */ +/** + * 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 #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 +#include #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 +#include +#include + #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 +#include #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"); -- 2.41.0