unknown changes
[fbgfx] / src / tunnel.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <math.h>
4 #include <assert.h>
5 #include <imago2.h>
6 #include "tpool.h"
7 #include "demo.h"
8 #include "screen.h"
9
10 #define VSCALE  1.5
11
12 #define TEX_FNAME       "data/grid.png"
13 #define TEX_USCALE      4
14 #define TEX_VSCALE      2
15
16 #define NUM_WORK_ITEMS  8
17
18 static struct work {
19         void *pixels;
20         int starty, num_lines;
21         long tm;
22         int xoffs, yoffs;
23 } work[NUM_WORK_ITEMS];
24
25 static int init(void);
26 static void destroy(void);
27 static void start(long trans_time);
28 static void stop(long trans_time);
29 static void draw(void);
30
31 static void (*draw_tunnel_range)(void*, int, int, int, int, long);
32
33 static void draw_tunnel_range16(void *pixels, int xoffs, int yoffs, int starty, int num_lines, long tm);
34 static void draw_tunnel_range32(void *pixels, int xoffs, int yoffs, int starty, int num_lines, long tm);
35 static int count_bits(unsigned int x);
36 static int count_zeros(unsigned int x);
37
38 static struct screen scr = {
39         "tunnel",
40         init,
41         destroy,
42         start,
43         stop,
44         draw
45 };
46
47 static int xsz, ysz, vxsz, vysz;
48 static int pan_width, pan_height;
49 static unsigned int *tunnel_map;
50 static unsigned char *tunnel_fog;
51
52 static int tex_xsz, tex_ysz;
53 static unsigned int *tex_pixels;
54 static int tex_xshift, tex_yshift;
55 static unsigned int tex_xmask, tex_ymask;
56
57 static struct thread_pool *tpool;
58
59 static long trans_start, trans_dur;
60 static int trans_dir;
61
62
63 struct screen *tunnel_screen(void)
64 {
65         return &scr;
66 }
67
68
69 static int init(void)
70 {
71         int i, j, n;
72         unsigned int *tmap;
73         unsigned char *fog;
74         float aspect = (float)fb_width / (float)fb_height;
75
76         switch(fb_depth) {
77         case 16:
78                 draw_tunnel_range = draw_tunnel_range16;
79                 break;
80         case 32:
81                 draw_tunnel_range = draw_tunnel_range32;
82                 break;
83         default:
84                 fprintf(stderr, "unsupported color depth: %d\n", fb_depth);
85                 return -1;
86         }
87
88         xsz = fb_width / 2;
89         ysz = fb_height;
90         vxsz = xsz * VSCALE;
91         vysz = ysz * VSCALE;
92
93         pan_width = vxsz - xsz;
94         pan_height = vysz - ysz;
95
96         if(!(tunnel_map = malloc(vxsz * vysz * sizeof *tunnel_map))) {
97                 fprintf(stderr, "failed to allocate tunnel map\n");
98                 return -1;
99         }
100         if(!(tunnel_fog = malloc(vxsz * vysz))) {
101                 fprintf(stderr, "failed to allocate tunnel fog map\n");
102                 return -1;
103         }
104
105         tmap = tunnel_map;
106         fog = tunnel_fog;
107
108         for(i=0; i<vysz; i++) {
109                 float y = 2.0 * (float)i / (float)vysz - 1.0;
110                 for(j=0; j<vxsz; j++) {
111                         float x = aspect * (2.0 * (float)j / (float)vxsz - 1.0);
112                         float tu = atan2(y, x) / M_PI * 0.5 + 0.5;
113                         float d = sqrt(x * x + y * y);
114                         float tv = d == 0.0 ? 0.0 : 1.0 / d;
115
116                         int tx = (int)(tu * 65535.0 * TEX_USCALE) & 0xffff;
117                         int ty = (int)(tv * 65535.0 * TEX_VSCALE) & 0xffff;
118
119                         int f = (int)(d * 128.0);
120
121                         *tmap++ = (tx << 16) | ty;
122                         *fog++ = f > 255 ? 255 : f;
123                 }
124         }
125
126         if(!(tex_pixels = img_load_pixels(TEX_FNAME, &tex_xsz, &tex_ysz, IMG_FMT_RGBA32))) {
127                 fprintf(stderr, "failed to load image " TEX_FNAME "\n");
128                 return -1;
129         }
130         if((count_bits(tex_xsz) | count_bits(tex_ysz)) != 1) {
131                 fprintf(stderr, "non-pow2 image (%dx%d)\n", tex_xsz, tex_ysz);
132                 return -1;
133         }
134
135         n = count_zeros(tex_xsz);
136         for(i=0; i<n; i++) {
137                 tex_xmask |= 1 << i;
138         }
139         tex_xshift = n;
140
141         n = count_zeros(tex_ysz);
142         for(i=0; i<n; i++) {
143                 tex_ymask |= 1 << i;
144         }
145         tex_yshift = n;
146
147         if(!(tpool = tpool_create(0))) {
148                 fprintf(stderr, "failed to create thread pool\n");
149                 return -1;
150         }
151
152         /* initialize the constant part of all work items */
153         for(i=0; i<NUM_WORK_ITEMS; i++) {
154                 int num_lines = ysz / NUM_WORK_ITEMS;
155                 work[i].pixels = fb_pixels;
156                 work[i].starty = i * num_lines;
157         }
158
159         return 0;
160 }
161
162 static void destroy(void)
163 {
164         tpool_destroy(tpool);
165         free(tunnel_map);
166         free(tunnel_fog);
167 }
168
169 static void start(long trans_time)
170 {
171         if(trans_time) {
172                 trans_start = time_msec;
173                 trans_dur = trans_time;
174                 trans_dir = 1;
175         }
176 }
177
178 static void stop(long trans_time)
179 {
180         if(trans_time) {
181                 trans_start = time_msec;
182                 trans_dur = trans_time;
183                 trans_dir = -1;
184         }
185 }
186
187
188 static void work_func(void *cls)
189 {
190         struct work *w = (struct work*)cls;
191         draw_tunnel_range(w->pixels, w->xoffs, w->yoffs, w->starty, w->num_lines, w->tm);
192 }
193
194 static void draw(void)
195 {
196         int i, num_lines = ysz / NUM_WORK_ITEMS;
197         int draw_lines = num_lines;
198         float t;
199         int xoffs, yoffs;
200
201         if(trans_dir) {
202                 long interval = time_msec - trans_start;
203                 int progr = num_lines * interval / trans_dur;
204                 if(trans_dir < 0) {
205                         draw_lines = num_lines - progr - 1;
206                 } else {
207                         draw_lines = progr;
208                 }
209                 if(progr >= num_lines) {
210                         trans_dir = 0;
211                 }
212         }
213
214         t = time_msec / 10000.0;
215         xoffs = (int)(cos(t * 3.0) * pan_width / 2) + pan_width / 2;
216         yoffs = (int)(sin(t * 4.0) * pan_height / 2) + pan_height / 2;
217
218         for(i=0; i<NUM_WORK_ITEMS; i++) {
219                 work[i].num_lines = draw_lines;
220                 work[i].tm = time_msec;
221                 work[i].xoffs = xoffs;
222                 work[i].yoffs = yoffs;
223
224                 tpool_enqueue(tpool, work + i, work_func, 0);
225         }
226         tpool_wait(tpool);
227 }
228
229 static void tunnel_color(int *rp, int *gp, int *bp, long toffs, unsigned int tpacked, int fog)
230 {
231         int r, g, b;
232         unsigned int col;
233         unsigned int tx = (((tpacked >> 16) & 0xffff) << tex_xshift) >> 16;
234         unsigned int ty = ((tpacked & 0xffff) << tex_yshift) >> 16;
235         tx += toffs;
236         ty += toffs << 1;
237
238         tx &= tex_xmask;
239         ty &= tex_ymask;
240
241         col = tex_pixels[(ty << tex_xshift) + tx];
242         r = col & 0xff;
243         g = (col >> 8) & 0xff;
244         b = (col >> 16) & 0xff;
245
246         *rp = (r * fog) >> 8;
247         *gp = (g * fog) >> 8;
248         *bp = (b * fog) >> 8;
249 }
250
251 #define PACK_RGB16(r, g, b) \
252         (((((r) >> 3) & 0x1f) << 11) | ((((g) >> 2) & 0x3f) << 5) | (((b) >> 3) & 0x1f))
253 #define PACK_RGB32(r, g, b) \
254         ((((r) & 0xff) << 16) | (((g) & 0xff) << 8) | ((b) & 0xff))
255
256 static void draw_tunnel_range16(void *pix, int xoffs, int yoffs, int starty, int num_lines, long tm)
257 {
258         int i, j;
259         unsigned int *tmap = tunnel_map + (starty + yoffs) * vxsz + xoffs;
260         unsigned char *fog = tunnel_fog + (starty + yoffs) * vxsz + xoffs;
261
262         long toffs = tm / 4;
263         unsigned int *pixels = (unsigned int*)pix + starty * (fb_width >> 1);
264
265         for(i=0; i<num_lines; i++) {
266                 for(j=0; j<xsz; j++) {
267                         unsigned int col;
268                         int r, g, b;
269
270                         tunnel_color(&r, &g, &b, toffs, tmap[j], fog[j]);
271                         col = PACK_RGB16(r, g, b);
272                         *pixels++ = (col << 16) | col;
273                 }
274                 tmap += vxsz;
275                 fog += vxsz;
276         }
277 }
278
279 static void draw_tunnel_range32(void *pix, int xoffs, int yoffs, int starty, int num_lines, long tm)
280 {
281         int i, j;
282         unsigned int *tmap = tunnel_map + (starty + yoffs) * vxsz + xoffs;
283         unsigned char *fog = tunnel_fog + (starty + yoffs) * vxsz + xoffs;
284
285         long toffs = tm / 4;
286         unsigned int *pixels = (unsigned int*)pix + starty * fb_width;
287
288         for(i=0; i<num_lines; i++) {
289                 for(j=0; j<xsz; j++) {
290                         unsigned int col;
291                         int r, g, b;
292
293                         tunnel_color(&r, &g, &b, toffs, tmap[j], fog[j]);
294                         col = PACK_RGB32(r, g, b);
295
296                         *pixels++ = col;
297                         *pixels++ = col;
298                 }
299                 tmap += vxsz;
300                 fog += vxsz;
301         }
302 }
303
304 static int count_bits(unsigned int x)
305 {
306         int i, nbits = 0;
307         for(i=0; i<32; i++) {
308                 if(x & 1) ++nbits;
309                 x >>= 1;
310         }
311         return nbits;
312 }
313
314 static int count_zeros(unsigned int x)
315 {
316         int i, num = 0;
317         for(i=0; i<32; i++) {
318                 if(x & 1) break;
319                 ++num;
320                 x >>= 1;
321         }
322         return num;
323 }