#include #include #include #include #include #include #include #include // min_c() and max_c() #ifdef DEBUGGING #define PRINT(x) printf x #else #define PRINT(x) #endif // macros #define set_pixel(x,y,color) (frame_buffer[x + (line_length*y)] = color) #define get_pixel(x,y) (frame_buffer[x + (line_length*y)]) class NApplication : public BApplication { public: NApplication(); bool is_quitting; // So that the WindowScreen knows what // to do when disconnected. private: bool QuitRequested(); void ReadyToRun(); }; class NWindowScreen : public BWindowScreen { public: NWindowScreen(status_t*); private: void ScreenConnected(bool); long MyCode(); static long Entry(void*); // handy stuff void set_frame_rate(float fps) {frame_pause = (bigtime_t)((1000 * 1000)/fps);} // special for demos enum { // used for star->last_draw INVALID = 0x7fffffff }; typedef struct { float init_velocity; float gravity; double cos_z_theta; int32 y; int32 x; int32 timeval; uint32 last_draw; int32 lx,ly; } particle; uint32 particle_count; particle *particle_list; // simple raster functions void draw_line(int x1, int y1, int x2, int y2, int color); void draw_rect(int x, int y, int w, int h, int color); void fill_rect(int x, int y, int w, int h, int color); void draw_ellipse(int cx, int cy, int wide, int deep, int color); void fill_ellipse(int x, int y, int xradius, int yradius, int color); void ellipse_points(int x, int y, int x_offset, int y_offset, int color); void ellipse_fill_points(int x, int y, int x_offset, int y_offset, int color); thread_id tid; sem_id sem; area_id area; uint8* save_buffer; uint8* frame_buffer; ulong line_length; bigtime_t frame_pause; // time between frames int width,height; int COLORS; bool thread_is_locked; // small hack to allow to quit the // app from ScreenConnected() }; main() { NApplication app; } NApplication::NApplication() :BApplication("application/x-vnd.Prok-DemoTemplate") { Run(); // see you in ReadyToRun() } void NApplication::ReadyToRun() { PRINT(("ReadyToRun()\n")); status_t ret = B_ERROR; is_quitting = false; NWindowScreen *ws = new NWindowScreen(&ret); PRINT(("WindowScreen ctor returned. ret = %s\n", strerror(ret))); // exit if constructing the WindowScreen failed. if((ws == NULL) || (ret < B_OK)) { //printf("the window screen was NULL, or there was an error\n"); PostMessage(B_QUIT_REQUESTED); } else PRINT(("everything's just peachy. done with ReadyToRun().\n")); } bool NApplication::QuitRequested() { PRINT(("QuitRequested()\n")); status_t ret; is_quitting = true; wait_for_thread(find_thread("rendering thread"), &ret); // wait for the render thread to finish return true; } NWindowScreen::NWindowScreen(status_t *ret) : BWindowScreen("Example", B_8_BIT_640x480, ret), width(640), height(480), COLORS(256) { PRINT(("WindowScreen ctor.\n")); thread_is_locked = true; tid = 0; if(*ret == B_OK) { PRINT(("creating blocking sem and save_buffer area.\n")); // this semaphore controls the access to the WindowScreen sem = create_sem(0,"WindowScreen Access"); // this area is used to save the whole framebuffer when // switching workspaces. (better than malloc()). area = create_area("save", (void**)&save_buffer, B_ANY_ADDRESS, 640*480, B_NO_LOCK, B_READ_AREA|B_WRITE_AREA); // exit if an error occured. if((sem < B_OK) || (area < B_OK)) { PRINT(("create_area() or create_sem() failed\n")); *ret = B_ERROR; } else { PRINT(("calling Show().\n")); Show(); // let's go. See you in ScreenConnected. } } else { PRINT(("BWindowScreen base class ctor returned failure\n")); be_app->PostMessage(B_QUIT_REQUESTED); } // set the frame rate set_frame_rate(30.); } void NWindowScreen::ScreenConnected(bool connected) { PRINT(("ScreenConnected()\n")); fflush(stdout); if(connected) { if(SetSpace(B_8_BIT_640x480) < B_OK) { SetFrameBuffer(640,480); PRINT(("SetSpace() failed\n")); // properly set the framebuffer. exit if an error occurs. be_app->PostMessage(B_QUIT_REQUESTED); return; } // get the framebuffer-related info, each time the // WindowScreen is connected (multiple monitor) frame_buffer = (uint8*)(CardInfo()->frame_buffer); line_length = FrameBufferInfo()->bytes_per_row; if(tid == 0) { // clean the framebuffer PRINT(("zeroing the framebuffer\n")); memset(frame_buffer,0,480*line_length); // spawn the rendering thread. exit if an error occurs. PRINT(("spawning the render thread.\n")); tid = spawn_thread(Entry,"rendering thread", B_URGENT_DISPLAY_PRIORITY,this); if(resume_thread(tid) < B_OK) { be_app->PostMessage(B_QUIT_REQUESTED); return; } } else { for(int y=0;y<480;y++) { // restore the framebuffer when switching back from // another workspace. memcpy(frame_buffer+y*line_length,save_buffer+640*y,640); } } // set our color list. rgb_color palette[256]; rgb_color c1; for(int i=0,j=0;i<256;i++,j++) { if(i<64) { c1.red = j*4; // greys c1.green = j*4; c1.blue = j*4; c1.alpha = 255; } if((i>=64) && (i<128)) { c1.red = j*4; // reds c1.green = 0; c1.blue = 0; c1.alpha = 255; } if((i>=128) && (i<192)) { c1.red = 0; // greens c1.green = j*4; c1.blue = 0; c1.alpha = 255; } if((i>=192) && (i<256)) { c1.red = 0i; // blues c1.green = 0; c1.blue = j*4; c1.alpha = 255; } if(j == 64) j=0; palette[i]=c1; } SetColorList(palette); // allow the rendering thread to run. thread_is_locked = false; release_sem(sem); } else /* !connected */ { // block the rendering thread. if(!thread_is_locked) { acquire_sem(sem); thread_is_locked = true; } // kill the rendering and clean up when quitting if((((NApplication*)be_app)->is_quitting)) { status_t ret; kill_thread(tid); wait_for_thread(tid,&ret); delete_sem(sem); delete_area(area); free(particle_list); } else { // set the color list black so that the screen doesn't seem // to freeze while saving the framebuffer rgb_color c={0,0,0,255}; rgb_color palette[256]; // build the palette for(int i=0;i<256;i++) palette[i] = c; // set the palette SetColorList(palette); // save the framebuffer for(int y=0;y<480;y++) memcpy(save_buffer+640*y,frame_buffer+y*line_length,640); } } } long NWindowScreen::Entry(void* p) { return ((NWindowScreen*)p)->MyCode(); } long NWindowScreen::MyCode() { bigtime_t trgt = system_time() + frame_pause; srandom(system_time()); // beforehand stuff particle_count = 1024*2; particle_list = (particle *)malloc(sizeof(particle)*particle_count); for (uint32 i=0; i>1)+(random()%(rand_max>>1)))/rand_max)*3.333; // magic number particle_list[i].gravity = -(((double)((rand_max>>1)+(random()%(rand_max>>1)))/rand_max))*0.599; // more magic // make the particle initialy invisible and fixed, but at a random moment in time particle_list[i].lx = 0; particle_list[i].ly = 0; particle_list[i].last_draw = INVALID; particle_list[i].timeval = random() & 64; particle_list[i].x = 0; // this gets figured out at drawtime particle_list[i].y = 0; // same here particle_list[i].cos_z_theta = cos(random() % 360); // grab an angle } // the loop o' fun while(!(((NApplication*)be_app)->is_quitting)) { // try to sync with the vertical retrace if(BScreen(this).WaitForRetrace() != B_OK) { // snoze for a bit so that other threads can be happy. // We are realtime priority you know if(system_time() < trgt) snooze(trgt - system_time()); trgt = system_time() + frame_pause; } // gain access to the framebuffer before writing to it. acquire_sem(sem); // block until we're allowed to own the framebuffer /////////////////////////////// // do neat stuff here // ////////////////////////////// PRINT(("rendering a frame.\n")); // eye candy VII - particles! - my own cookin particle *s; int32 x, y, cx,cy; set_frame_rate(60.); // woo. ntsc // calculate the center cx = width/2; cy = height/2; // palette test //set_frame_rate(0.1); //for(int i=0;i<256;i++) // draw_line(i,0,i,height, i); PRINT(("Starting particle drawing loop\n")); s = particle_list; for (uint32 i=0; ilx = s->x; s->ly = s->y; PRINT(("cx=%d, cy=%d\n", cx,cy)); // move the particle // find y and x // (s->gravity/2)*(s->timeval*s->timeval) * 1.85 is magic y = s->y = (int32)(cy + (int32)((s->gravity/2)*(s->timeval*s->timeval)*1.94) + ((s->init_velocity - (s->gravity*s->timeval)) * s->timeval)); x = s->x = (int32)(cx + (int32)(s->timeval * s->cos_z_theta)); // 3d rotation // interate timeval s->timeval++; // sanity check if(x <= 0) goto erase_and_reset; if(x > width) goto erase_and_reset; if(y < 0) goto erase; // invisible + erase last position if(y > height) goto erase_and_reset; // erase the previous position, if necessary if (s->last_draw != INVALID) set_pixel(s->lx,s->ly,0); // if it's visible, then draw it. set_pixel(s->x,s->y, 169); s->last_draw = 1; goto loop; erase_and_reset: if((s->lx <= width) && (s->lx >= 0) && (s->ly <= height) && (s->ly >= 0)) set_pixel(s->lx, s->ly,0); s->x = 0; s->y = 0; s->lx = 0; s->ly = 0; s->timeval = 0; s->last_draw = INVALID; goto loop; erase: // erase it. if(s->last_draw != INVALID) set_pixel(s->lx, s->ly,0); s->lx = s->x; s->ly = s->y; s->last_draw = INVALID; loop: s++; //printf("end draw loop\n"); } PRINT(("frame done\n")); ////////////////////////////////// // stop doing neat stuff // ///////////////////////////////// // release the semaphore while waiting. gotta release it // at some point or nasty things will happen! release_sem(sem); // loop for another frame! } return B_OK; } ////////////////////////////// // Misc - a place for demos to put their convenience functions ////////////////////////////// ////////////////////////////// // My Silly Raster Lib ////////////////////////////// /* Functions: void draw_line(int x1, int y1, int x2, int y2, int color); void draw_rect(int x, int y, int w, int h, int color); void fill_rect(int x, int y, int w, int h, int color); void draw_ellipse(int x, int y, int xradius, int yradius, int color); void fill_ellipse(int x, int y, int xradius, int yradius, int color); */ void NWindowScreen::draw_line(int x1, int y1, int x2, int y2, int color) { // Simple Bresenham's line drawing algorithm int d,x,y,ax,ay,sx,sy,dx,dy; #define ABS(x) (((x)<0) ? -(x) : (x)) #define SGN(x) (((x)<0) ? -1 : 1) dx=x2-x1; ax=ABS(dx)<<1; sx=SGN(dx); dy=y2-y1; ay=ABS(dy)<<1; sy=SGN(dy); x=x1; y=y1; if(ax>ay) { d=ay-(ax>>1); for(;;) { set_pixel(x,y,color); if(x==x2) return; if(d>=0) { y+=sy; d-=ax; } x+=sx; d+=ay; } } else { d=ax-(ay>>1); for(;;) { set_pixel(x,y,color); if(y==y2) return; if(d>=0) { x+=sx; d-=ay; } y+=sy; d+=ax; } } } void NWindowScreen::draw_rect(int x, int y, int w, int h, int color) { draw_line(x,y,x+w,y,color); draw_line(x,y,x,y+h,color); draw_line(x,y+h,x+w,y+h,color); draw_line(x+w,y,x+w,y+h,color); } void NWindowScreen::fill_rect(int x, int y, int w, int h, int color) { for(int i=0;i (deep_squared*(x + 1))) { if(d < 0) d += deep_squared*(2*x + 3); else { d += deep_squared*(2*x + 3) + wide_squared*(-2*y + 2); y--; } x++; ellipse_points(x, y, cx, cy, color); } d = deep_squared*((x+0.5)*(x+0.5)) + wide_squared*((y-1)*(y-1)) - deep_squared*wide_squared; while(y > 0) { if(d < 0) { d += deep_squared*(2*x + 2) + wide_squared*(-2*y + 3); x++; } else d += wide_squared*(-2*y + 3); y--; ellipse_points(x, y, cx, cy, color); } } void NWindowScreen::fill_ellipse(int cx, int cy, int wide, int deep, int color) { // if we're asked to draw a really small ellipse, put a single pixel in the buffer // and bail if((wide < 1) || (deep < 1)) { set_pixel(cx,cy,color); return; } // MidPoint Ellipse algorithm. // page 90 of Computer Graphics Principles and Practice 2nd edition (I highly recommend this book) int16 x, y; int16 wide_squared, deep_squared; double d; x = 0; y = deep; wide_squared = wide * wide; deep_squared = deep * deep; d = deep_squared - (wide_squared*deep) + (wide_squared/4); ellipse_fill_points(x, y, cx, cy, color); while((wide_squared*(y - 0.5)) > (deep_squared*(x + 1))) { if(d < 0) d += deep_squared*(2*x + 3); else { d += deep_squared*(2*x + 3) + wide_squared*(-2*y + 2); y--; } x++; ellipse_fill_points(x, y, cx, cy, color); } d = deep_squared*((x+0.5)*(x+0.5)) + wide_squared*((y-1)*(y-1)) - deep_squared*wide_squared; while(y > 0) { if(d < 0) { d += deep_squared*(2*x + 2) + wide_squared*(-2*y + 3); x++; } else d += wide_squared*(-2*y + 3); y--; ellipse_fill_points(x, y, cx, cy, color); } } void NWindowScreen::ellipse_points(int x, int y, int x_offset, int y_offset, int color) { // fill four pixels for every iteration in draw_ellipse // the x_offset and y_offset values are needed since the midpoint ellipse algorithm // assumes the midpoint to be at the origin // do a sanity check before each set_pixel, that way we clip to the edges int xCoord, yCoord; xCoord = x_offset + x; yCoord = y_offset + y; if((xCoord > 0) && (yCoord > 0) && (xCoord < width) && (yCoord < height)) set_pixel(xCoord,yCoord,color); xCoord = x_offset - x; if((xCoord > 0) && (yCoord > 0) && (xCoord < width) && (yCoord < height)) set_pixel(xCoord,yCoord,color); xCoord = x_offset + x; yCoord = y_offset - y; if((xCoord > 0) && (yCoord > 0) && (xCoord < width) && (yCoord < height)) set_pixel(xCoord,yCoord,color); xCoord = x_offset - x; if((xCoord > 0) && (yCoord > 0) && (xCoord < width) && (yCoord < height)) set_pixel(xCoord,yCoord,color); } void NWindowScreen::ellipse_fill_points(int x, int y, int x_offset, int y_offset, int color) { // put lines between two pixels twice. once for y positive, the other for y negative (symmetry) // for every iteration in fill_ellipse // the x_offset and y_offset values are needed since the midpoint ellipse algorithm // assumes the midpoint to be at the origin // do a sanity check before each set_pixel, that way we clip to the edges int xCoord1, yCoord1; int xCoord2, yCoord2; xCoord1 = x_offset - x; yCoord1 = y_offset + y; xCoord2 = x_offset + x; yCoord2 = y_offset + y; if((xCoord1 > 0) && (yCoord1 > 0) && (xCoord1 < width) && (yCoord1 < height)) if((xCoord2 > 0) && (yCoord2 > 0) && (xCoord2 < width) && (yCoord2 < height)) draw_line(xCoord1,yCoord1,xCoord2,yCoord2,color); xCoord1 = x_offset - x; yCoord1 = y_offset - y; xCoord2 = x_offset + x; yCoord2 = y_offset - y; if((xCoord1 > 0) && (yCoord1 > 0) && (xCoord1 < width) && (yCoord1 < height)) if((xCoord2 > 0) && (yCoord2 > 0) && (xCoord2 < width) && (yCoord2 < height)) draw_line(xCoord1,yCoord1,xCoord2,yCoord2,color); }