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