xref: /haiku/src/tests/kits/game/ParticlesII/particlesII.cpp (revision 1e60bdeab63fa7a57bc9a55b032052e95a18bd2c)
1 #include <Application.h>
2 #include <WindowScreen.h>
3 #include <Screen.h>
4 #include <string.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <math.h>
8 #include <SupportDefs.h> // min_c() and max_c()
9 
10 #ifdef DEBUGGING
11 #define PRINT(x) printf x
12 #else
13 #define PRINT(x)
14 #endif
15 
16 // macros
17 #define set_pixel(x,y,color) (frame_buffer[x + (line_length*y)] = color)
18 #define get_pixel(x,y) (frame_buffer[x + (line_length*y)])
19 
20 class NApplication : public BApplication {
21 public:
22 	NApplication();
23 	bool is_quitting; // So that the WindowScreen knows what
24                      //       to do when disconnected.
25 private:
26 	bool QuitRequested();
27 	void ReadyToRun();
28 };
29 
30 class NWindowScreen : public BWindowScreen {
31 public:
32 	NWindowScreen(status_t*);
33 private:
34 	void ScreenConnected(bool);
35 	long MyCode();
36 	static long Entry(void*);
37 	// handy stuff
38 	void set_frame_rate(float fps) {frame_pause = (bigtime_t)((1000 * 1000)/fps);}
39 	// special for demos
40 	enum {
41 		// used for star->last_draw
42 			INVALID	= 0x7fffffff
43 	};
44 	typedef struct {
45 		float init_velocity;
46 		float gravity;
47 		double cos_z_theta;
48 		int32 y;
49 		int32 x;
50 		int32 timeval;
51 		uint32 last_draw;
52 		int32 lx,ly;
53 	} particle;
54 	uint32 particle_count;
55 	particle *particle_list;
56 	// simple raster functions
57 	void draw_line(int x1, int y1, int x2, int y2, int color);
58 	void draw_rect(int x, int y, int w, int h, int color);
59 	void fill_rect(int x, int y, int w, int h, int color);
60 	void draw_ellipse(int cx, int cy, int wide, int deep, int color);
61 	void fill_ellipse(int x, int y, int xradius, int yradius, int color);
62 	void ellipse_points(int x, int y, int x_offset, int y_offset, int color);
63 	void ellipse_fill_points(int x, int y, int x_offset, int y_offset, int color);
64 	thread_id tid;
65 	sem_id sem;
66 	area_id area;
67 	uint8* save_buffer;
68 	uint8* frame_buffer;
69 	ulong line_length;
70 	bigtime_t frame_pause; // time between frames
71 	int width,height;
72 	int COLORS;
73 	bool thread_is_locked;	// small hack to allow to quit the
74 							// 	app from ScreenConnected()
75 };
76 
77 int
78 main()
79 {
80 	NApplication app;
81 	return 0;
82 }
83 
84 NApplication::NApplication()
85       :BApplication("application/x-vnd.Prok-DemoTemplate")
86 {
87 	Run(); // see you in ReadyToRun()
88 }
89 
90 void NApplication::ReadyToRun()
91 {
92 	PRINT(("ReadyToRun()\n"));
93 	status_t ret = B_ERROR;
94 	is_quitting = false;
95 	NWindowScreen *ws = new NWindowScreen(&ret);
96 	PRINT(("WindowScreen ctor returned. ret = %s\n", strerror(ret)));
97 	// exit if constructing the WindowScreen failed.
98 	if((ws == NULL) || (ret < B_OK))
99 	{
100 		//printf("the window screen was NULL, or there was an error\n");
101 		PostMessage(B_QUIT_REQUESTED);
102 	}
103 	else
104 		PRINT(("everything's just peachy. done with ReadyToRun().\n"));
105 }
106 
107 bool NApplication::QuitRequested()
108 {
109 	PRINT(("QuitRequested()\n"));
110 	status_t ret;
111 	is_quitting = true;
112 	wait_for_thread(find_thread("rendering thread"), &ret); // wait for the render thread to finish
113 	return true;
114 }
115 
116 NWindowScreen::NWindowScreen(status_t *ret)
117 	: BWindowScreen("Example", B_8_BIT_640x480, ret), width(639), height(479), COLORS(256)
118 {
119 	PRINT(("WindowScreen ctor.\n"));
120 	thread_is_locked = true;
121 	tid = 0;
122 	if(*ret == B_OK)
123 	{
124 		PRINT(("creating blocking sem and save_buffer area.\n"));
125 		// this semaphore controls the access to the WindowScreen
126 		sem = create_sem(0,"WindowScreen Access");
127 		// this area is used to save the whole framebuffer when
128 		//       switching workspaces. (better than malloc()).
129 		area = create_area("save", (void**)&save_buffer, B_ANY_ADDRESS, 640*480, B_NO_LOCK, B_READ_AREA|B_WRITE_AREA);
130 		// exit if an error occured.
131 		if((sem < B_OK) || (area < B_OK))
132 		{
133 			PRINT(("create_area() or create_sem() failed\n"));
134 			*ret = B_ERROR;
135 		}
136 		else
137 		{
138 			PRINT(("calling Show().\n"));
139 			Show(); // let's go. See you in ScreenConnected.
140 		}
141 	}
142 	else
143 	{
144 		PRINT(("BWindowScreen base class ctor returned failure\n"));
145 		be_app->PostMessage(B_QUIT_REQUESTED);
146 	}
147 	// set the frame rate
148 	set_frame_rate(30.);
149 }
150 
151 
152 void
153 NWindowScreen::ScreenConnected(bool connected)
154 {
155 	PRINT(("ScreenConnected()\n"));
156 	fflush(stdout);
157 	if(connected)
158 	{
159 		if(SetSpace(B_8_BIT_640x480) < B_OK)
160 		{
161 			//SetFrameBuffer(640, 480);
162 			PRINT(("SetSpace() failed\n"));
163 			// properly set the framebuffer. exit if an error occurs.
164 			be_app->PostMessage(B_QUIT_REQUESTED);
165 			return;
166 		}
167 		// get the framebuffer-related info, each time the
168 		// WindowScreen is connected (multiple monitor)
169 		frame_buffer = (uint8*)(CardInfo()->frame_buffer);
170 		line_length = FrameBufferInfo()->bytes_per_row;
171 		if(tid == 0)
172 		{
173 			// clean the framebuffer
174 			PRINT(("zeroing the framebuffer\n"));
175 			memset(frame_buffer,0,480*line_length);
176 			// spawn the rendering thread. exit if an error occurs.
177 			PRINT(("spawning the render thread.\n"));
178 			tid = spawn_thread(Entry,"rendering thread", B_URGENT_DISPLAY_PRIORITY,this);
179 			if(resume_thread(tid) < B_OK)
180 			{
181 				be_app->PostMessage(B_QUIT_REQUESTED);
182 				return;
183 			}
184 		}
185 		else
186 		{
187 			for(int y=0;y<480;y++)
188 			{
189 				// restore the framebuffer when switching back from
190 				// another workspace.
191             	memcpy(frame_buffer+y*line_length,save_buffer+640*y,640);
192             }
193 		}
194 		// set our color list.
195 		rgb_color palette[256];
196 		rgb_color c1;
197 		for(int i=0,j=0;i<256;i++,j++)
198 		{
199 			if(i<64)
200 			{
201 				c1.red = j*4; // greys
202 				c1.green = j*4;
203 				c1.blue = j*4;
204 				c1.alpha = 255;
205 			}
206 			if((i>=64) && (i<128))
207 			{
208 				c1.red = j*4; // reds
209 				c1.green = 0;
210 				c1.blue = 0;
211 				c1.alpha = 255;
212 			}
213 			if((i>=128) && (i<192))
214 			{
215 				c1.red = 0; // greens
216 				c1.green = j*4;
217 				c1.blue = 0;
218 				c1.alpha = 255;
219 			}
220 			if((i>=192) && (i<256))
221 			{
222 				c1.red = 0; // blues
223 				c1.green = 0;
224 				c1.blue = j*4;
225 				c1.alpha = 255;
226 			}
227 			if(j == 64)
228 				j=0;
229 			palette[i]=c1;
230 		}
231 		SetColorList(palette);
232 
233 		// allow the rendering thread to run.
234 		thread_is_locked = false;
235 		release_sem(sem);
236 	}
237 	else /* !connected */
238 	{
239 		// block the rendering thread.
240 		if(!thread_is_locked)
241 		{
242 			acquire_sem(sem);
243 			thread_is_locked = true;
244 		}
245 		// kill the rendering and clean up when quitting
246 		if((((NApplication*)be_app)->is_quitting))
247 		{
248 			status_t ret;
249 			kill_thread(tid);
250 			wait_for_thread(tid,&ret);
251 			delete_sem(sem);
252 			delete_area(area);
253 			free(particle_list);
254 		}
255 		else
256 		{
257 			// set the color list black so that the screen doesn't seem
258 			// to freeze while saving the framebuffer
259 			rgb_color c={0,0,0,255};
260 			rgb_color palette[256];
261 			// build the palette
262 			for(int i=0;i<256;i++)
263 				palette[i] = c;
264 			// set the palette
265 			SetColorList(palette);
266 			// save the framebuffer
267 			for(int y=0;y<480;y++)
268 				memcpy(save_buffer+640*y,frame_buffer+y*line_length,640);
269 		}
270 	}
271 }
272 
273 
274 long
275 NWindowScreen::Entry(void* p)
276 {
277    return ((NWindowScreen*)p)->MyCode();
278 }
279 
280 
281 long
282 NWindowScreen::MyCode()
283 {
284 	bigtime_t trgt = system_time() + frame_pause;
285 	srandom(system_time());
286 	// beforehand stuff
287 	particle_count = 1024*2;
288 	particle_list = (particle *)malloc(sizeof(particle)*particle_count);
289 	for (uint32 i=0; i<particle_count; i++) {
290 		uint32 rand_max = 0xffffffff;
291 		particle_list[i].init_velocity = -((double)((rand_max>>1)+(random()%(rand_max>>1)))/rand_max)*3.333; // magic number
292 		particle_list[i].gravity = -(((double)((rand_max>>1)+(random()%(rand_max>>1)))/rand_max))*0.599; // more magic
293 
294 		// make the particle initialy invisible and fixed, but at a random moment in time
295 		particle_list[i].lx = 0;
296 		particle_list[i].ly = 0;
297 		particle_list[i].last_draw = INVALID;
298 		particle_list[i].timeval = random() & 64;
299 		particle_list[i].x = 0; // this gets figured out at drawtime
300 		particle_list[i].y = 0; // same here
301 		particle_list[i].cos_z_theta = cos(random() % 360); // grab an angle
302 	}
303 
304 
305 	// the loop o' fun
306 	while (!(((NApplication*)be_app)->is_quitting)) {
307 		// try to sync with the vertical retrace
308 		if (BScreen(this).WaitForRetrace() != B_OK) {
309 			// snoze for a bit so that other threads can be happy.
310 			// We are realtime priority you know
311 			if (system_time() < trgt)
312 				snooze(trgt - system_time());
313 			trgt = system_time() + frame_pause;
314 		}
315 
316 		// gain access to the framebuffer before writing to it.
317 		acquire_sem(sem); // block until we're allowed to own the framebuffer
318 
319 		///////////////////////////////
320 		// do neat stuff here //
321 		//////////////////////////////
322 		PRINT(("rendering a frame.\n"));
323 
324 
325 		// eye candy VII - particles! - my own cookin
326 		int32 x, y, cx,cy;
327 		set_frame_rate(60.); // woo. ntsc
328 		// calculate the center
329 		cx = width/2;
330 		cy = height/2;
331 
332 		// palette test
333 		//set_frame_rate(0.1);
334 		//for(int i=0;i<256;i++)
335 		//	draw_line(i,0,i,height, i);
336 
337 		PRINT(("Starting particle drawing loop\n"));
338 		particle *s = particle_list;
339 		for (uint32 i=0; i<particle_count; i++) {
340 			PRINT(("drawing particle %d\r", i));
341 
342 			// save the old position
343 			s->lx = s->x;
344 			s->ly = s->y;
345 
346 			PRINT(("cx=%d, cy=%d\n", cx,cy));
347 
348 			// move the particle
349 			// find y and x
350 			// (s->gravity/2)*(s->timeval*s->timeval) * 1.85 is magic
351 			y = s->y = (int32)(cy + (int32)((s->gravity/2)*(s->timeval*s->timeval)*1.94) + ((s->init_velocity - (s->gravity*s->timeval)) * s->timeval));
352 			x = s->x = (int32)(cx + (int32)(s->timeval * s->cos_z_theta)); // 3d rotation
353 
354 			// interate timeval
355 			s->timeval++;
356 
357 			// sanity check
358 			if(x <= 0)
359 				goto erase_and_reset;
360 			if(x > width)
361 				goto erase_and_reset;
362 			if(y < 0)
363 				goto erase; // invisible + erase last position
364 			if(y > height)
365 				goto erase_and_reset;
366 
367 			// erase the previous position, if necessary
368 			if (s->last_draw != INVALID)
369 				set_pixel(s->lx,s->ly,0);
370 
371 			// if it's visible, then draw it.
372 			set_pixel(s->x,s->y, 169);
373 			s->last_draw = 1;
374 			goto loop;
375 
376 			erase_and_reset:
377 			if((s->lx <= width) && (s->lx >= 0) && (s->ly <= height) && (s->ly >= 0))
378 				set_pixel(s->lx, s->ly,0);
379 			s->x = 0;
380 			s->y = 0;
381 			s->lx = 0;
382 			s->ly = 0;
383 			s->timeval = 0;
384 			s->last_draw = INVALID;
385 			goto loop;
386 
387 			erase:
388 			// erase it.
389 			if(s->last_draw != INVALID)
390 				set_pixel(s->lx, s->ly,0);
391 			s->lx = s->x;
392 			s->ly = s->y;
393 			s->last_draw = INVALID;
394 			loop:
395 				s++;
396 			//printf("end draw loop\n");
397 		}
398 		PRINT(("frame done\n"));
399 
400 		//////////////////////////////////
401 		// stop doing neat stuff //
402 		/////////////////////////////////
403 
404 		// release the semaphore while waiting. gotta release it
405 		// at some point or nasty things will happen!
406 		release_sem(sem);
407 		// loop for another frame!
408 	}
409 	return B_OK;
410 }
411 
412 //////////////////////////////
413 // Misc - a place for demos to put their convenience functions
414 //////////////////////////////
415 
416 
417 //////////////////////////////
418 // My Silly Raster Lib
419 //////////////////////////////
420 
421 /*
422 
423 	Functions:
424 void draw_line(int x1, int y1, int x2, int y2, int color);
425 void draw_rect(int x, int y, int w, int h, int color);
426 void fill_rect(int x, int y, int w, int h, int color);
427 void draw_ellipse(int x, int y, int xradius, int yradius, int color);
428 void fill_ellipse(int x, int y, int xradius, int yradius, int color);
429 
430 */
431 
432 void
433 NWindowScreen::draw_line(int x1, int y1, int x2, int y2, int color)
434 {
435 	// Simple Bresenham's line drawing algorithm
436 	int d,x,y,ax,ay,sx,sy,dx,dy;
437 
438 #define ABS(x) (((x)<0) ? -(x) : (x))
439 #define SGN(x) (((x)<0) ? -1 : 1)
440 
441 	dx=x2-x1; ax=ABS(dx)<<1; sx=SGN(dx);
442 	dy=y2-y1; ay=ABS(dy)<<1; sy=SGN(dy);
443 
444 	x=x1;
445 	y=y1;
446 	if(ax>ay)
447 	{
448 		d=ay-(ax>>1);
449 		for(;;)
450 		{
451 			set_pixel(x,y,color);
452 			if(x==x2) return;
453 			if(d>=0)
454 			{
455 				y+=sy;
456 				d-=ax;
457 			}
458 			x+=sx;
459 			d+=ay;
460 		}
461 	}
462 	else
463 	{
464 		d=ax-(ay>>1);
465 		for(;;)
466 		{
467 			set_pixel(x,y,color);
468 			if(y==y2) return;
469 			if(d>=0)
470 			{
471 				x+=sx;
472 				d-=ay;
473 			}
474 			y+=sy;
475 			d+=ax;
476 		}
477 	}
478 }
479 
480 
481 void
482 NWindowScreen::draw_rect(int x, int y, int w, int h, int color)
483 {
484 	draw_line(x,y,x+w,y,color);
485 	draw_line(x,y,x,y+h,color);
486 	draw_line(x,y+h,x+w,y+h,color);
487 	draw_line(x+w,y,x+w,y+h,color);
488 }
489 
490 
491 void
492 NWindowScreen::fill_rect(int x, int y, int w, int h, int color)
493 {
494 	for (int i = 0; i < w; i++) {
495 		for (int j = 0; j < h; j++) {
496 			set_pixel(i, j, color);
497 		}
498 	}
499 }
500 
501 
502 void
503 NWindowScreen::draw_ellipse(int cx, int cy, int wide, int deep, int color)
504 {
505 	// if we're asked to draw a really small ellipse, put a single pixel in the buffer
506 	// and bail
507 	if((wide < 1) || (deep < 1))
508 	{
509 		set_pixel(cx,cy,color);
510 		return;
511 	}
512 
513 	// MidPoint Ellipse algorithm.
514 	// page 90 of Computer Graphics Principles and Practice 2nd edition (I highly recommend this book)
515 	int16 x, y;
516 	int16 wide_squared, deep_squared;
517 	double d;
518 
519 	x = 0;
520 	y = deep;
521 	wide_squared = wide * wide;
522 	deep_squared = deep * deep;
523 	d = deep_squared - (wide_squared*deep) + (wide_squared/4);
524 
525 	ellipse_points(x, y, cx, cy, color);
526 	while((wide_squared*(y - 0.5)) > (deep_squared*(x + 1)))
527 	{
528 		if(d < 0)
529 			d += deep_squared*(2*x + 3);
530 		else
531 		{
532 			d += deep_squared*(2*x + 3) + wide_squared*(-2*y + 2);
533 			y--;
534 		}
535 		x++;
536 		ellipse_points(x, y, cx, cy, color);
537 	}
538 
539 	d = deep_squared*((x+0.5)*(x+0.5)) + wide_squared*((y-1)*(y-1)) - deep_squared*wide_squared;
540 	while(y > 0)
541 	{
542 		if(d < 0)
543 		{
544 			d += deep_squared*(2*x + 2) + wide_squared*(-2*y + 3);
545 			x++;
546 		}
547 		else
548 			d += wide_squared*(-2*y + 3);
549 		y--;
550 		ellipse_points(x, y, cx, cy, color);
551 	}
552 }
553 
554 
555 void
556 NWindowScreen::fill_ellipse(int cx, int cy, int wide, int deep, int color)
557 {
558 	// if we're asked to draw a really small ellipse, put a single pixel in the buffer
559 	// and bail
560 	if((wide < 1) || (deep < 1))
561 	{
562 		set_pixel(cx,cy,color);
563 		return;
564 	}
565 
566 	// MidPoint Ellipse algorithm.
567 	// page 90 of Computer Graphics Principles and Practice 2nd edition (I highly recommend this book)
568 	int16 x, y;
569 	int16 wide_squared, deep_squared;
570 	double d;
571 
572 	x = 0;
573 	y = deep;
574 	wide_squared = wide * wide;
575 	deep_squared = deep * deep;
576 	d = deep_squared - (wide_squared*deep) + (wide_squared/4);
577 
578 	ellipse_fill_points(x, y, cx, cy, color);
579 	while((wide_squared*(y - 0.5)) > (deep_squared*(x + 1)))
580 	{
581 		if(d < 0)
582 			d += deep_squared*(2*x + 3);
583 		else
584 		{
585 			d += deep_squared*(2*x + 3) + wide_squared*(-2*y + 2);
586 			y--;
587 		}
588 		x++;
589 		ellipse_fill_points(x, y, cx, cy, color);
590 	}
591 
592 	d = deep_squared*((x+0.5)*(x+0.5)) + wide_squared*((y-1)*(y-1)) - deep_squared*wide_squared;
593 	while(y > 0)
594 	{
595 		if(d < 0)
596 		{
597 			d += deep_squared*(2*x + 2) + wide_squared*(-2*y + 3);
598 			x++;
599 		}
600 		else
601 			d += wide_squared*(-2*y + 3);
602 		y--;
603 		ellipse_fill_points(x, y, cx, cy, color);
604 	}
605 }
606 
607 
608 void
609 NWindowScreen::ellipse_points(int x, int y, int x_offset, int y_offset, int color)
610 {
611 	// fill four pixels for every iteration in draw_ellipse
612 
613 	// the x_offset and y_offset values are needed since the midpoint ellipse algorithm
614 	// assumes the midpoint to be at the origin
615 	// do a sanity check before each set_pixel, that way we clip to the edges
616 
617 	int xCoord, yCoord;
618 
619 	xCoord = x_offset + x;
620 	yCoord = y_offset + y;
621 	if((xCoord > 0) && (yCoord > 0) && (xCoord < width) && (yCoord < height))
622 		set_pixel(xCoord,yCoord,color);
623 	xCoord = x_offset - x;
624 	if((xCoord > 0) && (yCoord > 0) && (xCoord < width) && (yCoord < height))
625 		set_pixel(xCoord,yCoord,color);
626 	xCoord = x_offset + x;
627 	yCoord = y_offset - y;
628 	if((xCoord > 0) && (yCoord > 0) && (xCoord < width) && (yCoord < height))
629 		set_pixel(xCoord,yCoord,color);
630 	xCoord = x_offset - x;
631 	if((xCoord > 0) && (yCoord > 0) && (xCoord < width) && (yCoord < height))
632 		set_pixel(xCoord,yCoord,color);
633 }
634 
635 
636 void
637 NWindowScreen::ellipse_fill_points(int x, int y, int x_offset, int y_offset, int color)
638 {
639 	// put lines between two pixels twice. once for y positive, the other for y negative (symmetry)
640 	// for every iteration in fill_ellipse
641 
642 	// the x_offset and y_offset values are needed since the midpoint ellipse algorithm
643 	// assumes the midpoint to be at the origin
644 	// do a sanity check before each set_pixel, that way we clip to the edges
645 
646 	int xCoord1, yCoord1;
647 	int xCoord2, yCoord2;
648 
649 	xCoord1 = x_offset - x;
650 	yCoord1 = y_offset + y;
651 	xCoord2 = x_offset + x;
652 	yCoord2 = y_offset + y;
653 	if((xCoord1 > 0) && (yCoord1 > 0) && (xCoord1 < width) && (yCoord1 < height))
654 		if((xCoord2 > 0) && (yCoord2 > 0) && (xCoord2 < width) && (yCoord2 < height))
655 			draw_line(xCoord1,yCoord1,xCoord2,yCoord2,color);
656 
657 	xCoord1 = x_offset - x;
658 	yCoord1 = y_offset - y;
659 	xCoord2 = x_offset + x;
660 	yCoord2 = y_offset - y;
661 	if((xCoord1 > 0) && (yCoord1 > 0) && (xCoord1 < width) && (yCoord1 < height))
662 		if((xCoord2 > 0) && (yCoord2 > 0) && (xCoord2 < width) && (yCoord2 < height))
663 			draw_line(xCoord1,yCoord1,xCoord2,yCoord2,color);
664 
665 }
666