removed clang-format and clang_complete files from the repo
[dosdemo] / src / scr / grise.c
1 #include "demo.h"
2 #include "imago2.h"
3 #include "screen.h"
4 #include <assert.h>
5 #include <math.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9
10 #include "rlebmap.h"
11
12 /* APPROX. 170 FPS Minimum */
13
14 static void updatePropeller(float t);
15
16 #define BG_FILENAME "data/grise.png"
17 #define GROBJ_01_FILENAME "data/grobj_01.png"
18
19 #define BB_SIZE 512 /* Let's use a power of 2. Maybe we'll zoom/rotate the effect */
20
21 /* Every backBuffer scanline is guaranteed to have that many dummy pixels before and after */
22 #define PIXEL_PADDING 32
23
24 /* Make sure this is less than PIXEL_PADDING*/
25 #define MAX_DISPLACEMENT 16
26
27 #define MIN_SCROLL PIXEL_PADDING
28 #define MAX_SCROLL (backgroundW - FB_WIDTH - MIN_SCROLL)
29
30 #define FAR_SCROLL_SPEED 15.0f
31 #define NEAR_SCROLL_SPEED 120.0f
32
33 #define HORIZON_HEIGHT 100
34 #define REFLECTION_HEIGHT (240 - HORIZON_HEIGHT)
35
36 #define NORMALMAP_SCANLINE 372
37
38 static int init(void);
39 static void destroy(void);
40 static void start(long trans_time);
41 static void stop(long trans_time);
42 static void draw(void);
43
44 static void convert32To16(unsigned int *src32, unsigned short *dst16, unsigned int pixelCount);
45 static void processNormal();
46 static void initScrollTables();
47 static void updateScrollTables(float dt);
48
49 static unsigned short *background = 0;
50 static int backgroundW = 0;
51 static int backgroundH = 0;
52
53 static unsigned int lastFrameTime = 0;
54 static float lastFrameDuration = 0.0f;
55
56 static short *displacementMap;
57
58 static unsigned short *backBuffer;
59
60 static float scrollScaleTable[REFLECTION_HEIGHT];
61 static float scrollTable[REFLECTION_HEIGHT];
62 static int scrollTableRounded[REFLECTION_HEIGHT];
63 static int scrollModTable[REFLECTION_HEIGHT];
64 static float nearScrollAmount = 0.0f;
65
66 static unsigned char miniFXBuffer[1024];
67
68 static RleBitmap *grobj = 0;
69 static RleBitmap *rlePropeller = 0;
70
71 static struct screen scr = {"galaxyrise", init, destroy, start, 0, draw};
72
73 struct screen *grise_screen(void) {
74         return &scr;
75 }
76
77 static int init(void) {
78         unsigned char *tmpBitmap;
79         int tmpBitmapW, tmpBitmapH;
80
81         /* Allocate back buffer */
82         backBuffer = (unsigned short *)calloc(BB_SIZE * BB_SIZE, sizeof(unsigned short));
83
84         /* grise.png contains the background (horizon), baked reflection and normalmap for
85          * displacement */
86         if (!(background =
87                   img_load_pixels(BG_FILENAME, &backgroundW, &backgroundH, IMG_FMT_RGBA32))) {
88                 fprintf(stderr, "failed to load image " BG_FILENAME "\n");
89                 return -1;
90         }
91
92         /* Convert to 16bpp */
93         convert32To16((unsigned int *)background, background,
94                       backgroundW * NORMALMAP_SCANLINE); /* Normalmap will keep its 32 bit color */
95
96         /* Load reflected objects */
97         if (!(tmpBitmap =
98                   img_load_pixels(GROBJ_01_FILENAME, &tmpBitmapW, &tmpBitmapH, IMG_FMT_GREY8))) {
99                 fprintf(stderr, "failed to load image " GROBJ_01_FILENAME "\n");
100                 return -1;
101         }
102
103         grobj = rleEncode(0, tmpBitmap, tmpBitmapW, tmpBitmapH);
104
105         img_free_pixels(tmpBitmap);
106
107         initScrollTables();
108
109         processNormal();
110
111         return 0;
112 }
113
114 static void destroy(void) {
115         free(backBuffer);
116         backBuffer = 0;
117
118         img_free_pixels(background);
119
120         rleDestroy(grobj);
121 }
122
123 static void start(long trans_time) { lastFrameTime = time_msec; }
124
125 static void draw(void) {
126         int scroll = MIN_SCROLL + (MAX_SCROLL - MIN_SCROLL) * mouse_x / FB_WIDTH;
127         unsigned short *dst = backBuffer + PIXEL_PADDING;
128         unsigned short *src = background + scroll;
129         int scanline = 0;
130         int i = 0;
131         short *dispScanline;
132         int d;
133         int accum = 0;
134         int md, sc;
135         int scrolledIndex;
136
137         lastFrameDuration = (time_msec - lastFrameTime) / 1000.0f;
138         lastFrameTime = time_msec;
139
140         /* Update mini-effects here */
141         updatePropeller(4.0f * time_msec / 1000.0f);
142
143         /* First, render the horizon */
144         for (scanline = 0; scanline < HORIZON_HEIGHT; scanline++) {
145                 memcpy(dst, src, FB_WIDTH * 2);
146                 src += backgroundW;
147                 dst += BB_SIZE;
148         }
149
150         /* Create scroll offsets for all scanlines of the normalmap */
151         updateScrollTables(lastFrameDuration);
152
153         /* Render the baked reflection one scanline below its place, so that
154          * the displacement that follows will be done in a cache-friendly way
155          */
156         src -= PIXEL_PADDING; /* We want to also fill the PADDING pixels here */
157         dst = backBuffer + (HORIZON_HEIGHT + 1) * BB_SIZE;
158         for (scanline = 0; scanline < REFLECTION_HEIGHT; scanline++) {
159                 memcpy(dst, src, (FB_WIDTH + PIXEL_PADDING) * 2);
160                 src += backgroundW;
161                 dst += BB_SIZE;
162         }
163
164         /* Blit reflections first, to be  displaced */
165         for (i = 0; i < 5; i++)
166                 rleBlitScaleInv(rlePropeller, backBuffer + PIXEL_PADDING, FB_WIDTH, FB_HEIGHT,
167                                 BB_SIZE, 134 + (i - 3) * 60, 200, 1.0f, 1.8f);
168
169         /* Perform displacement */
170         dst = backBuffer + HORIZON_HEIGHT * BB_SIZE + PIXEL_PADDING;
171         src = dst + BB_SIZE; /* The pixels to be displaced are 1 scanline below */
172         dispScanline = displacementMap;
173         for (scanline = 0; scanline < REFLECTION_HEIGHT; scanline++) {
174
175                 md = scrollModTable[scanline];
176                 sc = scrollTableRounded[scanline];
177                 accum = 0;
178
179                 for (i = 0; i < FB_WIDTH; i++) {
180                         /* Try to immitate modulo without the division */
181                         if (i == md)
182                                 accum += md;
183                         scrolledIndex = i - accum + sc;
184                         if (scrolledIndex >= md)
185                                 scrolledIndex -= md;
186
187                         /* Displace */
188                         d = dispScanline[scrolledIndex];
189                         *dst++ = src[i + d];
190                 }
191                 src += backgroundW;
192                 dst += BB_SIZE - FB_WIDTH;
193                 dispScanline += backgroundW;
194         }
195
196         /* Then after displacement, blit the objects */
197         for (i = 0; i < 5; i++)
198                 rleBlit(rlePropeller, backBuffer + PIXEL_PADDING, FB_WIDTH, FB_HEIGHT, BB_SIZE,
199                         134 + (i - 3) * 60, 100);
200
201         /* Blit effect to framebuffer */
202         src = backBuffer + PIXEL_PADDING;
203         dst = fb_pixels;
204         for (scanline = 0; scanline < FB_HEIGHT; scanline++) {
205                 memcpy(dst, src, FB_WIDTH * 2);
206                 src += BB_SIZE;
207                 dst += FB_WIDTH;
208         }
209
210         swap_buffers(0);
211 }
212
213 /* src and dst can be the same */
214 static void convert32To16(unsigned int *src32, unsigned short *dst16, unsigned int pixelCount) {
215         unsigned int p;
216         while (pixelCount) {
217                 p = *src32++;
218                 *dst16++ = ((p << 8) & 0xF800)     /* R */
219                            | ((p >> 5) & 0x07E0)   /* G */
220                            | ((p >> 19) & 0x001F); /* B */
221                 pixelCount--;
222         }
223 }
224
225 /* Normal map preprocessing */
226 /* Scale normal with depth and unpack R component (horizontal component) */
227 static void processNormal() {
228         int scanline;
229         int i;
230         int x;
231         short maxDisplacement = 0;
232         short minDisplacement = 256;
233         unsigned short *dst;
234         short *dst2;
235         unsigned int *normalmap = (unsigned int *)background;
236         normalmap += NORMALMAP_SCANLINE * backgroundW;
237         dst = (unsigned short *)normalmap;
238         displacementMap = (short *)dst;
239         dst2 = displacementMap;
240
241         for (scanline = 0; scanline < REFLECTION_HEIGHT; scanline++) {
242                 scrollModTable[scanline] = (int)(backgroundW / scrollScaleTable[scanline] + 0.5f);
243                 for (i = 0; i < backgroundW; i++) {
244                         x = (int)(i * scrollScaleTable[scanline] + 0.5f);
245                         if (x < backgroundW) {
246                                 *dst = (unsigned short)(normalmap[x] >> 8) & 0xFF;
247                                 if ((short)*dst > maxDisplacement)
248                                         maxDisplacement = (short)(*dst);
249                                 if ((short)*dst < minDisplacement)
250                                         minDisplacement = (short)(*dst);
251                         } else {
252                                 *dst = 0;
253                         }
254                         dst++;
255                 }
256                 normalmap += backgroundW;
257         }
258
259         if (maxDisplacement == minDisplacement) {
260                 printf("Warning: grise normalmap fucked up\n");
261                 return;
262         }
263
264         /* Second pass - subtract half maximum displacement to displace in both directions */
265         for (scanline = 0; scanline < REFLECTION_HEIGHT; scanline++) {
266                 for (i = 0; i < backgroundW; i++) {
267                         /* Remember that MIN_SCROLL is the padding around the screen, so ti's the
268                          * maximum displacement we can get (positive & negative) */
269                         *dst2 = 2 * MAX_DISPLACEMENT * (*dst2 - minDisplacement) /
270                                     (maxDisplacement - minDisplacement) -
271                                 MAX_DISPLACEMENT;
272                         *dst2 = (short)((float)*dst2 / scrollScaleTable[scanline] +
273                                         0.5f); /* Displacements must also scale with distance*/
274                         dst2++;
275                 }
276         }
277 }
278
279 static float distanceScale(int scanline) {
280         float farScale, t;
281         farScale = (float)NEAR_SCROLL_SPEED / (float)FAR_SCROLL_SPEED;
282         t = (float)scanline / ((float)REFLECTION_HEIGHT - 1);
283         return 1.0f / (1.0f / farScale + (1.0f - 1.0f / farScale) * t);
284 }
285
286 static void initScrollTables() {
287         int i = 0;
288         for (i = 0; i < REFLECTION_HEIGHT; i++) {
289                 scrollScaleTable[i] = distanceScale(i);
290                 scrollTable[i] = 0.0f;
291                 scrollTableRounded[i] = 0;
292         }
293 }
294
295 static void updateScrollTables(float dt) {
296         int i = 0;
297
298         nearScrollAmount += dt * NEAR_SCROLL_SPEED;
299         nearScrollAmount = (float)fmod(nearScrollAmount, 512.0f);
300
301         for (i = 0; i < REFLECTION_HEIGHT; i++) {
302                 scrollTable[i] = nearScrollAmount / scrollScaleTable[i];
303                 scrollTableRounded[i] = (int)(scrollTable[i] + 0.5f) % scrollModTable[i];
304         }
305 }
306
307 /* -------------------------------------------------------------------------------------------------
308  *                                   PROPELLER STUFF
309  * -------------------------------------------------------------------------------------------------
310  */
311
312 #define PROPELLER_CIRCLE_RADIUS 18
313 #define PROPELLER_CIRCLE_RADIUS_SQ (PROPELLER_CIRCLE_RADIUS * PROPELLER_CIRCLE_RADIUS)
314
315 static struct {
316         int circleX[3];
317         int circleY[3];
318 } propellerState;
319
320 static void updatePropeller(float t) {
321         int i, j;
322         int cx, cy, count = 0;
323         unsigned char *dst;
324         float x = 0.0f;
325         float y = 18.0f;
326         float nx, ny;
327         float cost, sint;
328         static float sin120 = 0.86602540378f;
329         static float cos120 = -0.5f;
330
331         /* Rotate */
332         sint = sin(t);
333         cost = cos(t);
334         nx = x * cost - y * sint;
335         ny = y * cost + x * sint;
336         x = nx;
337         y = ny;
338         propellerState.circleX[0] = (int)(x + 0.5f) + 16;
339         propellerState.circleY[0] = (int)(y + 0.5f) + 16;
340
341         /* Rotate by 120 degrees, for the second circle */
342         nx = x * cos120 - y * sin120;
343         ny = y * cos120 + x * sin120;
344         x = nx;
345         y = ny;
346         propellerState.circleX[1] = (int)(x + 0.5f) + 16;
347         propellerState.circleY[1] = (int)(y + 0.5f) + 16;
348
349         /* 3rd circle */
350         nx = x * cos120 - y * sin120;
351         ny = y * cos120 + x * sin120;
352         x = nx;
353         y = ny;
354         propellerState.circleX[2] = (int)(x + 0.5f) + 16;
355         propellerState.circleY[2] = (int)(y + 0.5f) + 16;
356
357         /* Write effect to the mini fx buffer*/
358         dst = miniFXBuffer;
359         for (j = 0; j < 32; j++) {
360                 for (i = 0; i < 32; i++) {
361                         count = 0;
362
363                         /* First circle */
364                         cx = propellerState.circleX[0] - i;
365                         cy = propellerState.circleY[0] - j;
366                         if (cx * cx + cy * cy < PROPELLER_CIRCLE_RADIUS_SQ)
367                                 count++;
368
369                         /* 2nd circle */
370                         cx = propellerState.circleX[1] - i;
371                         cy = propellerState.circleY[1] - j;
372                         if (cx * cx + cy * cy < PROPELLER_CIRCLE_RADIUS_SQ)
373                                 count++;
374
375                         /* 3rd circle */
376                         cx = propellerState.circleX[2] - i;
377                         cy = propellerState.circleY[2] - j;
378                         if (cx * cx + cy * cy < PROPELLER_CIRCLE_RADIUS_SQ)
379                                 count++;
380
381                         *dst++ = count >= 2;
382                 }
383         }
384
385         /* Then, encode to rle */
386         rlePropeller = rleEncode(rlePropeller, miniFXBuffer, 32, 32);
387
388         /* Distribute the produced streaks so that they don't produce garbage when interpolated */
389         rleDistributeStreaks(rlePropeller);
390 }