Return 0xCAFE from screen.init() to force display the screen first
[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 BB_SIZE 512     /* Let's use a power of 2. Maybe we'll zoom/rotate the effect */
15
16 /* Every backBuffer scanline is guaranteed to have that many dummy pixels before and after */
17 #define PIXEL_PADDING 32
18
19 #define MIN_SCROLL PIXEL_PADDING
20 #define MAX_SCROLL (backgroundW - fb_width - MIN_SCROLL)
21
22 #define FAR_SCROLL_SPEED 50.0f
23 #define NEAR_SCROLL_SPEED 400.0f
24
25 #define HORIZON_HEIGHT 100
26 #define REFLECTION_HEIGHT (240 - HORIZON_HEIGHT)
27
28 #define NORMALMAP_SCANLINE 372
29
30 static int init(void);
31 static void destroy(void);
32 static void start(long trans_time);
33 static void stop(long trans_time);
34 static void draw(void);
35
36 static void convert32To16(unsigned int *src32, unsigned short *dst16, unsigned int pixelCount);
37 static void processNormal();
38 static void initScrollTables();
39 static void updateScrollTables(float dt);
40
41 static unsigned short *background = 0;
42 static unsigned int backgroundW = 0;
43 static unsigned int backgroundH = 0;
44
45 static unsigned int lastFrameTime = 0;
46 static float lastFrameDuration = 0.0f;
47
48 static short *displacementMap;
49
50 static unsigned short *backBuffer;
51
52 static float scrollScaleTable[REFLECTION_HEIGHT];
53 static float scrollTable[REFLECTION_HEIGHT];
54 static int scrollTableRounded[REFLECTION_HEIGHT];
55 static int scrollModTable[REFLECTION_HEIGHT];
56 static float nearScrollAmount = 0.0f;
57
58 static struct screen scr = {
59         "mike",
60         init,
61         destroy,
62         start,
63         stop,
64         draw
65 };
66
67 struct screen *mike_screen(void)
68 {
69         return &scr;
70 }
71
72
73 static int init(void)
74 {
75         /* Allocate back buffer */
76         backBuffer = (unsigned short*) malloc(BB_SIZE * BB_SIZE * sizeof(unsigned short));
77
78         if (!(background = img_load_pixels(BG_FILENAME, &backgroundW, &backgroundH, IMG_FMT_RGBA32))) {
79                 fprintf(stderr, "failed to load image " BG_FILENAME "\n");
80                 return -1;
81         }
82
83         /* Convert to 16bpp */
84         convert32To16((unsigned int*)background, background, backgroundW * NORMALMAP_SCANLINE); /* Normalmap will keep its 32 bit color */
85
86         initScrollTables();
87
88         processNormal();
89
90 #ifdef MIKE_PC
91         return 0xCAFE;
92 #else
93         return 0;
94 #endif
95 }
96
97 static void destroy(void)
98 {
99         free(backBuffer);
100         backBuffer = 0;
101
102         img_free_pixels(background);
103 }
104
105 static void start(long trans_time)
106 {
107         lastFrameTime = time_msec;
108 }
109
110 static void stop(long trans_time)
111 {
112 }
113
114 static void draw(void)
115 {       
116         int scroll = MIN_SCROLL + (MAX_SCROLL - MIN_SCROLL) * mouse_x / fb_width;
117         unsigned short *dst = backBuffer + PIXEL_PADDING;
118         unsigned short *src = background + scroll;
119         int scanline = 0;
120         int i = 0;
121         short *dispScanline;
122         int d;
123
124         lastFrameDuration = (time_msec - lastFrameTime) / 1000.0f;
125         lastFrameTime = time_msec;
126
127         /* First, render the horizon */
128         for (scanline = 0; scanline < HORIZON_HEIGHT; scanline++) {
129                 memcpy(dst, src, fb_width * 2);
130                 src += backgroundW;
131                 dst += BB_SIZE;
132         }
133         
134         /* Create scroll opffsets for all scanlines of the normalmap */
135         updateScrollTables(lastFrameDuration);
136
137         /* Then, render the reflection under the horizon */
138         /* dst is already in place */
139         src = background + HORIZON_HEIGHT * backgroundW;
140         dispScanline = displacementMap;
141         for (scanline = 0; scanline < REFLECTION_HEIGHT; scanline++) {
142                 for (i = 0; i < fb_width; i++) {
143                         d = dispScanline[(i + scrollTableRounded[scanline]) % scrollModTable[scanline]];
144                         *dst++ = src[i + scroll + d];
145                 }
146                 src += backgroundW;
147                 dst += BB_SIZE - fb_width;
148                 dispScanline += backgroundW;
149         }
150
151         /* Blit effect to framebuffer */
152         src = backBuffer + PIXEL_PADDING;
153         dst = fb_pixels;
154         for (scanline = 0; scanline < fb_height; scanline++) {
155                 memcpy(dst, src, fb_width * 2);
156                 src += BB_SIZE;
157                 dst += fb_width;
158         }
159 }
160
161 /* src and dst can be the same */
162 static void convert32To16(unsigned int *src32, unsigned short *dst16, unsigned int pixelCount) {
163         unsigned int p;
164         while (pixelCount) {
165                 p = *src32++;
166                 *dst16++ =      ((p << 8) & 0xF800)             /* R */
167                         |               ((p >> 5) & 0x07E0)             /* G */
168                         |               ((p >> 19) & 0x001F);   /* B */
169                 pixelCount--;
170         }
171 }
172
173 /* Normal map preprocessing */
174 /* Scale normal with depth and unpack R component (horizontal component) */
175 static void processNormal() {
176         int scanline;
177         int i;
178         int x;
179         short maxDisplacement = 0;
180         short minDisplacement = 256;
181         unsigned short *dst;
182         short *dst2;
183         unsigned int *normalmap = (unsigned int*)background;
184         normalmap += NORMALMAP_SCANLINE * backgroundW;
185         dst = (unsigned short*)normalmap;
186         displacementMap = (short*)dst;
187         dst2 = displacementMap;
188
189         for (scanline = 0; scanline < REFLECTION_HEIGHT; scanline++) {
190                 scrollModTable[scanline] = (int) (backgroundW / scrollScaleTable[scanline] + 0.5f);
191                 for (i = 0; i < backgroundW; i++) {
192                         x = (int)(i * scrollScaleTable[scanline] + 0.5f);
193                         if (x < backgroundW) {
194                                 *dst = (unsigned short)(normalmap[x] >> 8) & 0xFF;
195                                 if ((short)*dst > maxDisplacement) maxDisplacement = (short)(*dst);
196                                 if ((short)*dst < minDisplacement) minDisplacement = (short)(*dst);
197                         } else {
198                                 *dst = 0;
199                         }
200                         dst++;
201                 }
202                 normalmap += backgroundW;
203         }
204
205         if (maxDisplacement == minDisplacement) {
206                 printf("Warning: grise normalmap fucked up\n");
207                 return;
208         }
209
210         /* Second pass - subtract half maximum displacement to displace in both directions */
211         for (scanline = 0; scanline < REFLECTION_HEIGHT; scanline++) {
212                 for (i = 0; i < backgroundW; i++) {
213                         /* Remember that MIN_SCROLL is the padding around the screen, so ti's the maximum displacement we can get (positive & negative) */
214                         *dst2 = 2 * MIN_SCROLL * (*dst2 - minDisplacement) / (maxDisplacement - minDisplacement) - MIN_SCROLL;
215                         *dst2 = (short)((float)*dst2 / scrollScaleTable[scanline] + 0.5f); /* Displacements must also scale with distance*/
216                         dst2++;
217                 }
218         }
219 }
220
221 static float distanceScale(int scanline) {
222         float farScale, t;
223         farScale = (float)NEAR_SCROLL_SPEED / (float)FAR_SCROLL_SPEED;
224         t = (float)scanline / ((float)REFLECTION_HEIGHT - 1);
225         return 1.0f / (1.0f / farScale + (1.0f - 1.0f / farScale) * t);
226 }
227
228 static void initScrollTables() {
229         int i = 0;
230         for (i = 0; i < REFLECTION_HEIGHT; i++) {
231                 scrollScaleTable[i] = distanceScale(i);
232                 scrollTable[i] = 0.0f;
233                 scrollTableRounded[i] = 0;
234         }
235 }
236
237
238 static void updateScrollTables(float dt) {
239         int i = 0;
240         
241         nearScrollAmount += dt * NEAR_SCROLL_SPEED;
242         nearScrollAmount = (float) fmod(nearScrollAmount, 512.0f);
243
244         for (i = 0; i < REFLECTION_HEIGHT; i++) {
245                 scrollTable[i] = nearScrollAmount / scrollScaleTable[i];
246                 scrollTableRounded[i] = (int)(scrollTable[i] + 0.5f) % scrollModTable[i];
247         }
248 }