#!/bin/bash
dir=$(dirname $(readlink -f $0))
-CFLAGS="-g -Werror -Wno-unused" \
+CFLAGS="-g -Werror -Wno-unused $CFLAGS" \
./autogen.sh \
"--libdir=$dir/src/plugins" \
"--datadir=$dir/data" \
*.lo
*.o
*.so
+*.prof
.deps
.libs
+gmon.*
aweather
level2
wsr88ddec
bin_PROGRAMS = gis_test wms_test
gis_test_SOURCES = gis_test.c gis.h
-gis_test_LDADD = $(AM_LDADD) libgis.la
+gis_test_LDADD = $(AM_LDADD) .libs/libgis.a
wms_test_SOURCES = wms_test.c gis-world.c gis-world.h wms.c wms.h
wms_test_LDADD = $(AM_LDADD)
test: all
make -C ../plugins all
- LD_LIBRARY_PATH=.libs .libs/gis_test
+ ./gis_test
gdb: all
LD_LIBRARY_PATH=.libs gdb .libs/gis_test
#define FOV_DIST 2000.0
#define MPPX(dist) (4*dist/FOV_DIST)
+// #define ROAM_DEBUG
/*************
* ROAM Code *
{
GisOpenGL *self = _self;
+ gdouble lat, lon, elev;
+ xyz2lle(point->x, point->y, point->z, &lat, &lon, &elev);
+
+#ifdef ROAM_DEBUG
+ lle2xyz(lat, lon, 0, &point->x, &point->y, &point->z);
+ return;
+#endif
+
gdouble cam_lle[3], cam_xyz[3];
gis_view_get_location(self->view, &cam_lle[0], &cam_lle[1], &cam_lle[2]);
lle2xyz(cam_lle[0], cam_lle[1], cam_lle[2], &cam_xyz[0], &cam_xyz[1], &cam_xyz[2]);
- gdouble lat, lon, elev;
- xyz2lle(point->x, point->y, point->z, &lat, &lon, &elev);
-
gdouble res = MPPX(distd(cam_xyz, (double*)point));
//g_message("lat=%f, lon=%f, res=%f", lat, lon, res);
- WmsCacheNode *node = wms_info_fetch_cache(self->srtm, res, lat, lon, NULL, roam_queue_draw, self);
+ point->node = wms_info_fetch_cache(self->srtm, point->node,
+ res, lat, lon, NULL, roam_queue_draw, self);
- if (node) {
- WmsBil *bil = node->data;
+ if (point->node) {
+ WmsBil *bil = point->node->data;
gint w = bil->width;
gint h = bil->height;
- gdouble xmin = node->latlon[0];
- gdouble ymin = node->latlon[1];
- gdouble xmax = node->latlon[2];
- gdouble ymax = node->latlon[3];
+ gdouble xmin = point->node->latlon[0];
+ gdouble ymin = point->node->latlon[1];
+ gdouble xmax = point->node->latlon[2];
+ gdouble ymax = point->node->latlon[3];
gdouble xdist = xmax - xmin;
gdouble ydist = ymax - ymin;
void roam_tri_func(RoamTriangle *tri, gpointer _self)
{
+#ifdef ROAM_DEBUG
+ glBegin(GL_TRIANGLES);
+ glNormal3dv(tri->p.r->norm); glVertex3dv((double*)tri->p.r);
+ glNormal3dv(tri->p.m->norm); glVertex3dv((double*)tri->p.m);
+ glNormal3dv(tri->p.l->norm); glVertex3dv((double*)tri->p.l);
+ glEnd();
+ return;
+#endif
+
GisOpenGL *self = _self;
if (tri->error < 0) return;
xyz2lle(tri->p.l->x, tri->p.l->y, tri->p.l->z, &lat[2], &lon[2], &elev[2]);
gdouble lat_max = MAX(MAX(lat[0], lat[1]), lat[2]);
gdouble lat_min = MIN(MIN(lat[0], lat[1]), lat[2]);
+ gdouble lat_avg = (lat_min+lat_max)/2;
gdouble lon_max = MAX(MAX(lon[0], lon[1]), lon[2]);
gdouble lon_min = MIN(MIN(lon[0], lon[1]), lon[2]);
+ gdouble lon_avg = (lon_min+lon_max)/2;
/* Get target resolution */
gdouble cam_lle[3], cam_xyz[3];
* - Also fetch center textures that aren't touched by a corner
* - Idea: send {lat,lon}{min,max} to fetch_cache and handle it in the recursion */
/* Fetch textures */
- WmsCacheNode *textures[4] = {
- wms_info_fetch_cache(self->bmng, res, lat_min, lon_min, NULL, roam_queue_draw, self),
- wms_info_fetch_cache(self->bmng, res, lat_max, lon_min, NULL, roam_queue_draw, self),
- wms_info_fetch_cache(self->bmng, res, lat_min, lon_max, NULL, roam_queue_draw, self),
- wms_info_fetch_cache(self->bmng, res, lat_max, lon_max, NULL, roam_queue_draw, self),
- };
+ tri->nodes[0] = wms_info_fetch_cache(self->bmng, tri->nodes[0], res, lat_min, lon_min, NULL, roam_queue_draw, self);
+ tri->nodes[1] = wms_info_fetch_cache(self->bmng, tri->nodes[1], res, lat_max, lon_min, NULL, roam_queue_draw, self);
+ tri->nodes[2] = wms_info_fetch_cache(self->bmng, tri->nodes[2], res, lat_min, lon_max, NULL, roam_queue_draw, self);
+ tri->nodes[3] = wms_info_fetch_cache(self->bmng, tri->nodes[3], res, lat_max, lon_max, NULL, roam_queue_draw, self);
+ tri->nodes[4] = wms_info_fetch_cache(self->bmng, tri->nodes[4], res, lat_avg, lon_avg, NULL, roam_queue_draw, self);
+ /* Hopefully get all textures at the same resolution to prevent overlaps */
+ //gdouble maxres = 0;
+ //for (int i = 0; i < 5; i++)
+ // if (tri->nodes[i] && tri->nodes[i]->res > maxres)
+ // maxres = tri->nodes[i]->res;
+ //if (maxres != 0) {
+ // tri->nodes[0] = wms_info_fetch_cache(self->bmng, tri->nodes[0], maxres, lat_min, lon_min, NULL, roam_queue_draw, self);
+ // tri->nodes[1] = wms_info_fetch_cache(self->bmng, tri->nodes[1], maxres, lat_max, lon_min, NULL, roam_queue_draw, self);
+ // tri->nodes[2] = wms_info_fetch_cache(self->bmng, tri->nodes[2], maxres, lat_min, lon_max, NULL, roam_queue_draw, self);
+ // tri->nodes[3] = wms_info_fetch_cache(self->bmng, tri->nodes[3], maxres, lat_max, lon_max, NULL, roam_queue_draw, self);
+ // tri->nodes[4] = wms_info_fetch_cache(self->bmng, tri->nodes[4], maxres, lat_avg, lon_avg, NULL, roam_queue_draw, self);
+ //}
/* Vertex color for hieght map viewing, 8848m == Everest */
gfloat colors[] = {
/* TODO: Prevent double exposure when of hi-res textures on top of
* low-res textures when some high-res textures are not yet loaded. */
glBlendFunc(GL_ONE, GL_ZERO);
- for (int i = 0; i < 4; i++) {
+ for (int i = 0; i < 5; i++) {
/* Skip missing textures */
- if (textures[i] == NULL)
+ if (tri->nodes[i] == NULL)
continue;
/* Skip already drawn textures */
switch (i) {
- case 3: if (textures[i] == textures[2]) continue;
- case 2: if (textures[i] == textures[1]) continue;
- case 1: if (textures[i] == textures[0]) continue;
+ case 4: if (tri->nodes[i] == tri->nodes[3]) continue;
+ case 3: if (tri->nodes[i] == tri->nodes[2]) continue;
+ case 2: if (tri->nodes[i] == tri->nodes[1]) continue;
+ case 1: if (tri->nodes[i] == tri->nodes[0]) continue;
}
- WmsCacheNode *node = textures[i];
+ WmsCacheNode *node = tri->nodes[i];
if (node->latlon[0] == -180) {
if (lon[0] < -90 || lon[1] < -90 || lon[2] < -90) {
/* Lighting */
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
+#ifdef ROAM_DEBUG
+ float light_ambient[] = {0.7f, 0.7f, 0.7f, 1.0f};
+ float light_diffuse[] = {2.0f, 2.0f, 2.0f, 1.0f};
+#else
float light_ambient[] = {0.2f, 0.2f, 0.2f, 1.0f};
float light_diffuse[] = {5.0f, 5.0f, 5.0f, 1.0f};
+#endif
float light_position[] = {-13*EARTH_R, 1*EARTH_R, 3*EARTH_R, 1.0f};
glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);
glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
+#ifndef ROAM_DEBUG
glCullFace(GL_BACK);
glEnable(GL_CULL_FACE);
+#endif
glClearDepth(1.0);
glDepthFunc(GL_LEQUAL);
double ang = atan(height/FOV_DIST);
gluPerspective(rad2deg(ang)*2, width/height, 1, 20*EARTH_R);
- if (self->sphere)
- roam_sphere_update_errors(self->sphere);
+#ifndef ROAM_DEBUG
+ roam_sphere_update(self->sphere);
+#endif
gis_opengl_end(self);
return FALSE;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+#ifndef ROAM_DEBUG
set_visuals(self);
glEnable(GL_TEXTURE_2D);
roam_sphere_draw(self->sphere);
+#endif
+
+#ifdef ROAM_DEBUG
+ set_visuals(self);
+ 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);
+#endif
//glDisable(GL_TEXTURE_2D);
//glEnable(GL_COLOR_MATERIAL);
//glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
//roam_sphere_draw(self->sphere);
- //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);
-
gis_plugins_foreach(self->plugins, G_CALLBACK(on_expose_plugin), self);
set_visuals(self);
else if (kv == GDK_Right || kv == GDK_l) gis_view_pan(self->view, 0, pan, 0);
else if (kv == GDK_minus || kv == GDK_o) gis_view_zoom(self->view, 10./9);
else if (kv == GDK_plus || kv == GDK_i) gis_view_zoom(self->view, 9./10);
- else if (kv == GDK_H ) gis_view_rotate(self->view, 0, 0, -10);
- else if (kv == GDK_J ) gis_view_rotate(self->view, 10, 0, 0);
- else if (kv == GDK_K ) gis_view_rotate(self->view, -10, 0, 0);
- else if (kv == GDK_L ) gis_view_rotate(self->view, 0, 0, 10);
+ else if (kv == GDK_H) gis_view_rotate(self->view, 0, 0, -10);
+ else if (kv == GDK_J) gis_view_rotate(self->view, 10, 0, 0);
+ else if (kv == GDK_K) gis_view_rotate(self->view, -10, 0, 0);
+ else if (kv == GDK_L) gis_view_rotate(self->view, 0, 0, 10);
+
+ /* Testing */
+#ifdef ROAM_DEBUG
+ else if (kv == GDK_n) roam_sphere_split_one(self->sphere);
+ else if (kv == GDK_p) roam_sphere_merge_one(self->sphere);
+ else if (kv == GDK_r) roam_sphere_split_merge(self->sphere);
+ else if (kv == GDK_u) roam_sphere_update(self->sphere);
+ gtk_widget_queue_draw(GTK_WIDGET(self));
+#endif
+
return TRUE;
}
{
gis_opengl_begin(self);
set_visuals(self);
- roam_sphere_update_errors(self->sphere);
+#ifndef ROAM_DEBUG
+ roam_sphere_update(self->sphere);
+#endif
gis_opengl_redraw(self);
gis_opengl_end(self);
}
GDK_KEY_PRESS_MASK);
g_object_set(self, "can-focus", TRUE, NULL);
+#ifndef ROAM_DEBUG
self->sm_source = g_timeout_add(10, (GSourceFunc)on_idle, self);
+#endif
g_signal_connect(self, "realize", G_CALLBACK(on_realize), NULL);
g_signal_connect(self, "configure-event", G_CALLBACK(on_configure), NULL);
g_pqueue_node_foreach (pqueue->root, NULL, func, user_data);
}
+static void
+g_pqueue_add_ptr_cb (gpointer obj, GPtrArray *ptrs)
+{
+ g_ptr_array_add(ptrs, obj);
+}
+/**
+ * g_pqueue_get_array:
+ * @pqueue: a #GQueue.
+ *
+ * Construct a GPtrArray for the items in pqueue. This can be useful when
+ * updating the priorities of all the elements in pqueue.
+ *
+ * Returns: A GPtrArray containing a pointer to each item in pqueue
+ *
+ * Since: 2.x
+ */
+GPtrArray *
+g_pqueue_get_array (GPQueue *pqueue)
+{
+ GPtrArray *ptrs = g_ptr_array_new();
+ g_pqueue_foreach(pqueue, (GFunc)g_pqueue_add_ptr_cb, ptrs);
+ return ptrs;
+}
+
static inline gint
cmp (GPQueue *pqueue,
GPQueueNode *a,
GFunc func,
gpointer user_data);
+GPtrArray* g_pqueue_get_array (GPQueue *pqueue);
+
GPQueueHandle g_pqueue_push (GPQueue *pqueue,
gpointer data);
/**
* TODO:
- * - Remove/free unused memory
* - Optimize for memory consumption
* - Profile for computation speed
- * - Implement good error algorithm
* - Target polygon count/detail
*/
+/* Misc */
+RoamView *roam_view_get()
+{
+ RoamView *view = g_new0(RoamView, 1);
+ glGetDoublev (GL_MODELVIEW_MATRIX, view->model);
+ glGetDoublev (GL_PROJECTION_MATRIX, view->proj);
+ glGetIntegerv(GL_VIEWPORT, view->view);
+ return view;
+}
+
/* For GPQueue comparators */
static gint tri_cmp(RoamTriangle *a, RoamTriangle *b, gpointer data)
{
self->z = z;
return self;
}
+RoamPoint *roam_point_dup(RoamPoint *self)
+{
+ RoamPoint *new = g_memdup(self, sizeof(RoamPoint));
+ new->tris = 0;
+ return new;
+}
void roam_point_add_triangle(RoamPoint *self, RoamTriangle *triangle)
{
for (int i = 0; i < 3; i++) {
- self->norm[i] *= self->refs;
+ self->norm[i] *= self->tris;
self->norm[i] += triangle->norm[i];
}
- self->refs++;
+ self->tris++;
for (int i = 0; i < 3; i++)
- self->norm[i] /= self->refs;
+ self->norm[i] /= self->tris;
}
void roam_point_remove_triangle(RoamPoint *self, RoamTriangle *triangle)
{
for (int i = 0; i < 3; i++) {
- self->norm[i] *= self->refs;
+ self->norm[i] *= self->tris;
self->norm[i] -= triangle->norm[i];
}
- self->refs--;
+ self->tris--;
for (int i = 0; i < 3; i++)
- self->norm[i] /= self->refs;
+ self->norm[i] /= self->tris;
+}
+void roam_point_update(RoamPoint *self, RoamSphere *sphere, gboolean do_height)
+{
+ if (!self->cached) {
+ /* Cache height */
+ if (do_height)
+ sphere->height_func(self, sphere->user_data);
+
+ /* Cache projection */
+ gluProject(self->x, self->y, self->z,
+ sphere->view->model, sphere->view->proj, sphere->view->view,
+ &self->px, &self->py, &self->pz);
+
+ self->cached = TRUE;
+ }
+}
+void roam_point_clear(RoamPoint *self)
+{
+ self->cached = FALSE;
}
/****************
self->norm[2] = pa[0] * pb[1] - pa[1] * pb[0];
double total = sqrt(self->norm[0] * self->norm[0] +
- self->norm[1] * self->norm[1] +
- self->norm[2] * self->norm[2]);
+ self->norm[1] * self->norm[1] +
+ self->norm[2] * self->norm[2]);
self->norm[0] /= total;
self->norm[1] /= total;
roam_point_add_triangle(self->p.m, self);
roam_point_add_triangle(self->p.r, self);
- roam_triangle_update_error(self, sphere, NULL);
+ if (sphere->view)
+ roam_triangle_update(self, sphere);
self->handle = g_pqueue_push(sphere->triangles, self);
}
else g_assert_not_reached();
}
-gboolean roam_triangle_update_error_visible(RoamPoint *point, double *model, double *proj)
+gboolean roam_point_visible(RoamPoint *self, RoamSphere *sphere)
{
- double x, y, z;
- int view[4] = {0,0,1,1};
- if (!gluProject(point->x, point->y, point->z, model, proj, view, &x, &y, &z)) {
- g_warning("RoamTriangle: update_error_visible - gluProject failed");
- return TRUE;
- }
- return !(x < 0 || x > 1 ||
- y < 0 || y > 1 ||
- z < 0 || z > 1);
+ gint *view = sphere->view->view;
+ return self->px > view[0] && self->px < view[2] &&
+ self->py > view[1] && self->py < view[3] &&
+ self->pz > 0 && self->pz < 1;
+ //double x, y, z;
+ //int view[4] = {0,0,1,1};
+ //gluProject(self->x, self->y, self->z,
+ // sphere->view->model, sphere->view->proj, view,
+ // &x, &y, &z);
+ //return !(x < 0 || x > 1 ||
+ // y < 0 || y > 1 ||
+ // z < 0 || z > 1);
}
-
-void roam_triangle_update_error(RoamTriangle *self, RoamSphere *sphere, GPQueue *triangles)
+gboolean roam_triangle_visible(RoamTriangle *self, RoamSphere *sphere)
{
- RoamPoint cur = {
- (self->p.l->x + self->p.r->x)/2,
- (self->p.l->y + self->p.r->y)/2,
- (self->p.l->z + self->p.r->z)/2,
- };
- RoamPoint good = cur;
- roam_sphere_update_point(sphere, &good);
-
- //g_message("cur = (%+5.2f %+5.2f %+5.2f) good = (%+5.2f %+5.2f %+5.2f)",
- // cur[0], cur[1], cur[2], good[0], good[1], good[2]);
-
- double model[16], proj[16];
- int view[4];
- glGetError();
- glGetDoublev(GL_MODELVIEW_MATRIX, model);
- glGetDoublev(GL_PROJECTION_MATRIX, proj);
- glGetIntegerv(GL_VIEWPORT, view);
- if (glGetError() != GL_NO_ERROR) {
- g_warning("RoamTriangle: update_error - glGet failed");
- return;
- }
+ /* Do this with a bounding box */
+ return roam_point_visible(self->p.l, sphere) ||
+ roam_point_visible(self->p.m, sphere) ||
+ roam_point_visible(self->p.r, sphere);
+}
- double scur[3], sgood[3];
- gluProject( cur.x, cur.y, cur.z, model,proj,view, &scur[0], &scur[1], &scur[2]);
- gluProject(good.x,good.y,good.z, model,proj,view, &sgood[0],&sgood[1],&sgood[2]);
+void roam_triangle_update(RoamTriangle *self, RoamSphere *sphere)
+{
+ /* Update points */
+ roam_point_update(self->p.l, sphere, TRUE);
+ roam_point_update(self->p.m, sphere, TRUE);
+ roam_point_update(self->p.r, sphere, TRUE);
/* Not exactly correct, could be out on both sides (middle in) */
- if (!roam_triangle_update_error_visible(self->p.l, model, proj) &&
- !roam_triangle_update_error_visible(self->p.m, model, proj) &&
- !roam_triangle_update_error_visible(self->p.r, model, proj) &&
- !roam_triangle_update_error_visible(&good, model, proj)) {
+ if (!roam_triangle_visible(self, sphere)) {
self->error = -1;
} else {
- self->error = sqrt((scur[0]-sgood[0])*(scur[0]-sgood[0]) +
- (scur[1]-sgood[1])*(scur[1]-sgood[1]));
-
- /* Multiply by size of triangle (todo, do this in screen space) */
- double sa[3], sb[3], sc[3];
- double *a = (double*)self->p.l;
- double *b = (double*)self->p.m;
- double *c = (double*)self->p.r;
- gluProject(a[0],a[1],a[2], model,proj,view, &sa[0],&sa[1],&sa[2]);
- gluProject(b[0],b[1],b[2], model,proj,view, &sb[0],&sb[1],&sb[2]);
- gluProject(c[0],c[1],c[2], model,proj,view, &sc[0],&sc[1],&sc[2]);
- double size = -( sa[0]*(sb[1]-sc[1]) + sb[0]*(sc[1]-sa[1]) + sc[0]*(sa[1]-sb[1]) ) / 2.0;
- //g_message("%f * %f = %f", self->error, size, self->error * size);
+ RoamPoint *l = self->p.l;
+ RoamPoint *m = self->p.m;
+ RoamPoint *r = self->p.r;
+
+ /* TODO: share this with the base */
+ RoamPoint base = { (l->x + r->x)/2,
+ (l->y + r->y)/2,
+ (l->z + r->z)/2 };
+ RoamPoint good = base;
+ roam_point_update(&base, sphere, FALSE);
+ roam_point_update(&good, sphere, TRUE);
+
+ self->error = sqrt((base.px - good.px) * (base.px - good.px) +
+ (base.py - good.py) * (base.py - good.py));
+
+ /* Multiply by size of triangle */
+ double size = -( l->px * (m->py - r->py) +
+ m->px * (r->py - l->py) +
+ r->px * (l->py - m->py) ) / 2.0;
+
/* Size < 0 == backface */
self->error *= size;
}
+}
- if (triangles)
- g_pqueue_priority_changed(triangles, self->handle);
+void roam_triangle_clear(RoamTriangle *self)
+{
+ /* Clear points */
+ roam_point_clear(self->p.l);
+ roam_point_clear(self->p.m);
+ roam_point_clear(self->p.r);
}
void roam_triangle_split(RoamTriangle *self, RoamSphere *sphere)
RoamTriangle *base = self->t.b;
- /* Calculate midpoint */
+ /* Duplicate midpoint */
RoamPoint *mid = roam_point_new(
- (self->p.l->x + self->p.r->x)/2,
- (self->p.l->y + self->p.r->y)/2,
- (self->p.l->z + self->p.r->z)/2
- );
- roam_sphere_update_point(sphere, mid);
+ (self->p.l->x + self->p.r->x)/2,
+ (self->p.l->y + self->p.r->y)/2,
+ (self->p.l->z + self->p.r->z)/2);
+ roam_point_update(mid, sphere, TRUE);
/* Add new triangles */
RoamTriangle *sl = roam_triangle_new(self->p.m, mid, self->p.l); // Self Left
/* Add/Remove diamonds */
RoamDiamond *diamond = roam_diamond_new(self, base, sl, sr, bl, br);
+ roam_diamond_update(diamond, sphere);
roam_diamond_add(diamond, sphere);
- roam_diamond_remove(self->diamond, sphere);
- roam_diamond_remove(base->diamond, sphere);
+ roam_diamond_remove(self->parent, sphere);
+ roam_diamond_remove(base->parent, sphere);
}
void roam_triangle_draw_normal(RoamTriangle *self)
{
RoamDiamond *self = g_new0(RoamDiamond, 1);
- self->kid[0] = kid0;
- self->kid[1] = kid1;
- self->kid[2] = kid2;
- self->kid[3] = kid3;
+ self->kids[0] = kid0;
+ self->kids[1] = kid1;
+ self->kids[2] = kid2;
+ self->kids[3] = kid3;
- kid0->diamond = self;
- kid1->diamond = self;
- kid2->diamond = self;
- kid3->diamond = self;
+ kid0->parent = self;
+ kid1->parent = self;
+ kid2->parent = self;
+ kid3->parent = self;
- self->parent[0] = parent0;
- self->parent[1] = parent1;
+ self->parents[0] = parent0;
+ self->parents[1] = parent1;
return self;
}
void roam_diamond_add(RoamDiamond *self, RoamSphere *sphere)
{
self->active = TRUE;
- roam_diamond_update_error(self, sphere, NULL);
+ self->error = MAX(self->parents[0]->error, self->parents[1]->error);
self->handle = g_pqueue_push(sphere->diamonds, self);
}
void roam_diamond_remove(RoamDiamond *self, RoamSphere *sphere)
sphere->polys -= 2;
/* TODO: pick the best split */
- RoamTriangle **kid = self->kid;
+ RoamTriangle **kids = self->kids;
/* Create triangles */
- RoamTriangle *tri = self->parent[0];
- RoamTriangle *base = self->parent[1];
+ RoamTriangle *tri = self->parents[0];
+ RoamTriangle *base = self->parents[1];
- roam_triangle_add(tri, kid[0]->t.b, base, kid[1]->t.b, sphere);
- roam_triangle_add(base, kid[2]->t.b, tri, kid[3]->t.b, sphere);
+ roam_triangle_add(tri, kids[0]->t.b, base, kids[1]->t.b, sphere);
+ roam_triangle_add(base, kids[2]->t.b, tri, kids[3]->t.b, sphere);
- roam_triangle_sync_neighbors(tri, kid[0], kid[0]->t.b);
- roam_triangle_sync_neighbors(tri, kid[1], kid[1]->t.b);
- roam_triangle_sync_neighbors(base, kid[2], kid[2]->t.b);
- roam_triangle_sync_neighbors(base, kid[3], kid[3]->t.b);
+ roam_triangle_sync_neighbors(tri, kids[0], kids[0]->t.b);
+ roam_triangle_sync_neighbors(tri, kids[1], kids[1]->t.b);
+ roam_triangle_sync_neighbors(base, kids[2], kids[2]->t.b);
+ roam_triangle_sync_neighbors(base, kids[3], kids[3]->t.b);
/* Remove triangles */
- roam_triangle_remove(kid[0], sphere);
- roam_triangle_remove(kid[1], sphere);
- roam_triangle_remove(kid[2], sphere);
- roam_triangle_remove(kid[3], sphere);
+ roam_triangle_remove(kids[0], sphere);
+ roam_triangle_remove(kids[1], sphere);
+ roam_triangle_remove(kids[2], sphere);
+ roam_triangle_remove(kids[3], sphere);
/* Add/Remove triangles */
if (tri->t.l->t.l == tri->t.r->t.r &&
- tri->t.l->t.l != tri && tri->diamond)
- roam_diamond_add(tri->diamond, sphere);
+ tri->t.l->t.l != tri && tri->parent) {
+ roam_diamond_update(tri->parent, sphere);
+ roam_diamond_add(tri->parent, sphere);
+ }
if (base->t.l->t.l == base->t.r->t.r &&
- base->t.l->t.l != base && base->diamond)
- roam_diamond_add(base->diamond, sphere);
+ base->t.l->t.l != base && base->parent) {
+ roam_diamond_update(base->parent, sphere);
+ roam_diamond_add(base->parent, sphere);
+ }
/* Remove and free diamond and child triangles */
roam_diamond_remove(self, sphere);
- g_assert(self->kid[0]->p.m == self->kid[1]->p.m &&
- self->kid[1]->p.m == self->kid[2]->p.m &&
- self->kid[2]->p.m == self->kid[3]->p.m);
- g_assert(self->kid[0]->p.m->refs == 0);
- g_free(self->kid[0]->p.m);
- g_free(self->kid[0]);
- g_free(self->kid[1]);
- g_free(self->kid[2]);
- g_free(self->kid[3]);
+ g_assert(self->kids[0]->p.m == self->kids[1]->p.m &&
+ self->kids[1]->p.m == self->kids[2]->p.m &&
+ self->kids[2]->p.m == self->kids[3]->p.m);
+ g_assert(self->kids[0]->p.m->tris == 0);
+ g_free(self->kids[0]->p.m);
+ g_free(self->kids[0]);
+ g_free(self->kids[1]);
+ g_free(self->kids[2]);
+ g_free(self->kids[3]);
g_free(self);
}
-void roam_diamond_update_error(RoamDiamond *self, RoamSphere *sphere, GPQueue *diamonds)
+void roam_diamond_update(RoamDiamond *self, RoamSphere *sphere)
{
- roam_triangle_update_error(self->parent[0], sphere, NULL);
- roam_triangle_update_error(self->parent[1], sphere, NULL);
- self->error = MAX(self->parent[0]->error, self->parent[1]->error);
- if (diamonds)
- g_pqueue_priority_changed(diamonds, self->handle);
+ roam_triangle_update(self->parents[0], sphere);
+ roam_triangle_update(self->parents[1], sphere);
+ self->error = MAX(self->parents[0]->error, self->parents[1]->error);
}
/**************
};
RoamTriangle *triangles[12];
- for (int i = 0; i < 8; i++)
- roam_sphere_update_point(self, vertexes[i]);
for (int i = 0; i < 12; i++)
triangles[i] = roam_triangle_new(
vertexes[_triangles[i][0][0]],
return self;
}
-static void roam_sphere_update_errors_cb(gpointer obj, GPtrArray *ptrs)
+void roam_sphere_update(RoamSphere *self)
{
- g_ptr_array_add(ptrs, obj);
-}
-void roam_sphere_update_errors(RoamSphere *self)
-{
- g_debug("RoamSphere: update_errors - polys=%d", self->polys);
- GPtrArray *ptrs = g_ptr_array_new();
- g_pqueue_foreach(self->triangles, (GFunc)roam_sphere_update_errors_cb, ptrs);
- for (int i = 0; i < ptrs->len; i++)
- roam_triangle_update_error(ptrs->pdata[i], self, self->triangles);
- g_ptr_array_free(ptrs, TRUE);
+ g_debug("RoamSphere: update - polys=%d", self->polys);
+ if (self->view) g_free(self->view);
+ self->view = roam_view_get();
+
+ GPtrArray *tris = g_pqueue_get_array(self->triangles);
+ GPtrArray *dias = g_pqueue_get_array(self->diamonds);
+
+ for (int i = 0; i < tris->len; i++) {
+ /* Note: this also updates points */
+ RoamTriangle *tri = tris->pdata[i];
+ roam_triangle_clear(tri);
+ roam_triangle_update(tri, self);
+ g_pqueue_priority_changed(self->triangles, tri->handle);
+ }
- ptrs = g_ptr_array_new();
- g_pqueue_foreach(self->diamonds, (GFunc)roam_sphere_update_errors_cb, ptrs);
- for (int i = 0; i < ptrs->len; i++)
- roam_diamond_update_error(ptrs->pdata[i], self, self->diamonds);
- g_ptr_array_free(ptrs, TRUE);
-}
-void roam_sphere_update_point(RoamSphere *self, RoamPoint *point)
-{
- self->height_func(point, self->user_data);
+ for (int i = 0; i < dias->len; i++) {
+ RoamDiamond *dia = dias->pdata[i];
+ roam_diamond_update(dia, self);
+ g_pqueue_priority_changed(self->diamonds, dia->handle);
+ }
+
+ g_ptr_array_free(tris, TRUE);
+ g_ptr_array_free(dias, TRUE);
}
+
void roam_sphere_split_one(RoamSphere *self)
{
RoamTriangle *to_split = g_pqueue_peek(self->triangles);
{
gint iters = 0, max_iters = 500;
gint target = 2000;
- while (self->polys < target && iters++ < max_iters)
- roam_sphere_split_one(self);
- while (self->polys > target && iters++ < max_iters)
- roam_sphere_merge_one(self);
+
+ if (!self->view)
+ return 0;
+
+ if (target - self->polys > 100)
+ while (self->polys < target && iters++ < max_iters)
+ roam_sphere_split_one(self);
+
+ if (self->polys - target > 100)
+ while (self->polys > target && iters++ < max_iters)
+ roam_sphere_merge_one(self);
+
while (((RoamTriangle*)g_pqueue_peek(self->triangles))->error >
((RoamDiamond *)g_pqueue_peek(self->diamonds ))->error &&
iters++ < max_iters) {
+ roam_sphere_merge_one(self);
roam_sphere_split_one(self);
- if (((RoamTriangle*)g_pqueue_peek(self->triangles))->error >
- ((RoamDiamond *)g_pqueue_peek(self->diamonds ))->error)
- roam_sphere_merge_one(self);
}
+
return iters;
}
void roam_sphere_draw(RoamSphere *self)
}
static void roam_sphere_free_tri(RoamTriangle *tri)
{
- if (--tri->p.l->refs == 0) g_free(tri->p.l);
- if (--tri->p.m->refs == 0) g_free(tri->p.m);
- if (--tri->p.r->refs == 0) g_free(tri->p.r);
+ if (--tri->p.l->tris == 0) g_free(tri->p.l);
+ if (--tri->p.m->tris == 0) g_free(tri->p.m);
+ if (--tri->p.r->tris == 0) g_free(tri->p.r);
g_free(tri);
}
void roam_sphere_free(RoamSphere *self)
#define __ROAM_H__
#include "gpqueue.h"
+#include "wms.h"
/* Roam */
+typedef struct _RoamView RoamView;
typedef struct _RoamPoint RoamPoint;
typedef struct _RoamTriangle RoamTriangle;
typedef struct _RoamDiamond RoamDiamond;
typedef void (*RoamTriFunc)(RoamTriangle *triangle, gpointer user_data);
typedef void (*RoamHeightFunc)(RoamPoint *point, gpointer user_data);
+/* Misc */
+struct _RoamView {
+ gdouble model[16];
+ gdouble proj[16];
+ gint view[4];
+};
+
/*************
* RoamPoint *
*************/
struct _RoamPoint {
- double x,y,z;
- double coords; // Texture coordinantes
- double norm[3]; // Vertex normal
- int refs; // Reference count
+ gdouble x,y,z; // Model coordinates
+ gdouble px,py,pz; // Projected coordinates
+
+ gboolean cached; // Height/projection cached
+
+ gint tris; // Associated triangles
+ gdouble norm[3]; // Vertex normal
+
+ WmsCacheNode *node; // TODO: don't depend on wms
};
RoamPoint *roam_point_new(double x, double y, double z);
void roam_point_add_triangle(RoamPoint *point, RoamTriangle *triangle);
void roam_point_remove_triangle(RoamPoint *point, RoamTriangle *triangle);
+void roam_point_update(RoamPoint *point, RoamSphere *sphere, gboolean do_height);
+void roam_point_clear(RoamPoint *self);
/****************
* RoamTriangle *
struct _RoamTriangle {
struct { RoamPoint *l,*m,*r; } p;
struct { RoamTriangle *l,*b,*r; } t;
- RoamDiamond *diamond;
+ RoamDiamond *parent;
double norm[3];
double error;
GPQueueHandle handle;
+
+ WmsCacheNode *nodes[5]; // TODO: don't depend on wms
};
RoamTriangle *roam_triangle_new(RoamPoint *l, RoamPoint *m, RoamPoint *r);
void roam_triangle_add(RoamTriangle *triangle,
RoamTriangle *left, RoamTriangle *base, RoamTriangle *right,
RoamSphere *sphere);
void roam_triangle_remove(RoamTriangle *triangle, RoamSphere *sphere);
-void roam_triangle_update_error(RoamTriangle *triangle, RoamSphere *sphere, GPQueue *triangles);
+void roam_triangle_update(RoamTriangle *triangle, RoamSphere *sphere);
void roam_triangle_split(RoamTriangle *triangle, RoamSphere *sphere);
void roam_triangle_draw_normal(RoamTriangle *triangle);
* RoamDiamond *
***************/
struct _RoamDiamond {
- RoamTriangle *kid[4];
- RoamTriangle *parent[2];
+ RoamTriangle *kids[4];
+ RoamTriangle *parents[2];
double error;
gboolean active;
GPQueueHandle handle;
void roam_diamond_add(RoamDiamond *diamond, RoamSphere *sphere);
void roam_diamond_remove(RoamDiamond *diamond, RoamSphere *sphere);
void roam_diamond_merge(RoamDiamond *diamond, RoamSphere *sphere);
-void roam_diamond_update_error(RoamDiamond *self, RoamSphere *sphere, GPQueue *diamonds);
+void roam_diamond_update(RoamDiamond *self, RoamSphere *sphere);
/**************
* RoamSphere *
struct _RoamSphere {
GPQueue *triangles;
GPQueue *diamonds;
+ RoamView *view;
RoamTriFunc tri_func;
RoamHeightFunc height_func;
gpointer user_data;
gint polys;
};
RoamSphere *roam_sphere_new(RoamTriFunc tri_func, RoamHeightFunc height_func, gpointer user_data);
-void roam_sphere_update_errors(RoamSphere *sphere);
-void roam_sphere_update_point(RoamSphere *sphere, RoamPoint *point);
+void roam_sphere_update(RoamSphere *sphere);
void roam_sphere_split_one(RoamSphere *sphere);
void roam_sphere_merge_one(RoamSphere *sphere);
gint roam_sphere_split_merge(RoamSphere *sphere);
/****************
* WmsCacheNode *
****************/
-WmsCacheNode *wms_cache_node_new(gdouble xmin, gdouble ymin, gdouble xmax, gdouble ymax)
+WmsCacheNode *wms_cache_node_new(WmsCacheNode *parent,
+ gdouble xmin, gdouble ymin, gdouble xmax, gdouble ymax, gint width)
{
WmsCacheNode *self = g_new0(WmsCacheNode, 1);
//g_debug("WmsCacheNode: new - %p %+7.3f,%+7.3f,%+7.3f,%+7.3f",
self->latlon[1] = ymin;
self->latlon[2] = xmax;
self->latlon[3] = ymax;
+ self->parent = parent;
+ if (ymin <= 0 && ymax >= 0)
+ self->res = ll2m(xmax-xmin, 0)/width;
+ else
+ self->res = ll2m(xmax-xmin, MIN(ABS(ymin),ABS(ymax)))/width;
return self;
}
self->width = width;
self->height = height;
- self->max_age = 30;
+ self->max_age = 60;
self->atime = time(NULL);
self->gc_source = g_timeout_add_seconds(1, (GSourceFunc)wms_info_gc, self);
- self->cache_root = wms_cache_node_new(-180, -90, 180, 90);
+ self->cache_root = wms_cache_node_new(NULL, -180, -90, 180, 90, width);
self->soup = soup_session_async_new();
return self;
}
}
/* Break if current resolution (m/px) is good enough */
- if (ll2m(xdist, cur_lat)/info->width < resolution ||
- ll2m(xdist, cur_lat)/info->width < info->resolution)
+ if (ll2m(xdist, cur_lat)/info->width <= resolution ||
+ ll2m(xdist, cur_lat)/info->width <= info->resolution)
break;
/* Get locations for the correct sub-tile */
g_string_append_printf(target_path, "/%d%d", xpos, ypos);
if (target_node->children[xpos][ypos] == NULL)
target_node->children[xpos][ypos] =
- wms_cache_node_new(xmin, ymin, xmax, ymax);
+ wms_cache_node_new(target_node,
+ xmin, ymin, xmax, ymax, info->width);
target_node = target_node->children[xpos][ypos];
}
g_string_free(target_path, TRUE);
}
}
-WmsCacheNode *wms_info_fetch(WmsInfo *info, gdouble resolution, gdouble lat, gdouble lon,
+/* TODO:
+ * - Store WmsCacheNode in point and then use parent pointers to go up/down
+ * - If resolution doesn't change, tell caller to skip remaining calculations
+ */
+WmsCacheNode *wms_info_fetch(WmsInfo *info, WmsCacheNode *root,
+ gdouble resolution, gdouble lat, gdouble lon,
gboolean *correct)
{
+ if (root && root->data && !root->caching &&
+ root->latlon[0] <= lon && lon <= root->latlon[2] &&
+ root->latlon[1] <= lat && lat <= root->latlon[3] &&
+ root->res <= resolution &&
+ (!root->parent || root->parent->res > resolution)) {
+ *correct = TRUE;
+ info->atime = time(NULL);
+ root->atime = info->atime;
+ return root;
+ }
+
if (info->cache_root == NULL) {
*correct = FALSE;
return NULL;
}
WmsCacheNode *node = info->cache_root;
WmsCacheNode *best = (node && node->data ? node : NULL);
- gdouble x=lon, y=lat;
- gdouble xmin=-180, ymin=-90, xmax=180, ymax=90;
- gdouble xdist = xmax - xmin;
- gdouble ydist = ymax - ymin;
+ gdouble xmin=-180, ymin=-90, xmax=180, ymax=90, xdist=360, ydist=180;
gdouble cur_lat = 0;
int xpos=0, ypos=0;
- while (ll2m(xdist, cur_lat)/info->width > resolution &&
- ll2m(xdist, cur_lat)/info->width > info->resolution) {
+ gdouble cur_res = ll2m(xdist, cur_lat)/info->width;
+ while (cur_res > resolution &&
+ cur_res > info->resolution) {
- xpos = (int)(((x - xmin) / xdist) * 4);
- ypos = (int)(((y - ymin) / ydist) * 4);
- //g_message("%d = (int)(((%f - %f) / %f) * 4)",
- // xpos, x, xmin, xdist);
+ xpos = ((lon - xmin) / xdist) * 4;
+ ypos = ((lat - ymin) / ydist) * 4;
if (xpos == 4) xpos--;
if (ypos == 4) ypos--;
xdist /= 4;
ydist /= 4;
xmin = xmin + xdist*(xpos+0);
ymin = ymin + ydist*(ypos+0);
- xmax = xmin + xdist;
- ymax = ymin + ydist;
cur_lat = MIN(ABS(ymin), ABS(ymax));
node = node->children[xpos][ypos];
break;
if (node->data)
best = node;
+
+ cur_res = ll2m(xdist, cur_lat)/info->width;
}
if (correct)
*correct = (node && node == best);
return best;
}
-WmsCacheNode *wms_info_fetch_cache(WmsInfo *info, gdouble res, gdouble lat, gdouble lon,
+WmsCacheNode *wms_info_fetch_cache(WmsInfo *info, WmsCacheNode *root,
+ gdouble res, gdouble lat, gdouble lon,
WmsChunkCallback chunk_callback, WmsDoneCallback done_callback, gpointer user_data)
{
+ /* Fetch a node, if it isn't cached, cache it, also keep it's parent cached */
gboolean correct;
- WmsCacheNode *node = wms_info_fetch(info, res, lat, lon, &correct);
+ WmsCacheNode *node = wms_info_fetch(info, root, res, lat, lon, &correct);
if (!node || !correct)
wms_info_cache(info, res, lat, lon, chunk_callback, done_callback, user_data);
+ //else if (node->parent && node->parent->data == NULL)
+ // wms_info_cache(info, node->parent->res, lat, lon, chunk_callback, done_callback, user_data);
+ //else if (node->parent)
+ // node->parent->atime = node->atime;
return node;
}
static WmsCacheNode *wms_info_gc_cb(WmsInfo *self, WmsCacheNode *node)
{
gboolean empty = FALSE;
- if (node->data && !node->caching &&
- self->atime - node->atime > self->max_age) {
+ if (self->atime - node->atime > self->max_age &&
+ node->data && node != self->cache_root && !node->caching) {
g_debug("WmsInfo: gc - expired node %p", node);
self->freeer(node);
node->data = NULL;
}
if (empty) {
g_debug("WmsInfo: gc - empty branch %p", node);
+ /*
+ * TODO: Don't prune nodes while we're caching WmsCacheNodes in the Roam triangles
+ * and points
g_free(node);
return NULL;
+ */
+ return node;
} else {
return node;
}
****************/
struct _WmsCacheNode {
gpointer data;
- gdouble latlon[4]; // xmin,ymin,xmax,ymax
+ gdouble latlon[4]; // xmin,ymin,xmax,ymax
+ gdouble res; // xmin,ymin,xmax,ymax
gboolean caching;
- time_t atime;
+ time_t atime;
+ WmsCacheNode *parent;
WmsCacheNode *children[4][4];
};
-WmsCacheNode *wms_cache_node_new(gdouble xmin, gdouble ymin, gdouble xmax, gdouble ymax);
+WmsCacheNode *wms_cache_node_new(WmsCacheNode *parent, gdouble xmin, gdouble ymin, gdouble xmax, gdouble ymax, gint width);
void wms_cache_node_free(WmsCacheNode *node, WmsFreeer freeer);
* WmsInfo *
***********/
struct _WmsInfo {
- WmsLoader loader;
- WmsFreeer freeer;
gchar *uri_prefix;
gchar *uri_layer;
gchar *uri_format;
guint max_age;
guint gc_source;
time_t atime;
+ WmsLoader loader;
+ WmsFreeer freeer;
WmsCacheNode *cache_root;
- SoupSession *soup;
+ SoupSession *soup;
};
WmsInfo *wms_info_new(WmsLoader loader, WmsFreeer freeer,
WmsChunkCallback chunk_callback, WmsDoneCallback done_callback,
gpointer user_data);
-WmsCacheNode *wms_info_fetch(WmsInfo *info, gdouble resolution, gdouble lat, gdouble lon,
+WmsCacheNode *wms_info_fetch(WmsInfo *info, WmsCacheNode *root,
+ gdouble resolution, gdouble lat, gdouble lon,
gboolean *correct);
-WmsCacheNode *wms_info_fetch_cache(WmsInfo *info, gdouble resolution, gdouble lat, gdouble lon,
+WmsCacheNode *wms_info_fetch_cache(WmsInfo *info, WmsCacheNode *root,
+ gdouble resolution, gdouble lat, gdouble lon,
WmsChunkCallback chunk_callback, WmsDoneCallback done_callback,
gpointer user_data);
gdouble res = 200, lon = -101.76, lat = 46.85;
WmsInfo *bmng_info = wms_info_new_for_bmng(bmng_pixbuf_loader, bmng_pixbuf_freeer);
- wms_info_cache(bmng_info, res, lat, lon, chunk_callback, done_callback, &bmng_state);
+ wms_info_fetch_cache(bmng_info, NULL, res, lat, lon, chunk_callback, done_callback, &bmng_state);
WmsInfo *srtm_info = wms_info_new_for_srtm(srtm_pixbuf_loader, srtm_pixbuf_freeer);
- wms_info_cache(srtm_info, res, lat, lon, chunk_callback, done_callback, &srtm_state);
+ wms_info_fetch_cache(srtm_info, NULL, res, lat, lon, chunk_callback, done_callback, &srtm_state);
gtk_widget_show_all(win);
gtk_main();