Various reflection improvements
[dosdemo] / src / mike.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <math.h>
5 #include <assert.h>
6 #include "imago2.h"
7 #include "demo.h"
8 #include "screen.h"
9
10 /* APPROX. 170 FPS Minimum */
11
12 #define BG_FILENAME "data/grise.png"
13
14 #define MIN_SCROLL 32
15 #define MAX_SCROLL (backgroundW - fb_width - MIN_SCROLL)
16
17 #define FAR_SCROLL_SPEED 50.0f
18 #define NEAR_SCROLL_SPEED 400.0f
19
20 #define HORIZON_HEIGHT 100
21 #define REFLECTION_HEIGHT (240 - HORIZON_HEIGHT)
22
23 #define NORMALMAP_SCANLINE 372
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 convert32To16(unsigned int *src32, unsigned short *dst16, unsigned int pixelCount);
32 static void processNormal();
33 static void initScrollTables();
34 static void updateScrollTables(float dt);
35
36 static unsigned short *background = 0;
37 static unsigned int backgroundW = 0;
38 static unsigned int backgroundH = 0;
39
40 static unsigned int lastFrameTime = 0;
41 static float lastFrameDuration = 0.0f;
42
43 static short *displacementMap;
44
45 static float scrollScaleTable[REFLECTION_HEIGHT];
46 static float scrollTable[REFLECTION_HEIGHT];
47 static int scrollTableRounded[REFLECTION_HEIGHT];
48 static int scrollModTable[REFLECTION_HEIGHT];
49 static float nearScrollAmount = 0.0f;
50
51 static struct screen scr = {
52         "mike",
53         init,
54         destroy,
55         start,
56         stop,
57         draw
58 };
59
60 struct screen *mike_screen(void)
61 {
62         return &scr;
63 }
64
65
66 static int init(void)
67 {
68         if (!(background = img_load_pixels(BG_FILENAME, &backgroundW, &backgroundH, IMG_FMT_RGBA32))) {
69                 fprintf(stderr, "failed to load image " BG_FILENAME "\n");
70                 return -1;
71         }
72
73         /* Convert to 16bpp */
74         convert32To16((unsigned int*)background, background, backgroundW * NORMALMAP_SCANLINE); /* Normalmap will keep its 32 bit color */
75
76         initScrollTables();
77
78         processNormal();
79
80         return 0;
81 }
82
83 static void destroy(void)
84 {
85         //img_free_pixels(background);
86 }
87
88 static void start(long trans_time)
89 {
90         lastFrameTime = time_msec;
91 }
92
93 static void stop(long trans_time)
94 {
95 }
96
97 static void draw(void)
98 {       
99         int scroll = MIN_SCROLL + (MAX_SCROLL - MIN_SCROLL) * mouse_x / fb_width;
100         unsigned short *dst = fb_pixels;
101         unsigned short *src = background + scroll;
102         int scanline = 0;
103         int i = 0;
104         short *disp;
105         int d;
106
107         lastFrameDuration = (time_msec - lastFrameTime) / 1000.0f;
108         lastFrameTime = time_msec;
109
110         for (scanline = 0; scanline < fb_height; scanline++) {
111                 memcpy(dst, src, fb_width * 2);
112                 src += backgroundW;
113                 dst += fb_width;
114         }
115         
116         updateScrollTables(lastFrameDuration);
117
118         dst = (unsigned short*) fb_pixels + HORIZON_HEIGHT * fb_width;
119         src = background + HORIZON_HEIGHT * backgroundW;
120         disp = displacementMap;
121         for (scanline = 0; scanline < REFLECTION_HEIGHT; scanline++) {
122                 for (i = 0; i < fb_width; i++) {
123                         d = disp[(i + scrollTableRounded[scanline]) % scrollModTable[scanline]];
124                         *dst++ = src[i + scroll + d];
125                 }
126                 src += backgroundW;
127                 disp += backgroundW;
128         }
129 }
130
131 /* src and dst can be the same */
132 static void convert32To16(unsigned int *src32, unsigned short *dst16, unsigned int pixelCount) {
133         unsigned int p;
134         while (pixelCount) {
135                 p = *src32++;
136                 *dst16++ =      ((p << 8) & 0xF800)             /* R */
137                         |               ((p >> 5) & 0x07E0)             /* G */
138                         |               ((p >> 19) & 0x001F);   /* B */
139                 pixelCount--;
140         }
141 }
142
143 /* Normal map preprocessing */
144 /* Scale normal with depth and unpack R component (horizontal component) */
145 static void processNormal() {
146         int scanline;
147         int i;
148         int x;
149         short maxDisplacement = 0;
150         short minDisplacement = 256;
151         unsigned short *dst;
152         short *dst2;
153         unsigned int *normalmap = (unsigned int*)background;
154         normalmap += NORMALMAP_SCANLINE * backgroundW;
155         dst = (unsigned short*)normalmap;
156         displacementMap = (short*)dst;
157         dst2 = displacementMap;
158
159         for (scanline = 0; scanline < REFLECTION_HEIGHT; scanline++) {
160                 scrollModTable[scanline] = (int) (backgroundW / scrollScaleTable[scanline] + 0.5f);
161                 for (i = 0; i < backgroundW; i++) {
162                         x = (int)(i * scrollScaleTable[scanline] + 0.5f);
163                         if (x < backgroundW) {
164                                 *dst = (unsigned short)(normalmap[x] >> 8) & 0xFF;
165                                 if ((short)*dst > maxDisplacement) maxDisplacement = (short)(*dst);
166                                 if ((short)*dst < minDisplacement) minDisplacement = (short)(*dst);
167                         } else {
168                                 *dst = 0;
169                         }
170                         dst++;
171                 }
172                 normalmap += backgroundW;
173         }
174
175         if (maxDisplacement == minDisplacement) {
176                 printf("Warning: grise normalmap fucked up\n");
177                 return;
178         }
179
180         /* Second pass - subtract half maximum displacement to displace in both directions */
181         for (scanline = 0; scanline < REFLECTION_HEIGHT; scanline++) {
182                 for (i = 0; i < backgroundW; i++) {
183                         /* Remember that MIN_SCROLL is the padding around the screen, so ti's the maximum displacement we can get (positive & negative) */
184                         *dst2 = 2 * MIN_SCROLL * (*dst2 - minDisplacement) / (maxDisplacement - minDisplacement) - MIN_SCROLL;
185                         *dst2 = (short)((float)*dst2 / scrollScaleTable[scanline] + 0.5f); /* Displacements must also scale with distance*/
186                         dst2++;
187                 }
188         }
189 }
190
191 static float distanceScale(int scanline) {
192         float farScale, t;
193         farScale = (float)NEAR_SCROLL_SPEED / (float)FAR_SCROLL_SPEED;
194         t = (float)scanline / ((float)REFLECTION_HEIGHT - 1);
195         return 1.0f / (1.0f / farScale + (1.0f - 1.0f / farScale) * t);
196 }
197
198 static void initScrollTables() {
199         int i = 0;
200         for (i = 0; i < REFLECTION_HEIGHT; i++) {
201                 scrollScaleTable[i] = distanceScale(i);
202                 scrollTable[i] = 0.0f;
203                 scrollTableRounded[i] = 0;
204         }
205 }
206
207
208 static void updateScrollTables(float dt) {
209         int i = 0;
210         
211         nearScrollAmount += dt * NEAR_SCROLL_SPEED;
212         nearScrollAmount = (float) fmod(nearScrollAmount, 512.0f);
213
214         for (i = 0; i < REFLECTION_HEIGHT; i++) {
215                 scrollTable[i] = nearScrollAmount / scrollScaleTable[i];
216                 scrollTableRounded[i] = (int)(scrollTable[i] + 0.5f) % scrollModTable[i];
217         }
218 }