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