X-Git-Url: https://git.ozlabs.org/?a=blobdiff_plain;f=prpong.c;h=7799f63fdccf90d068ab2f90d34f858163cfdc26;hb=d1dd6d7b08f5ac95e6e0806abe93e51701044e23;hp=09d5113cf191efce46edfae000300ebe14a708ca;hpb=799de455ce16b8b7368a67de4ce1c9213347e866;p=ponghero.git diff --git a/prpong.c b/prpong.c index 09d5113..7799f63 100644 --- a/prpong.c +++ b/prpong.c @@ -11,83 +11,86 @@ #include "list/list.h" #include #include +#include +#include #define MAX_X CWIID_IR_X_MAX #define MAX_Y CWIID_IR_Y_MAX +struct coord +{ + double x; + double y; +}; + /* Obtained from http://tog.acm.org/GraphicsGems/gemsiii/insectc.c */ /* Faster Line Segment Intersection */ /* Franklin Antonio */ - -/* The use of some short working variables allows this code to run */ -/* faster on 16-bit computers, but is not essential. It should not */ -/* affect operation on 32-bit computers. The short working variables*/ -/* to not restrict the range of valid input values, as these were */ -/* constrained in any case, due to algorithm restrictions. */ -static bool lines_intersect(double x1, double y1, - double x2, double y2, - double x3, double y3, - double x4, double y4, - double *x, double *y) +static bool lines_intersect(const struct coord *start1, + const struct coord *start2, + const struct coord *start3, + const struct coord *start4, + struct coord *intersect) { - double Ax,Bx,Cx,Ay,By,Cy,d,e,f,num; - double x1lo,x1hi,y1lo,y1hi; + struct coord lo, hi; - Ax = x2-x1; - Bx = x3-x4; + Ax = start2->x - start1->x; + Bx = start3->x - start4->x; - if(Ax<0) { /* X bound box test*/ - x1lo=x2; x1hi=x1; + if(Ax<0) { /* X bound box test*/ + lo.x = start2->x; hi.x = start1->x; } else { - x1hi=x2; x1lo=x1; + hi.x = start2->x; lo.x = start1->x; } if(Bx>0) { - if(x1hi < x4 || x3 < x1lo) return false; + if(hi.x < start4->x || start3->x < lo.x) return false; } else { - if(x1hi < x3 || x4 < x1lo) return false; + if(hi.x < start3->x || start4->x < lo.x) return false; } - Ay = y2-y1; - By = y3-y4; + Ay = start2->y - start1->y; + By = start3->y - start4->y; - if(Ay<0) { /* Y bound box test*/ - y1lo=y2; y1hi=y1; + if(Ay<0) { /* Y bound box test*/ + lo.y = start2->y; hi.y = start1->y; } else { - y1hi=y2; y1lo=y1; + hi.y = start2->y; lo.y = start1->y; } if(By>0) { - if(y1hi < y4 || y3 < y1lo) return false; + if(hi.y < start4->y || start3->y < lo.y) return false; } else { - if(y1hi < y3 || y4 < y1lo) return false; + if(hi.y < start3->y || start4->y < lo.y) return false; } - Cx = x1-x3; - Cy = y1-y3; - d = By*Cx - Bx*Cy; /* alpha numerator*/ - f = Ay*Bx - Ax*By; /* both denominator*/ - if(f>0) { /* alpha tests*/ - if(d<0 || d>f) return false; + Cx = start1->x - start3->x; + Cy = start1->y - start3->y; + d = By*Cx - Bx*Cy; /* alpha numerator*/ + f = Ay*Bx - Ax*By; /* both denominator*/ + if (f>0) { /* alpha tests*/ + if (d < 0 || d > f) return false; } else { - if(d>0 || d 0 || d < f) return false; } - e = Ax*Cy - Ay*Cx; /* beta numerator*/ - if(f>0) { /* beta tests*/ - if(e<0 || e>f) return false; + e = Ax*Cy - Ay*Cx; /* beta numerator*/ + if (f > 0) { /* beta tests*/ + if (e < 0 || e > f) return false; } else { - if(e>0 || e 0 || e < f) return false; } /*compute intersection coordinates*/ + /* Don't worry about tiny values of f (< 1/2 pixel across screen) */ + if (fabs(f) < 1.0 / (2.0 * MAX_X * MAX_Y)) + return false; - if(f==0) return false; - num = d*Ax; /* numerator */ - *x = x1 + num / f; /* intersection x */ + num = d*Ax; /* numerator */ + intersect->x = start1->x + num / f; /* intersection x */ num = d*Ay; - *y = y1 + num / f; /* intersection y */ + intersect->y = start1->y + num / f; /* intersection y */ return true; } @@ -472,13 +475,6 @@ static void line(SDL_Surface *s, } } -#if 1 -struct coord -{ - double x; - double y; -}; - struct ball { struct coord pos; @@ -546,15 +542,12 @@ static void thick_line(SDL_Surface *s, const struct line_segment *l, static bool intersect(const struct coord *c1, const struct coord *c2, const struct line_segment *seg, struct coord *intersect) { - return lines_intersect(c1->x, c1->y, c2->x, c2->y, - seg->start.x, seg->start.y, - seg->end.x, seg->end.y, - &intersect->x, &intersect->y); + return lines_intersect(c1, c2, &seg->start, &seg->end, intersect); } static double dist(const struct coord *c1, const struct coord *c2) { - float x = (c1->x - c2->x), y = (c1->y - c2->y); + double x = (c1->x - c2->x), y = (c1->y - c2->y); return sqrt(x * x + y * y); } @@ -564,19 +557,20 @@ static unsigned rad_to_deg(double rad) } static void bounce(struct ball *ball, const struct line_segment *line, - struct coord *remainder) + struct coord *move, double increase_speed) { double len, speed, lang, iang, oang; struct coord isect, new; - new.x = ball->pos.x + ball->move.x; - new.y = ball->pos.y + ball->move.y; + new.x = ball->pos.x + move->x; + new.y = ball->pos.y + move->y; /* How far were we supposed to move ball? */ - len = sqrt(remainder->x * remainder->x + remainder->y * remainder->y); + len = sqrt(move->x * move->x + move->y * move->y); /* How far is it to intersection? */ - intersect(&ball->pos, &new, line, &isect); + if (!intersect(&ball->pos, &new, line, &isect)) + errx(1, "No intersection any more?\n");; len -= dist(&ball->pos, &isect); /* Move ball to intersection. */ @@ -592,16 +586,16 @@ static void bounce(struct ball *ball, const struct line_segment *line, rad_to_deg(iang), rad_to_deg(oang)); - /* Set new direction for ball, at same speed */ + /* Set new direction for ball, at slightly increased speed */ speed = sqrt(ball->move.x * ball->move.x + ball->move.y * ball->move.y); - speed *= 1.01; + speed += increase_speed; ball->move.x = sin(oang) * speed; ball->move.y = cos(oang) * speed; - /* Set remainder. */ - remainder->x = sin(oang) * len; - remainder->y = cos(oang) * len; - printf("len = %f, remainder = %f,%f\n", len, remainder->x, remainder->y); + /* Set move. */ + move->x = sin(oang) * len; + move->y = cos(oang) * len; + printf("len = %f, move = %f,%f\n", len, move->x, move->y); } static struct line_segment border[] = { @@ -655,20 +649,176 @@ static void clear_ignore(struct list_head *lines) i->ignore = false; } +static uint16_t *find_valid_point(struct cwiid_state *state) +{ + unsigned int i; + + for (i = 0; i < CWIID_IR_SRC_COUNT; i++) { + if (state->ir_src[i].valid) + return state->ir_src[i].pos; + } + return NULL; +} + +static void calibrate(SDL_Surface *screen, + cwiid_wiimote_t *wiimote, + struct coord *calib, unsigned x, unsigned y) +{ + uint16_t *pos; + struct cwiid_state state; + unsigned int last_x = MAX_X, last_y = MAX_Y, count = 0; + + /* Must see it for a full half second. */ + while (count < 20) { + SDL_Event event; + + if (cwiid_get_state(wiimote, &state)) + errx(1, "No wii state"); + + pos = find_valid_point(&state); + if (!pos || pos[0] != last_x || pos[1] != last_y) + count = 0; + + if (pos) { + last_x = pos[0]; + last_y = pos[1]; + count++; + } + + SDL_Delay(25); + if (SDL_PollEvent(&event) + && (event.type == SDL_QUIT + || (event.type == SDL_KEYDOWN + && event.key.keysym.sym == SDLK_ESCAPE))) { + SDL_Quit(); + exit(0); + } + } + + calib->x = last_x; + calib->y = last_y; + printf("Calibration point: %u,%u\n", last_x, last_y); +} + +static bool map_coord(unsigned int x, unsigned int y, + const struct coord calib[], + struct coord *res) +{ + struct coord line_start, line_end; + struct coord pos = { x, y }, intersect; + double d1, d2; + + /* Calibration layout: + * + * 0 1 + * + * 3 2 + * + * We figure out the distance to each side, and then map using: + * + * x = d1 / (d1 + d2) * (rawend - rawstart) + rawstart + */ + line_start.x = 0; + line_end.x = MAX_X-1; + line_start.y = line_end.y = y; + + if (!lines_intersect(&calib[0], &calib[3], &line_start, &line_end, + &intersect)) + return false; + d1 = dist(&pos, &intersect); + if (!lines_intersect(&calib[1], &calib[2], &line_start, &line_end, + &intersect)) + return false; + d2 = dist(&pos, &intersect); + res->x = d1 / (d1 + d2) * MAX_X; + + line_start.y = 0; + line_end.y = MAX_Y-1; + line_start.x = line_end.x = x; + + if (!lines_intersect(&calib[0], &calib[1], &line_start, &line_end, + &intersect)) + return false; + d1 = dist(&pos, &intersect); + if (!lines_intersect(&calib[3], &calib[2], &line_start, &line_end, + &intersect)) + return false; + d2 = dist(&pos, &intersect); + res->y = d1 / (d1 + d2) * MAX_Y; + + return true; +} + +static struct line_segment *new_line(unsigned int startx, unsigned int starty, + unsigned int endx, unsigned int endy, + const struct coord calib[]) +{ + struct line_segment *l = malloc(sizeof(*l)); + + l->ignore = false; + if (!map_coord(startx, starty, calib, &l->start) + || !map_coord(endx, endy, calib, &l->end)) { + free(l); + return NULL; + } + return l; +} + + int main(int argc, char *argv[]) { struct ball ball; SDL_Surface *screen, *ballsur, *savesur = NULL; Uint8 video_bpp; Uint32 videoflags; - unsigned int i; + unsigned int i, time_since_last_ir = INT_MAX; SDL_Rect rect; - bool creating_lines = false; - struct timeval line_life = { 5, 0 }; + struct timeval line_life = { 3, 0 }; + cwiid_wiimote_t *wiimote; + struct cwiid_state state; + struct coord calib[4]; + bdaddr_t addr = *BDADDR_ANY; + bool mouse = false, needs_calibration = true, drawing = false; LIST_HEAD(lines); + struct cwiid_ir_src *ir, last_ir = { .valid = 0 }; + struct line_segment *n; + + videoflags = SDL_SWSURFACE; + + if (argv[1] && streq(argv[1], "--fullscreen")) { + videoflags |= SDL_FULLSCREEN; + argc--; + argv++; + } + + if (argv[1] && strstarts(argv[1], "--calib=")) { + needs_calibration = false; + if (sscanf(argv[1], "--calib=%lf,%lf,%lf,%lf,%lf,%lf,%lf,%lf", + &calib[0].x, &calib[0].y, + &calib[1].x, &calib[1].y, + &calib[2].x, &calib[2].y, + &calib[3].x, &calib[3].y) != 8) + errx(1, "Not enough calibration points"); + argc--; + argv++; + } + + if (argv[1] && streq(argv[1], "--mouse")) { + mouse = true; + needs_calibration = false; + calib[0].x = calib[0].y = 0; + calib[1].x = MAX_X - 1; + calib[1].y = 0; + calib[2].x = MAX_X - 1; + calib[2].y = MAX_Y - 1; + calib[3].x = 0; + calib[3].y = MAX_Y - 1; + argc--; + argv++; + } if (argc != 5) - errx(1, "Usage: %s ballx bally ballangle ballspeed\n" + errx(1, "Usage: %s [--fullscreen] [--calib=x,y,x,y,x,y,x,y] [--mouse] ballx bally ballangle ballspeed\n" " Where ballangle is 0 for north, 90 for east, etc\n", argv[0]); @@ -679,15 +829,23 @@ int main(int argc, char *argv[]) printf("ball move = %f,%f\n", ball.move.x, ball.move.y); /* Initialize SDL */ - if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) { + if (SDL_Init(SDL_INIT_VIDEO) < 0) { fprintf(stderr, "Couldn't initialize SDL: %s\n",SDL_GetError()); return(1); } video_bpp = 0; - videoflags = SDL_SWSURFACE; - /* Set 640x480 video mode */ + if (!mouse) { + printf("Put Wiimote in discoverable mode (press 1+2)\n"); + wiimote = cwiid_open(&addr, 0); + if (!wiimote) + errx(1, "Can't find the Wiimote"); + + if (cwiid_set_rpt_mode(wiimote, CWIID_RPT_IR)) + errx(1, "Can't set IR repeat mode"); + } + if ( (screen=SDL_SetVideoMode(MAX_X,MAX_Y,video_bpp,videoflags)) == NULL ) { errx(1, "Couldn't set %dx%dx%d video mode: %s", MAX_X, MAX_Y,video_bpp, SDL_GetError()); @@ -704,6 +862,14 @@ int main(int argc, char *argv[]) SDL_UnlockSurface(screen); SDL_UpdateRect(screen, 0, 0, 0, 0); + if (needs_calibration) { + /* Calibration */ + calibrate(screen, wiimote, &calib[0], 0, 0); + calibrate(screen, wiimote, &calib[1], MAX_X - 1, 0); + calibrate(screen, wiimote, &calib[2], MAX_X - 1, MAX_Y - 1); + calibrate(screen, wiimote, &calib[3], 0, MAX_Y - 1); + } + /* Draw borders, put them in list. */ for (i = 0; i < ARRAY_SIZE(border); i++) { border[i].ignore = false; @@ -721,12 +887,14 @@ int main(int argc, char *argv[]) double best_d; SDL_Event event; struct timeval now; + bool ignored = false; gettimeofday(&now, NULL); /* Expire lines */ list_for_each_safe(&lines, l, next, list) { if (timercmp(&l->expires, &now, <)) { + printf("Deleting line %p\n", l); list_del(&l->list); /* FIXME: Undraw properly. */ thick_line(screen, l, 0xFFFFFFFF); @@ -755,21 +923,24 @@ int main(int argc, char *argv[]) printf("Ball bouncing off (%f,%f %f,%f)!\n", best_l->start.x, best_l->start.y, best_l->end.x, best_l->end.y); - bounce(&ball, best_l, &move); + /* Only increase speed on *first* bounce, to avoid + * mega speed increases. */ + bounce(&ball, best_l, &move, ignored ? 0.0 : 0.1); /* If we moved, stop ignoring lines. */ - if (move.x > 0.001 || move.y > 0.001) + if (ignored && (move.x > 0.001 || move.y > 0.001)) clear_ignore(&lines); /* Don't hit the same line twice. */ printf("Ignoring that line\n"); - best_l->ignore = true; + best_l->ignore = ignored = true; goto again; } - if (move.x > 0.001 || move.y > 0.001) { + if (ignored && (move.x > 0.001 || move.y > 0.001)) { clear_ignore(&lines); - printf("Moving by %f,%f to %f,%f\n", move.x, move.y, new.x, new.y); + printf("Moving by %f,%f to %f,%f\n", + move.x, move.y, new.x, new.y); } /* Restore what was there before ball. */ @@ -797,27 +968,74 @@ int main(int argc, char *argv[]) SDL_Delay(25); while (SDL_PollEvent(&event)) { - if (event.type == SDL_QUIT) { + if (event.type == SDL_QUIT + || (event.type == SDL_KEYDOWN + && event.key.keysym.sym == SDLK_ESCAPE)) { SDL_Quit(); return 0; } - if (event.type == SDL_MOUSEBUTTONDOWN) - creating_lines = true; - else if (event.type == SDL_MOUSEBUTTONUP) - creating_lines = false; - - if (creating_lines && event.type == SDL_MOUSEMOTION) { - struct line_segment *n = malloc(sizeof(*n)); - n->start.x = event.motion.x - event.motion.xrel; - n->start.y = event.motion.y - event.motion.yrel; - n->end.x = event.motion.x; - n->end.y = event.motion.y; - n->ignore = false; - timeradd(&now, &line_life, &n->expires); - list_add_tail(&lines, &n->list); - thick_line(screen, n, 0); + if (mouse) { + switch (event.type) { + case SDL_MOUSEBUTTONDOWN: + drawing = true; + break; + case SDL_MOUSEBUTTONUP: + drawing = false; + break; + case SDL_MOUSEMOTION: + if (!drawing) + break; + + n = new_line(event.motion.x, + event.motion.y, + event.motion.x + + event.motion.xrel, + event.motion.y + + event.motion.yrel, + calib); + + if (n) { + timeradd(&now, &line_life, + &n->expires); + list_add_tail(&lines, &n->list); + thick_line(screen, n, 0); + } + } } } + + if (mouse) + continue; + + if (cwiid_get_state(wiimote, &state)) + errx(1, "No wii state"); + + /* Find biggest state. */ + ir = &state.ir_src[0]; + for (i = 0; i < CWIID_IR_SRC_COUNT; i++) { + if (!state.ir_src[i].valid) + continue; + if (!ir->valid || state.ir_src[i].size > ir->size) + ir = &state.ir_src[0]; + } + + if (ir->valid) { + /* Give it some slack for missing one or two... */ + if (time_since_last_ir <= 5) { + n = new_line(last_ir.pos[0], + last_ir.pos[1], + ir->pos[0], + ir->pos[1], + calib); + if (n) { + timeradd(&now, &line_life, &n->expires); + list_add_tail(&lines, &n->list); + thick_line(screen, n, 0); + } + } + time_since_last_ir = 0; + last_ir = *ir; + } + time_since_last_ir++; } } -#endif