1 /*	Quantum Minigolf, a computer game illustrating quantum mechanics
2 	Copyright (C) 2007 Friedemann Reinhard <friedemann.reinhard@gmail.com>
3 
4 	This program is free software; you can redistribute it and/or modify
5 	it under the terms of the GNU General Public License as published by
6 	the Free Software Foundation; either version 2 of the License, or
7 	(at your option) any later version.
8 
9 	This program is distributed in the hope that it will be useful,
10 	but WITHOUT ANY WARRANTY; without even the implied warranty of
11 	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 	GNU General Public License for more details.
13 
14 	You should have received a copy of the GNU General Public License
15 	along with this program; if not, write to the Free Software
16 	Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 */
18 
19 #include "Renderer.h"
20 
21 // Constructor:
22 // initialize all graphics buffers, load the complex color map and the fonts
Renderer(int width,int height,int flag,int holex,int holey,int holer,int rball)23 Renderer::Renderer(int width, int height, int flag, int holex, int holey, int holer, int rball)
24 {
25 	this->width = width;
26 	this->height = height;
27 
28 	this->holex = holex;
29 	this->holey = holey;
30 	this->holer = holer;
31 	this->rball = rball;
32 
33 #ifdef _PROFILE
34     char dummy[80];
35     SDL_VideoInfo *scrnfo;
36 #endif
37 
38     SDL_Init( SDL_INIT_VIDEO );
39 	SDL_WM_SetCaption("Quantum Minigolf", NULL);
40 	SDL_WM_SetIcon(SDL_LoadBMP("/usr/local/share/quantumminigolf/gfx/icon.bmp"), NULL);
41 
42 #ifdef _PROFILE
43     scrnfo = SDL_GetVideoInfo();
44     printf("Video hardware info:\n");
45     printf("hw_available: %d\n", scrnfo->hw_available);
46     printf("wm_available: %d\n", scrnfo->hw_available);
47     printf("blit_sw: %d\n", scrnfo->blit_sw);
48     printf("blit_sw_CC: %d\n", scrnfo->blit_sw_CC);
49     printf("video_mem: %d\n", scrnfo->video_mem);
50     printf("optimal color depth: %d bits\n", scrnfo->vfmt->BitsPerPixel);
51     SDL_VideoDriverName(dummy, 80);
52     printf("video driver: %s\n", dummy);
53     printf("\n");
54 #endif
55 
56 #ifdef VR
57     screen = SDL_SetVideoMode(640, 480, 32, flag | SDL_HWACCEL );
58 #endif
59 #ifndef VR
60 	screen = SDL_SetVideoMode(width, height, 32, flag | SDL_HWACCEL );
61 #endif
62 
63     bBuffer = SDL_CreateRGBSurface( SDL_SWSURFACE, screen->w,
64 				     screen->h,
65 				     screen->format->BitsPerPixel,
66 				     screen->format->Rmask,
67 				     screen->format->Gmask,
68 				     screen->format->Bmask,
69 				     screen->format->Amask);
70 
71 	if (!bBuffer)
72 	{
73 		printf("Error: Can't create SDL_SWSURFACE \n\n");
74 		exit(1);
75 	}
76 
77     wave = SDL_CreateRGBSurface( SDL_SWSURFACE|SDL_SRCALPHA, screen->w,
78 					screen->h,
79 					32,
80 					0xff << 24,
81 					0xff << 16,
82 					0xff << 8,
83 					0xff);
84 		if (!wave)
85 	{
86 		printf("Error: Can't create SDL_SWSURFACE \n\n");
87 		exit(1);
88 	}
89 
90 		cmapm = SDL_LoadBMP( "/usr/local/share/quantumminigolf/gfx/cmap_mono.bmp" );
91 		cmapc = SDL_LoadBMP( "/usr/local/share/quantumminigolf/gfx/cmap.bmp" );
92 		win = SDL_LoadBMP( "/usr/local/share/quantumminigolf/gfx/win.bmp" );
93 		lose = SDL_LoadBMP( "/usr/local/share/quantumminigolf/gfx/lose.bmp" );
94 
95 	if (cmapm == NULL)
96 	{
97 		printf("Error: Can't load bitmap cmap_mono\n\n");
98 		exit(1);
99 	}
100 	if (cmapc == NULL)
101 	{
102 		printf("Error: Can't load bitmap cmap\n\n");
103 		exit(1);
104 	}
105 	if (win==NULL )
106 	{
107 		printf("Error: Can't load bitmap win\n\n");
108 		exit(1);
109 	}
110 	if (lose==NULL)
111 	{
112 		printf("Error: Can't load bitmap lose\n\n");
113 		exit(1);
114 	}
115 
116 	// compute transparency data of the color maps
117 	cmapc = SDL_ConvertSurface(cmapc, wave->format, SDL_SWSURFACE | SDL_SRCALPHA);
118 	Uint32 *pdata = (Uint32 *)cmapc->pixels;
119 	for(int i=0; i<cmapc->w; i++){
120 		for(int j=0; j<cmapc->h; j++){
121 			unsigned char red, green, blue, alpha;
122 			SDL_GetRGBA(pdata[j*cmapc->w + i], cmapc->format, &red, &green, &blue, &alpha);
123 			alpha = red;
124 			if(green > alpha) alpha = green; if(blue > alpha) alpha = blue;
125 			if(alpha > 0){
126 				red = red*255/alpha;		if(red > 255) red=255;
127 				green = green*255/alpha;	if(green > 255) green=255;
128 				blue = blue*255/alpha;		if(blue > 255) blue=255;
129 			}
130 			pdata[j*cmapc->w + i] = SDL_MapRGBA(cmapc->format, red, green, blue, alpha);
131 		}
132 	}
133 
134 	cmapm = SDL_ConvertSurface(cmapm, wave->format, SDL_SWSURFACE | SDL_SRCALPHA);
135 	pdata = (Uint32 *)cmapm->pixels;
136 	for(int i=0; i<cmapm->w; i++){
137 		for(int j=0; j<cmapm->h; j++){
138 			unsigned char red, green, blue, alpha;
139 			SDL_GetRGBA(pdata[j*cmapm->w + i], cmapm->format, &red, &green, &blue, &alpha);
140 			alpha = red;
141 			if(green > alpha) alpha = green; if(blue > alpha) alpha = blue;
142 			if(alpha > 0){
143 				red = red*255/alpha;		if(red > 255) red=255;
144 				green = green*255/alpha;	if(green > 255) green=255;
145 				blue = blue*255/alpha;		if(blue > 255) blue=255;
146 			}
147 			pdata[j*cmapm->w + i] = SDL_MapRGBA(cmapm->format, red, green, blue, alpha);
148 		}
149 	}
150 	cmap = cmapc;
151 
152 	win = SDL_ConvertSurface(win, screen->format, SDL_SWSURFACE);
153 	lose = SDL_ConvertSurface(lose, screen->format, SDL_SWSURFACE);
154 
155 	SDL_SetColorKey(win, SDL_SRCCOLORKEY, SDL_MapRGB(win->format, 255, 255, 255));
156 	SDL_SetColorKey(lose, SDL_SRCCOLORKEY, SDL_MapRGB(lose->format, 255, 255, 255));
157 
158 	//load the fonts for the menu - preferably tahoma, otherwise LinLiberty
159 	TTF_Init();
160 
161 #ifdef LINUX
162 		fntsml = TTF_OpenFont( "/usr/local/share/quantumminigolf/fonts/default.ttf", 12 );
163 		fntbg = TTF_OpenFont( "/usr/local/share/quantumminigolf/fonts/default.ttf", 24 );
164 #endif LINUX
165 
166 #ifdef WIN32
167 	fntsml = TTF_OpenFont( "C:\\windows\\fonts\\tahoma.ttf", 12 );
168 	if(fntsml==NULL){
169  		fntsml = TTF_OpenFont( "/usr/local/share/quantumminigolf/fonts/default.ttf", 12 );
170  	}
171 
172  	fntbg = TTF_OpenFont( "C:\\windows\\fonts\\tahoma.ttf", 24 );
173  	if(fntbg==NULL){
174  		fntbg = TTF_OpenFont( "/usr/local/share/quantumminigolf/fonts/default.ttf", 24 );
175  	}
176 #endif
177 
178     rBuffer.x = 0;
179 #ifdef VR
180 	rBuffer.y = 160;  //160
181 #endif
182 #ifndef VR
183 	rBuffer.y = 0;
184 #endif
185 
186     rBuffer.w = bBuffer->w;
187     rBuffer.h = bBuffer->h;
188 
189     SDL_EventState(SDL_ACTIVEEVENT, SDL_IGNORE);
190     SDL_EventState(SDL_MOUSEMOTION, SDL_IGNORE);
191 	SDL_EventState(SDL_KEYUP, SDL_IGNORE);
192 	SDL_EventState(SDL_KEYDOWN, SDL_ENABLE);
193 
194     SDL_ShowCursor( SDL_DISABLE );
195 }
196 
197 
~Renderer(void)198 Renderer::~Renderer(void)
199 {
200 	TTF_CloseFont( fntsml );
201 	TTF_CloseFont( fntbg );
202 	TTF_Quit();
203 
204 	SDL_FreeSurface(bBuffer);
205 	SDL_FreeSurface(wave);
206 	SDL_FreeSurface(cmap);
207 	SDL_FreeSurface(win);
208 	SDL_FreeSurface(lose);
209     SDL_Quit();
210 }
211 
212 
213 // RenderTrack
214 // render the bare track (i.e.: the potential bitmap) without any balls, clubs, and holes
RenderTrack()215 void Renderer::RenderTrack(){
216 	SDL_Rect rblit;
217 
218 	rblit.x = 0;
219 	rblit.y = 0;
220 	rblit.w = bBuffer->w;
221     rblit.h = bBuffer->h;
222 
223 	SDL_BlitSurface (V, NULL, bBuffer, &rblit);
224 }
225 
226 //RenderHole
227 //render the (minimalistic, OK) hole into the bBuffer
RenderHole(float x,float y,float r)228 void Renderer::RenderHole(float x, float y, float r){
229 	float phi;
230 
231 	Uint32 *buffer_dat = (Uint32 *)bBuffer->pixels;
232 	SDL_PixelFormat *fmt = bBuffer->format;
233 
234 	for(phi=0; phi<2*M_PI; phi+=.01){
235 		buffer_dat[(int)(x + r*sin(phi)) + (int)(y + r*cos(phi))*width] = SDL_MapRGB(fmt, 255, 255, 0);
236 	}
237 }
238 
239 
240 
241 //RenderWave
242 //given the complex wavefunction psi render it into buffer
243 //nota bene: The color coding of the wavefunction is performed by mapping it
244 //to the precalculated color map 'cmap'
RenderWave(fftwf_complex * psi)245 void Renderer::RenderWave(fftwf_complex *psi){
246 	Uint32 *buffer_dat, *cmap_dat, *V_dat, *wave_dat;
247 	int x, y;
248 	int cw = cmap->w;
249 	int ch = cmap->h;
250 	int cx, cy;
251 	Uint8 red, dummy;
252 
253 	SDL_PixelFormat *fmt = bBuffer->format;
254 
255 	buffer_dat = (Uint32*)bBuffer->pixels;
256 	cmap_dat = (Uint32*)cmap->pixels;
257 	V_dat = (Uint32*)V->pixels;
258 	wave_dat = (Uint32*)wave->pixels;
259 
260 	for (x=0; x<width; x++){
261 		for (y=0; y<height; y++)
262 		{
263 				cx = (int)(psi[x*height + y][1])+128;
264 				cy = (int)(psi[x*height + y][0])+128;
265 				if(cx > 240) cx = 240;
266 				if(cx < 0) cx = 0;
267 				if(cy > 240) cy = 240;
268 				if(cy < 0) cy = 0;
269 			wave_dat[y*width + x] = cmap_dat[cx*cw + cy];
270 		}
271 	}
272 	SDL_Rect rblit;
273 
274 	rblit.x = 0;
275 	rblit.y = 0;
276 	rblit.w = bBuffer->w;
277     rblit.h = bBuffer->h;
278 
279 	SDL_BlitSurface (wave, NULL, bBuffer, &rblit);
280 
281 	RenderHole(holex, holey, holer);
282 }
283 
284 // RenderBall
285 // render the (classical) ball at a position posx, posy into buffer.
286 // used at the beginning (to draw the ball at the "Abschlag")
287 // and at the end (i.e. after the position measurement)
RenderBall(float posx,float posy)288 void Renderer::RenderBall(float posx, float posy){
289     SDL_PixelFormat *fmt = bBuffer->format;
290 
291    float r, phi;
292    Uint32 *buffer_dat = (Uint32*)bBuffer->pixels;
293 
294     // render the ball
295 	for(phi=0; phi<2*M_PI; phi += .1){
296 		for(r=0; r<rball; r+=.5){
297 			buffer_dat[(int)(posx + r*sin(phi)) + (int)(posy + r*cos(phi))*width] = SDL_MapRGB(fmt, 0, 0, 255);
298 		}
299 	}
300 
301     RenderHole(holex, holey, holer);
302 }
303 
304 // Render the racket
305 // l: length of the racket
306 // r: distance from ball
307 // ix, iy: position of the ball
308 // mousey: mouse y position
RenderRacket(float l,float r,int ix,int iy,float rphi)309 void Renderer::RenderRacket(float l, float r, int ix, int iy, float rphi){
310 	float xl, xo, yl, yo; // upper and lower coordinate bounds of the racket
311 	float dx, dy; // x and y increments
312 	int x, y;
313 
314 	float i;
315 
316 	SDL_PixelFormat *fmt = bBuffer->format;
317 
318 	Uint32 *buffer_dat = (Uint32*)bBuffer->pixels;
319 
320 	xo = ix + r*cos(rphi) - .5* l * sin(rphi);
321 	xl = ix + r*cos(rphi) + .5* l * sin(rphi);
322 	yo = iy + r*sin(rphi) + .5* l * cos(rphi);
323 	yl = iy + r*sin(rphi) - .5* l * cos(rphi);
324 
325 	dx = xo-xl;
326 	dy = yo-yl;
327 
328 	for(i=0; i<1; i+=.01){
329 		x = (int)(xl+i*dx); if(x>(width-1))x=width-1; if(x<0)x=0;
330 		y = (int)(yl+i*dy); if(y>(height-1))y=height-1; if(y<0)y=0;
331 		buffer_dat[y*width+x] = SDL_MapRGB(fmt, 255,255,0);
332 	}
333 }
334 
335 // RenderFlash
336 // Render a flash (one white frame) to indicate the position measurement
RenderFlash()337 void Renderer::RenderFlash(){
338 	int x, y;
339 
340 	SDL_PixelFormat *fmt = bBuffer->format;
341 
342 	Uint32 *buffer_dat = (Uint32*)bBuffer->pixels;
343 
344 	for(x=0; x<width; x++){
345 		for(y=0; y<height; y++){
346 			buffer_dat[y*width+x] = SDL_MapRGB(fmt, 255,255,255);
347 		}
348 	}
349 }
350 
351 // RenderExtro
352 // Depending on result, render either the win / lose message
353 // at position ypos.
RenderExtro(int result,int ypos)354 void Renderer::RenderExtro(int result, int ypos){
355 	SDL_Rect rResblit;
356 	SDL_Surface *res;
357 
358 	if(result == QMG_WIN)
359 	res = win;
360     else
361 	res = lose;
362 
363 	if(ypos > height/2-res->h)
364 	    ypos = height/2-res->h;
365 
366 	rResblit.x = width/2-res->w/2;
367 	rResblit.y = ypos;
368 	rResblit.w = res->w;
369     rResblit.h = res->h;
370 
371 	SDL_BlitSurface (res, NULL, bBuffer, &rResblit);
372 }
373 
374 // RenderMenu
375 // render an info screen with
376 // transparent background which will be overlayed when selecting the track
RenderMenu(bool quantum)377 void Renderer::RenderMenu(bool quantum){
378 	int linesep = 20;
379 
380 	if(fntbg==NULL || fntsml==NULL) return;
381 
382 	SDL_Color clrFg = {255,0,0,255};  // Red ("Fg" is foreground)
383 	SDL_Surface *welcome = TTF_RenderText_Solid( fntbg, "Welcome to Quantum Minigolf", clrFg );
384 	SDL_Surface *select = TTF_RenderText_Solid( fntsml, "Press <left> or <right> to select your track,", clrFg );
385 	SDL_Surface *start = TTF_RenderText_Solid( fntsml, "<Enter> or <Space> to start a game", clrFg );
386 	SDL_Surface *toggle= TTF_RenderText_Solid( fntsml, "<q> to toggle quantum mode", clrFg );
387 	string map = "<c> to switch to "; map.append((cmap==cmapm) ? "colored " : "monochrome "); map.append("colormap");
388 	SDL_Surface *switchmap = TTF_RenderText_Solid( fntsml, map.c_str(), clrFg );
389 	SDL_Surface *help= TTF_RenderText_Solid( fntsml, "<h> to toggle help overlay", clrFg );
390 	SDL_Surface *esc= TTF_RenderText_Solid( fntsml, "<Esc> to quit", clrFg );
391 	string qinfo = "(You are in ";
392 	qinfo.append(quantum ? "quantum" : "classical");
393 	qinfo.append(" mode)");
394 	SDL_Surface *info = TTF_RenderText_Solid( fntsml, qinfo.c_str(), clrFg );
395 	SDL_Surface *instr = TTF_RenderText_Solid( fntsml, "Once the game is running, click, hold and release the left mouse button", clrFg );
396 	SDL_Surface *iinstr = TTF_RenderText_Solid( fntsml, "to kick the ball. Click once more to measure its position.", clrFg );
397 	SDL_Rect rcDest = {150,50,0,0};
398 	SDL_BlitSurface( welcome, NULL, bBuffer,&rcDest );
399 	rcDest.x = 150; rcDest.y += 2*linesep;
400 	SDL_BlitSurface( select, NULL, bBuffer,&rcDest );
401 	rcDest.y += linesep;
402 	SDL_BlitSurface( start, NULL, bBuffer,&rcDest );
403 	rcDest.y += linesep;
404 	SDL_BlitSurface( toggle, NULL, bBuffer,&rcDest );
405 	rcDest.y += linesep;
406 	SDL_BlitSurface( info, NULL, bBuffer,&rcDest );
407 	rcDest.y += linesep;
408 	SDL_BlitSurface( switchmap, NULL, bBuffer,&rcDest );
409 	rcDest.y += linesep;
410 	SDL_BlitSurface( help, NULL, bBuffer,&rcDest );
411 	rcDest.y += linesep;
412 	SDL_BlitSurface( esc, NULL, bBuffer,&rcDest );
413 
414 	rcDest.y = 240;
415 	SDL_BlitSurface( instr, NULL, bBuffer,&rcDest );
416 	rcDest.y += linesep;
417 	SDL_BlitSurface( iinstr, NULL, bBuffer,&rcDest );
418 
419 	SDL_FreeSurface( welcome );
420 	SDL_FreeSurface( select );
421 	SDL_FreeSurface( start );
422 	SDL_FreeSurface( toggle );
423 	SDL_FreeSurface( switchmap );
424 	SDL_FreeSurface( info );
425 	SDL_FreeSurface( help );
426 	SDL_FreeSurface( esc );
427 	SDL_FreeSurface( instr );
428 	SDL_FreeSurface( iinstr );
429 }
430 
431 // Blit
432 // Blit the contents of bBuffer to the screen.
433 // Note that all RenderXXX functions only draw to bBuffer. I.E.
434 // You have to call Blit before anything rendered gets visible
Blit()435 void Renderer::Blit(){
436 	SDL_BlitSurface( bBuffer, NULL, screen, &rBuffer );
437 	SDL_UpdateRect( screen, 0, 0, 0, 0 );
438 }
439 
SaveFrame(const char * fname)440 void Renderer::SaveFrame(const char *fname){
441     SDL_SaveBMP(bBuffer, fname);
442 }
443