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