X-Git-Url: https://git.ozlabs.org/?p=ponghero.git;a=blobdiff_plain;f=prpong.c;h=cd5a5a66826eae3092d4adeef82dcc5ed9243c7b;hp=c62d0fcced8827bfc3881fe0e0ee2b03a17257ac;hb=ca2ca6fb976f96fb9a752d80bd2b6538e8571ed9;hpb=1774b5732984e261bf4fd68350051e7ff706c36c diff --git a/prpong.c b/prpong.c index c62d0fc..cd5a5a6 100644 --- a/prpong.c +++ b/prpong.c @@ -4,100 +4,96 @@ #include #include #include +#include +#include +#include #include "stdrusty.h" +#include "list/list.h" #include +#include +#include -#define MAX_X 512 -#define MAX_Y 512 +#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 */ - -/* return values */ -#define DONT_INTERSECT 0 -#define DO_INTERSECT 1 -#define PARALLEL 2 - -/* The SAME_SIGNS macro assumes arithmetic where the exclusive-or */ -/* operation will work on sign bits. This works for twos-complement,*/ -/* and most other machine arithmetic. */ -#define SAME_SIGNS( a, b ) \ - (((long) ((unsigned long) a ^ (unsigned long) b)) >= 0 ) - - -/* 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 int lines_intersect(long x1, long y1, long x2, long y2, long x3, long y3, - long x4,long y4, long *x, long *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; + struct coord lo, hi; - long Ax,Bx,Cx,Ay,By,Cy,d,e,f,num,offset; - short x1lo,x1hi,y1lo,y1hi; - - Ax = x2-x1; - Bx = x3-x4; + Ax = start2->x - start1->x; + Bx = start3->x - start4->x; - if(Ax<0) { /* X bound box test*/ - x1lo=(short)x2; x1hi=(short)x1; + if(Ax<0) { /* X bound box test*/ + lo.x = start2->x; hi.x = start1->x; } else { - x1hi=(short)x2; x1lo=(short)x1; + hi.x = start2->x; lo.x = start1->x; } if(Bx>0) { - if(x1hi < (short)x4 || (short)x3 < x1lo) return DONT_INTERSECT; + if(hi.x < start4->x || start3->x < lo.x) return false; } else { - if(x1hi < (short)x3 || (short)x4 < x1lo) return DONT_INTERSECT; + 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=(short)y2; y1hi=(short)y1; + if(Ay<0) { /* Y bound box test*/ + lo.y = start2->y; hi.y = start1->y; } else { - y1hi=(short)y2; y1lo=(short)y1; + hi.y = start2->y; lo.y = start1->y; } if(By>0) { - if(y1hi < (short)y4 || (short)y3 < y1lo) return DONT_INTERSECT; + if(hi.y < start4->y || start3->y < lo.y) return false; } else { - if(y1hi < (short)y3 || (short)y4 < y1lo) return DONT_INTERSECT; + 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 DONT_INTERSECT; + 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 DONT_INTERSECT; + 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 PARALLEL; - num = d*Ax; /* numerator */ - offset = SAME_SIGNS(num,f) ? f/2 : -f/2; /* round direction*/ - *x = x1 + (num+offset) / f; /* intersection x */ + num = d*Ax; /* numerator */ + intersect->x = start1->x + num / f; /* intersection x */ num = d*Ay; - offset = SAME_SIGNS(num,f) ? f/2 : -f/2; - *y = y1 + (num+offset) / f; /* intersection y */ + intersect->y = start1->y + num / f; /* intersection y */ - return DO_INTERSECT; + return true; } - + // A set of very useful macros that you will find in most // code that I write whether I use them in a program or // not. @@ -478,13 +474,6 @@ static void line(SDL_Surface *s, } } -#if 1 -struct coord -{ - long x; - long y; -}; - struct ball { struct coord pos; @@ -494,12 +483,18 @@ struct ball struct line_segment { struct coord start, end; + struct list_node list; + bool ignore; + struct timeval expires; }; -static void thick_line(SDL_Surface *s, const struct line_segment *l) +static void thick_line(SDL_Surface *s, const struct line_segment *l, + unsigned color) { struct line_segment adj; - int x, y; + int x, y, minx = MAX_X, miny = MAX_Y, maxx = 0, maxy = 0; + + SDL_LockSurface(s); /* Cheap hack */ for (x = -1; x < 2; x++) { @@ -509,24 +504,49 @@ static void thick_line(SDL_Surface *s, const struct line_segment *l) adj.start.y = l->start.y + y; adj.end.y = l->end.y + y; + if (adj.start.x < minx) + minx = adj.start.x; + if (adj.start.x > maxx) + maxx = adj.start.x; + if (adj.start.y < miny) + miny = adj.start.y; + if (adj.start.y > maxy) + maxy = adj.start.y; + if (adj.end.x < minx) + minx = adj.end.x; + if (adj.end.x > maxx) + maxx = adj.end.x; + if (adj.end.y < miny) + miny = adj.end.y; + if (adj.end.y > maxy) + maxy = adj.end.y; + line(s, adj.start.x, adj.start.y, adj.end.x, adj.end.y, - 0); + color); } } + + if (minx < 0) + minx = 0; + if (miny < 0) + miny = 0; + if (maxx >= MAX_X) + maxx = MAX_X-1; + if (maxy >= MAX_Y) + maxy = MAX_Y-1; + SDL_UpdateRect(s, minx, miny, maxx-minx, maxy-miny); + SDL_UnlockSurface(s); } 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) == DO_INTERSECT; + return lines_intersect(c1, c2, &seg->start, &seg->end, intersect); } static double dist(const struct coord *c1, const struct coord *c2) { - long 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); } @@ -536,23 +556,25 @@ 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. */ ball->pos = isect; + printf("ball is now at %f,%f\n", ball->pos.x, ball->pos.y); /* Outgoing angle = 2 * line angle - incident angle. */ lang = atan2(line->end.x - line->start.x, line->end.y - line->start.y); @@ -563,15 +585,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); - ball->move.x = round(sin(oang) * speed); - ball->move.y = round(cos(oang) * speed); - - /* Set remainder. */ - remainder->x = round(sin(oang) * len); - remainder->y = round(cos(oang) * len); - printf("len = %f, remainder = %li,%li\n", len, remainder->x, remainder->y); + speed += increase_speed; + ball->move.x = sin(oang) * speed; + ball->move.y = cos(oang) * speed; + + /* 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[] = { @@ -617,35 +640,47 @@ static SDL_Surface *ball_surface(SDL_Surface *screen) return ball; } +static void clear_ignore(struct list_head *lines) +{ + struct line_segment *i; + printf("Unignoring...\n"); + list_for_each(lines, i, list) + i->ignore = false; +} + int main(int argc, char *argv[]) { struct ball ball; - struct line_segment lines[ARRAY_SIZE(border) + 1]; - static int isect_count = 0; SDL_Surface *screen, *ballsur, *savesur = NULL; Uint8 video_bpp; Uint32 videoflags; - int i; - SDL_Event event; - struct line_segment *last_l = NULL; + unsigned int i, time_since_last_ir = INT_MAX; SDL_Rect rect; + struct timeval line_life = { 3, 0 }; + cwiid_wiimote_t *wiimote; + struct cwiid_state state; + bdaddr_t addr = *BDADDR_ANY; + LIST_HEAD(lines); + struct cwiid_ir_src *ir, last_ir = { .valid = 0 }; + + videoflags = SDL_SWSURFACE; - if (argc != 9) - errx(1, "Usage: %s ballx bally ballangle ballspeed linestartx linestarty lineendx linendy\n" + if (argv[1] && streq(argv[1], "--fullscreen")) { + videoflags = SDL_SWSURFACE; + argc--; + argv++; + } + + if (argc != 5) + errx(1, "Usage: %s [--fullscreen] ballx bally ballangle ballspeed\n" " Where ballangle is 0 for north, 90 for east, etc\n", argv[0]); ball.pos.x = atol(argv[1]); ball.pos.y = atol(argv[2]); - ball.move.x = roundf(sin(deg_to_rad(atof(argv[3]))) * atol(argv[4])); - ball.move.y = roundf(cos(deg_to_rad(atof(argv[3]))) * atol(argv[4])); - printf("ball move = %li,%li\n", ball.move.x, ball.move.y); - - memcpy(lines, border, sizeof(border)); - lines[ARRAY_SIZE(border)].start.x = atof(argv[5]); - lines[ARRAY_SIZE(border)].start.y = atof(argv[6]); - lines[ARRAY_SIZE(border)].end.x = atof(argv[7]); - lines[ARRAY_SIZE(border)].end.y = atof(argv[8]); + ball.move.x = sin(deg_to_rad(atof(argv[3]))) * atol(argv[4]); + ball.move.y = cos(deg_to_rad(atof(argv[3]))) * atol(argv[4]); + printf("ball move = %f,%f\n", ball.move.x, ball.move.y); /* Initialize SDL */ if ( SDL_Init(SDL_INIT_VIDEO) < 0 ) { @@ -654,9 +689,15 @@ int main(int argc, char *argv[]) } video_bpp = 0; - videoflags = SDL_SWSURFACE; - /* Set 640x480 video mode */ + 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()); @@ -670,28 +711,40 @@ int main(int argc, char *argv[]) /* White. */ memset(screen->pixels, 0xFF, screen->pitch * screen->h); - /* Draw lines. */ - for (i = 0; i < ARRAY_SIZE(lines); i++) - thick_line(screen, &lines[i]); - SDL_UnlockSurface(screen); SDL_UpdateRect(screen, 0, 0, 0, 0); + /* Draw borders, put them in list. */ + for (i = 0; i < ARRAY_SIZE(border); i++) { + border[i].ignore = false; + border[i].expires.tv_sec = LONG_MAX; + border[i].expires.tv_usec = 0; + list_add(&lines, &border[i].list); + thick_line(screen, &border[i], 0); + } + ballsur = ball_surface(screen); - do { + for (;;) { struct coord new, isect, move = ball.move; - struct line_segment *best_l; - unsigned i; + struct line_segment *best_l, *l, *next; double best_d; -#if 0 - SDL_Rect rect; - - rect.x = ball.pos.x - ballsur->w/2; - rect.y = ball.pos.y - ballsur->h/2; - rect.w = ballsur->w; - rect.h = ballsur->h; -#endif + 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); + free(l); + } + } again: best_d = MAX_X + MAX_Y; @@ -699,32 +752,41 @@ int main(int argc, char *argv[]) new.x = ball.pos.x + move.x; new.y = ball.pos.y + move.y; - printf("Ball at %li,%li (%li,%li)\n", ball.pos.x, ball.pos.y, - move.x, move.y); - - for (i = 0; i < ARRAY_SIZE(lines); i++) { - if (last_l == &lines[i]) - continue; - if (intersect(&ball.pos, &new, &lines[i], &isect)) { + list_for_each(&lines, l, list) { + if (!l->ignore && intersect(&ball.pos, &new, l, &isect)) { double d; d = dist(&ball.pos, &isect); if (d < best_d) { - best_l = &lines[i]; + best_l = l; best_d = d; } } } + if (best_l) { - printf("Ball bouncing off (%lu,%lu %lu,%lu)!\n", + 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 (ignored && (move.x > 0.001 || move.y > 0.001)) + clear_ignore(&lines); + /* Don't hit the same line twice. */ - last_l = best_l; - isect_count++; + printf("Ignoring that line\n"); + best_l->ignore = ignored = true; goto again; } + 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); + } + /* Restore what was there before ball. */ if (savesur) { SDL_BlitSurface(savesur, NULL, screen, &rect); @@ -747,9 +809,45 @@ int main(int argc, char *argv[]) rect.x = new.x - ballsur->w/2; rect.y = new.y - ballsur->h/2; ball.pos = new; - SDL_Delay(5); - } while (!SDL_PollEvent(&event) || event.type != SDL_QUIT); - SDL_Quit(); - return 0; + SDL_Delay(25); + + if (SDL_PollEvent(&event) + && (event.type == SDL_QUIT + || (event.type == SDL_KEYDOWN + && event.key.keysym.sym == SDLK_ESCAPE))) { + SDL_Quit(); + return 0; + } + + 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 <= 3) { + struct line_segment *n = malloc(sizeof(*n)); + /* Wiimote coordinates are backwards for us. */ + n->start.x = MAX_X - last_ir.pos[0]; + n->start.y = MAX_Y - last_ir.pos[1]; + n->end.x = MAX_X - ir->pos[0]; + n->end.y = MAX_Y - ir->pos[1]; + n->ignore = false; + 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