/* * $Id: gs_query.c,v 2.1 2006/02/09 03:08:57 glynn Exp $ */ /* gs_query.c Bill Brown, USACERL January 1994 */ #include #include #include /* Values needed for Ray-Convex Polyhedron Intersection Test below * originally by Eric Haines, erich@eye.com */ #ifndef HUGE_VAL #define HUGE_VAL 1.7976931348623157e+308 #endif /* return codes */ #define MISSED 0 #define FRONTFACE 1 #define BACKFACE -1 /* end Ray-Convex Polyhedron Intersection Test values */ /***********************************************************************/ /* Crude method of intersecting line of sight with closest part of surface. Uses los vector to determine the point of first intersection which is returned in point. Returns 0 if los doesn't intersect. */ /* los should ALREADY be in surf-world coordinates */ int gs_los_intersect1(int surfid, float (*los)[3], float *point) { float dx, dy, dz, u_d[3]; float a[3], incr, min_incr, tlen, len; int outside, above, below, edge, istep; float b[3]; geosurf *gs; typbuff *buf; if (NULL == (gs = gs_get_surf(surfid))) { return (0); } if (0 == GS_v3dir(los[FROM], los[TO], u_d)) { return (0); } buf = gs_get_att_typbuff(gs, ATT_TOPO, 0); istep = edge = below = 0; len = 0.0; tlen = GS_distance(los[FROM], los[TO]); incr = tlen / 1000.0; min_incr = incr / 1000.0; dx = incr * u_d[X]; dy = incr * u_d[Y]; dz = incr * u_d[Z]; a[X] = los[FROM][X]; a[Y] = los[FROM][Y]; a[Z] = los[FROM][Z]; b[X] = a[X] - gs->x_trans; b[Y] = a[Y] - gs->y_trans; if (viewcell_tri_interp(gs, buf, b, 0)) { /* expects surface coords */ b[Z] += gs->z_trans; if (a[Z] < b[Z]) { /* viewing from below surface */ /* don't use this method fprintf(stderr,"view from below\n"); below = 1; */ return (0); } } while (incr > min_incr) { outside = 0; above = 0; b[X] = a[X] - gs->x_trans; b[Y] = a[Y] - gs->y_trans; if (viewcell_tri_interp(gs, buf, b, 0)) { /* ignores masks */ b[Z] += gs->z_trans; above = (a[Z] > b[Z]); } else { outside = 1; if (istep > 10) { edge = 1; below = 1; } } while (outside || above) { a[X] += dx; a[Y] += dy; a[Z] += dz; len += incr; outside = 0; above = 0; b[X] = a[X] - gs->x_trans; b[Y] = a[Y] - gs->y_trans; if (viewcell_tri_interp(gs, buf, b, 0)) { b[Z] += gs->z_trans; above = (a[Z] > b[Z]); } else { outside = 1; } if (len > tlen) { return 0; /* over surface *//* under surface */ } } /* could look for spikes here - see if any data points along shadow of line on surf go above los */ /* back up several spots? */ a[X] -= (1.0 * dx); a[Y] -= (1.0 * dy); a[Z] -= (1.0 * dz); incr /= 2.0; ++istep; dx = incr * u_d[X]; dy = incr * u_d[Y]; dz = incr * u_d[Z]; } if ((edge) && (b[Z] - (a[Z] + dz * 2.0) > incr * u_d[Z])) { fprintf(stderr, "looking under surface\n"); return 0; } point[X] = b[X]; point[Y] = b[Y]; point[Z] = b[Z] - gs->z_trans; return (1); } /* * This version uses the shadow of the los projected down to * the surface to generate a line_on_surf, then follows each * point in that line until the los intersects it. */ int gs_los_intersect(int surfid, float **los, float *point) { double incr; float p1, p2, u_d[3]; int above, ret, num, i, usedx; float a[3], b[3]; float bgn[3], end[3], a1[3]; geosurf *gs; typbuff *buf; Point3 *points; if (NULL == (gs = gs_get_surf(surfid))) { return (0); } if (0 == GS_v3dir(los[FROM], los[TO], u_d)) { return (0); } buf = gs_get_att_typbuff(gs, ATT_TOPO, 0); GS_v3eq(bgn, los[FROM]); GS_v3eq(end, los[TO]); bgn[X] -= gs->x_trans; bgn[Y] -= gs->y_trans; end[X] -= gs->x_trans; end[Y] -= gs->y_trans; /* trans? */ points = gsdrape_get_allsegments(gs, bgn, end, &num); /* DEBUG { float t1[3], t2[3]; t1[X] = los[FROM][X] ; t1[Y] = los[FROM][Y] ; t2[X] = los[TO][X] ; t2[Y] = los[TO][Y] ; GS_set_draw(GSD_FRONT); gsd_pushmatrix(); gsd_do_scale(1); gsd_translate(gs->x_trans, gs->y_trans, gs->z_trans); gsd_linewidth(1); gsd_color_func(GS_default_draw_color()); gsd_line_onsurf(gs, t1, t2); gsd_popmatrix(); GS_set_draw(GSD_BACK); gsd_flush(); } fprintf(stderr,"%d points to check\n", num); fprintf(stderr,"point0 = %.6lf %.6lf %.6lf FT =%.6lf %.6lf %.6lf\n", points[0][X],points[0][Y],points[0][Z], los[FROM][X],los[FROM][Y],los[FROM][Z]); fprintf(stderr,"incr1 = %.6lf: %.6lf %.6lf %.6lf\n",incr,u_d[X],u_d[Y],u_d[Z]); fprintf(stderr,"first point below surf\n"); fprintf(stderr,"incr2 = %f\n", (float)incr); fprintf(stderr,"(%d/%d) %f > %f\n", i,num, a[Z], points[i][Z]); fprintf(stderr,"incr3 = %f\n", (float)incr); fprintf(stderr,"all points above surf\n"); */ if (num < 2) { fprintf(stderr, "-----------------------------\n"); fprintf(stderr, "%d points to check\n", num); return (0); } /* use larger of deltas for better precision */ usedx = (fabs(u_d[X]) > fabs(u_d[Y])); if (usedx) { incr = ((points[0][X] - (los[FROM][X] - gs->x_trans)) / u_d[X]); } else if (u_d[Y]) { incr = ((points[0][Y] - (los[FROM][Y] - gs->y_trans)) / u_d[Y]); } else { point[X] = los[FROM][X] - gs->x_trans; point[Y] = los[FROM][Y] - gs->y_trans; return (viewcell_tri_interp(gs, buf, point, 1)); } /* DEBUG fprintf(stderr,"-----------------------------\n"); fprintf(stderr,"%d points to check\n", num); fprintf(stderr,"incr1 = %.6lf: %.9f %.9f %.9f\n",incr,u_d[X],u_d[Y],u_d[Z]); fprintf(stderr, "\tpoint0 = %.6f %.6f %.6f\n\tFT = %.6f %.6f %.6f\n\tpoint%d = %.6f %.6f\n", points[0][X],points[0][Y],points[0][Z], los[FROM][X],los[FROM][Y],los[FROM][Z], num-1, points[num-1][X],points[num-1][Y]); */ /* This should bring us right above (or below) the first point */ a[X] = los[FROM][X] + incr * u_d[X] - gs->x_trans; a[Y] = los[FROM][Y] + incr * u_d[Y] - gs->y_trans; a[Z] = los[FROM][Z] + incr * u_d[Z] - gs->z_trans; if (a[Z] < points[0][Z]) { /* viewing from below surface */ /* don't use this method */ /* DEBUG fprintf(stderr,"first point below surf\n"); fprintf(stderr,"aZ= %.6f point0 = %.6f %.6f %.6f FT =%.6f %.6f %.6f\n", a[Z], points[0][X],points[0][Y],points[0][Z], los[FROM][X],los[FROM][Y],los[FROM][Z]); */ return (0); } GS_v3eq(a1, a); GS_v3eq(b, a); for (i = 1; i < num; i++) { if (usedx) { incr = ((points[i][X] - a1[X]) / u_d[X]); } else { incr = ((points[i][Y] - a1[Y]) / u_d[Y]); } a[X] = a1[X] + (incr * u_d[X]); a[Y] = a1[Y] + (incr * u_d[Y]); a[Z] = a1[Z] + (incr * u_d[Z]); above = (a[Z] >= points[i][Z]); if (above) { GS_v3eq(b, a); continue; } /* * Now we know b[Z] is above points[i-1] * and a[Z] is below points[i] * Since there should only be one polygon along this seg, * just interpolate to intersect */ if (usedx) { incr = ((a[X] - b[X]) / u_d[X]); } else { incr = ((a[Y] - b[Y]) / u_d[Y]); } if (1 == (ret = segs_intersect(1.0, points[i][Z], 0.0, points[i - 1][Z], 1.0, a[Z], 0.0, b[Z], &p1, &p2))) { point[X] = points[i - 1][X] + (u_d[X] * incr * p1); point[Y] = points[i - 1][Y] + (u_d[Y] * incr * p1); point[Z] = p2; return (1); } fprintf(stderr, "line of sight error %d\n", ret); return 0; } /* over surface */ return 0; } /***********************************************************************/ /* Ray-Convex Polyhedron Intersection Test * originally by Eric Haines, erich@eye.com * * This test checks the ray against each face of a polyhedron, checking whether * the set of intersection points found for each ray-plane intersection * overlaps the previous intersection results. If there is no overlap (i.e. * no line segment along the ray that is inside the polyhedron), then the * ray misses and returns 0; else 1 is returned if the ray is entering the * polyhedron, -1 if the ray originates inside the polyhedron. If there is * an intersection, the distance and the nunber of the face hit is returned. * * org, dir origin and direction of ray * tmax maximum useful distance along ray * phdrn[] list of planes in convex polyhedron * ph_num number of planes in convex polyhedron * *tresult returned: distance of intersection along ray * *pn returned: number of face hit (0 to ph_num-1) * */ int RayCvxPolyhedronInt(Point3 org, Point3 dir, double tmax, Point4 * phdrn, int ph_num, double *tresult, int *pn) { double tnear, tfar, t, vn, vd; int fnorm_num, bnorm_num; /* front/back face # hit */ tnear = -HUGE_VAL; tfar = tmax; /* Test each plane in polyhedron */ for (; ph_num--;) { /* Compute intersection point T and sidedness */ vd = DOT3(dir, phdrn[ph_num]); vn = DOT3(org, phdrn[ph_num]) + phdrn[ph_num][W]; if (vd == 0.0) { /* ray is parallel to plane - check if ray origin is inside plane's half-space */ if (vn > 0.0) { /* ray origin is outside half-space */ return (MISSED); } } else { /* ray not parallel - get distance to plane */ t = -vn / vd; if (vd < 0.0) { /* front face - T is a near point */ if (t > tfar) { return (MISSED); } if (t > tnear) { /* hit near face, update normal */ fnorm_num = ph_num; tnear = t; } } else { /* back face - T is a far point */ if (t < tnear) { return (MISSED); } if (t < tfar) { /* hit far face, update normal */ bnorm_num = ph_num; tfar = t; } } } } /* survived all tests */ /* Note: if ray originates on polyhedron, may want to change 0.0 to some * epsilon to avoid intersecting the originating face. */ if (tnear >= 0.0) { /* outside, hitting front face */ *tresult = tnear; *pn = fnorm_num; return (FRONTFACE); } else { if (tfar < tmax) { /* inside, hitting back face */ *tresult = tfar; *pn = bnorm_num; return (BACKFACE); } else { /* inside, but back face beyond tmax */ return (MISSED); } } } /***********************************************************************/ void gs_get_databounds_planes(Point4 * planes) { float n, s, w, e, b, t; Point3 tlfront, brback; GS_get_zrange(&b, &t, 0); gs_get_xrange(&w, &e); gs_get_yrange(&s, &n); tlfront[X] = tlfront[Y] = 0.0; tlfront[Z] = t; brback[X] = e - w; brback[Y] = n - s; brback[Z] = b; /* top */ planes[0][X] = planes[0][Y] = 0.0; planes[0][Z] = 1.0; planes[0][W] = -(DOT3(planes[0], tlfront)); /* bottom */ planes[1][X] = planes[1][Y] = 0.0; planes[1][Z] = -1.0; planes[1][W] = -(DOT3(planes[1], brback)); /* left */ planes[2][Y] = planes[2][Z] = 0.0; planes[2][X] = -1.0; planes[2][W] = -(DOT3(planes[2], tlfront)); /* right */ planes[3][Y] = planes[3][Z] = 0.0; planes[3][X] = 1.0; planes[3][W] = -(DOT3(planes[3], brback)); /* front */ planes[4][X] = planes[4][Z] = 0.0; planes[4][Y] = -1.0; planes[4][W] = -(DOT3(planes[4], tlfront)); /* back */ planes[5][X] = planes[5][Z] = 0.0; planes[5][Y] = 1.0; planes[5][W] = -(DOT3(planes[5], brback)); return; } /***********************************************************************/ /* Gets all current cutting planes & data bounding planes, intersects los with resulting convex polyhedron, then replaces los[FROM] with first point on ray inside data. */ int gs_setlos_enterdata(Point3 * los) { Point4 planes[12]; /* MAX_CPLANES + 6 - should define this */ Point3 dir; double dist, maxdist; int num, ret, retp; /* might want to tell if retp is a clipping plane */ gs_get_databounds_planes(planes); num = gsd_get_cplanes(planes + 6); GS_v3dir(los[FROM], los[TO], dir); maxdist = GS_distance(los[FROM], los[TO]); ret = RayCvxPolyhedronInt(los[0], dir, maxdist, planes, num + 6, &dist, &retp); if (ret == MISSED) { return (0); } if (ret == FRONTFACE) { GS_v3mult(dir, (float) dist); GS_v3add(los[FROM], dir); } return (1); } /***********************************************************************/ /* DEBUG **** void pr_plane(int pnum) { switch(pnum) { case 0: fprintf(stderr,"top plane"); break; case 1: fprintf(stderr,"bottom plane"); break; case 2: fprintf(stderr,"left plane"); break; case 3: fprintf(stderr,"right plane"); break; case 4: fprintf(stderr,"front plane"); break; case 5: fprintf(stderr,"back plane"); break; default: fprintf(stderr,"clipping plane %d", 6 - pnum); break; } return; } ********/