539c06d731608a14e236838f4b8672c41b37e789
[dosdemo] / src / grise.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 #define GROBJ_01_FILENAME "data/grobj_01.png"
14
15 #define BB_SIZE 512     /* Let's use a power of 2. Maybe we'll zoom/rotate the effect */
16
17 /* Every backBuffer scanline is guaranteed to have that many dummy pixels before and after */
18 #define PIXEL_PADDING 32
19
20 #define MIN_SCROLL PIXEL_PADDING
21 #define MAX_SCROLL (backgroundW - fb_width - MIN_SCROLL)
22
23 #define FAR_SCROLL_SPEED 50.0f
24 #define NEAR_SCROLL_SPEED 400.0f
25
26 #define HORIZON_HEIGHT 100
27 #define REFLECTION_HEIGHT (240 - HORIZON_HEIGHT)
28
29 #define NORMALMAP_SCANLINE 372
30
31 static int init(void);
32 static void destroy(void);
33 static void start(long trans_time);
34 static void stop(long trans_time);
35 static void draw(void);
36
37 static void convert32To16(unsigned int *src32, unsigned short *dst16, unsigned int pixelCount);
38 static void processNormal();
39 static void initScrollTables();
40 static void updateScrollTables(float dt);
41
42 static void rleEncode(unsigned char *pixels, unsigned int w, unsigned int h);
43
44 static unsigned short *background = 0;
45 static unsigned int backgroundW = 0;
46 static unsigned int backgroundH = 0;
47
48 static unsigned int lastFrameTime = 0;
49 static float lastFrameDuration = 0.0f;
50
51 static short *displacementMap;
52
53 static unsigned short *backBuffer;
54
55 static float scrollScaleTable[REFLECTION_HEIGHT];
56 static float scrollTable[REFLECTION_HEIGHT];
57 static int scrollTableRounded[REFLECTION_HEIGHT];
58 static int scrollModTable[REFLECTION_HEIGHT];
59 static float nearScrollAmount = 0.0f;
60
61 static struct screen scr = {
62         "galaxyrise",
63         init,
64         destroy,
65         start,
66         stop,
67         draw
68 };
69
70 struct screen *grise_screen(void)
71 {
72         return &scr;
73 }
74
75
76 static int init(void)
77 {
78         unsigned char *reflectedObject;
79         int reflectedObjectW, reflectedObjectH;
80
81         /* Allocate back buffer */
82         backBuffer = (unsigned short*) malloc(BB_SIZE * BB_SIZE * sizeof(unsigned short));
83
84         /* grise.png contains the background (horizon), baked reflection and normalmap for displacement */
85         if (!(background = img_load_pixels(BG_FILENAME, &backgroundW, &backgroundH, IMG_FMT_RGBA32))) {
86                 fprintf(stderr, "failed to load image " BG_FILENAME "\n");
87                 return -1;
88         }
89
90         /* Convert to 16bpp */
91         convert32To16((unsigned int*)background, background, backgroundW * NORMALMAP_SCANLINE); /* Normalmap will keep its 32 bit color */
92
93         /* Load reflected objects */
94         if (!(reflectedObject = img_load_pixels(GROBJ_01_FILENAME, &reflectedObjectW, &reflectedObjectH, IMG_FMT_GREY8))) {
95                 fprintf(stderr, "failed to load image " GROBJ_01_FILENAME "\n");
96                 return -1;
97         }
98
99         rleEncode(reflectedObject, reflectedObjectW, reflectedObjectH);
100
101         img_free_pixels(reflectedObject);
102
103         initScrollTables();
104
105         processNormal();
106
107 #ifdef MIKE_PC
108         return 0xCAFE;
109 #else
110         return 0;
111 #endif
112 }
113
114 static void destroy(void)
115 {
116         free(backBuffer);
117         backBuffer = 0;
118
119         img_free_pixels(background);
120 }
121
122 static void start(long trans_time)
123 {
124         lastFrameTime = time_msec;
125 }
126
127 static void stop(long trans_time)
128 {
129 }
130
131 static void draw(void)
132 {       
133         int scroll = MIN_SCROLL + (MAX_SCROLL - MIN_SCROLL) * mouse_x / fb_width;
134         unsigned short *dst = backBuffer + PIXEL_PADDING;
135         unsigned short *src = background + scroll;
136         int scanline = 0;
137         int i = 0;
138         short *dispScanline;
139         int d;
140
141         lastFrameDuration = (time_msec - lastFrameTime) / 1000.0f;
142         lastFrameTime = time_msec;
143
144         /* First, render the horizon */
145         for (scanline = 0; scanline < HORIZON_HEIGHT; scanline++) {
146                 memcpy(dst, src, fb_width * 2);
147                 src += backgroundW;
148                 dst += BB_SIZE;
149         }
150         
151         /* Create scroll offsets for all scanlines of the normalmap */
152         updateScrollTables(lastFrameDuration);
153
154         /* Render the baked reflection one scanline below its place, so that 
155          * the displacement that follows will be done in a cache-friendly way
156          */
157         src -= PIXEL_PADDING; /* We want to also fill the PADDING pixels here */
158         dst = backBuffer + (HORIZON_HEIGHT + 1) * BB_SIZE;
159         for (scanline = 0; scanline < REFLECTION_HEIGHT; scanline++) {
160                 memcpy(dst, src, (fb_width + PIXEL_PADDING) * 2);
161                 src += backgroundW;
162                 dst += BB_SIZE;
163         }
164         
165         /* Perform displacement */
166         dst = backBuffer + HORIZON_HEIGHT * BB_SIZE + PIXEL_PADDING;
167         src = dst + BB_SIZE; /* The pixels to be displaced are 1 scanline below */
168         dispScanline = displacementMap;
169         for (scanline = 0; scanline < REFLECTION_HEIGHT; scanline++) {
170                 for (i = 0; i < fb_width; i++) {
171                         d = dispScanline[(i + scrollTableRounded[scanline]) % scrollModTable[scanline]];
172                         *dst++ = src[i + d];
173                 }
174                 src += backgroundW;
175                 dst += BB_SIZE - fb_width;
176                 dispScanline += backgroundW;
177         }
178
179         /* Blit effect to framebuffer */
180         src = backBuffer + PIXEL_PADDING;
181         dst = fb_pixels;
182         for (scanline = 0; scanline < fb_height; scanline++) {
183                 memcpy(dst, src, fb_width * 2);
184                 src += BB_SIZE;
185                 dst += fb_width;
186         }
187 }
188
189 /* src and dst can be the same */
190 static void convert32To16(unsigned int *src32, unsigned short *dst16, unsigned int pixelCount) {
191         unsigned int p;
192         while (pixelCount) {
193                 p = *src32++;
194                 *dst16++ =      ((p << 8) & 0xF800)             /* R */
195                         |               ((p >> 5) & 0x07E0)             /* G */
196                         |               ((p >> 19) & 0x001F);   /* B */
197                 pixelCount--;
198         }
199 }
200
201 /* Normal map preprocessing */
202 /* Scale normal with depth and unpack R component (horizontal component) */
203 static void processNormal() {
204         int scanline;
205         int i;
206         int x;
207         short maxDisplacement = 0;
208         short minDisplacement = 256;
209         unsigned short *dst;
210         short *dst2;
211         unsigned int *normalmap = (unsigned int*)background;
212         normalmap += NORMALMAP_SCANLINE * backgroundW;
213         dst = (unsigned short*)normalmap;
214         displacementMap = (short*)dst;
215         dst2 = displacementMap;
216
217         for (scanline = 0; scanline < REFLECTION_HEIGHT; scanline++) {
218                 scrollModTable[scanline] = (int) (backgroundW / scrollScaleTable[scanline] + 0.5f);
219                 for (i = 0; i < backgroundW; i++) {
220                         x = (int)(i * scrollScaleTable[scanline] + 0.5f);
221                         if (x < backgroundW) {
222                                 *dst = (unsigned short)(normalmap[x] >> 8) & 0xFF;
223                                 if ((short)*dst > maxDisplacement) maxDisplacement = (short)(*dst);
224                                 if ((short)*dst < minDisplacement) minDisplacement = (short)(*dst);
225                         } else {
226                                 *dst = 0;
227                         }
228                         dst++;
229                 }
230                 normalmap += backgroundW;
231         }
232
233         if (maxDisplacement == minDisplacement) {
234                 printf("Warning: grise normalmap fucked up\n");
235                 return;
236         }
237
238         /* Second pass - subtract half maximum displacement to displace in both directions */
239         for (scanline = 0; scanline < REFLECTION_HEIGHT; scanline++) {
240                 for (i = 0; i < backgroundW; i++) {
241                         /* Remember that MIN_SCROLL is the padding around the screen, so ti's the maximum displacement we can get (positive & negative) */
242                         *dst2 = 2 * MIN_SCROLL * (*dst2 - minDisplacement) / (maxDisplacement - minDisplacement) - MIN_SCROLL;
243                         *dst2 = (short)((float)*dst2 / scrollScaleTable[scanline] + 0.5f); /* Displacements must also scale with distance*/
244                         dst2++;
245                 }
246         }
247 }
248
249 static float distanceScale(int scanline) {
250         float farScale, t;
251         farScale = (float)NEAR_SCROLL_SPEED / (float)FAR_SCROLL_SPEED;
252         t = (float)scanline / ((float)REFLECTION_HEIGHT - 1);
253         return 1.0f / (1.0f / farScale + (1.0f - 1.0f / farScale) * t);
254 }
255
256 static void initScrollTables() {
257         int i = 0;
258         for (i = 0; i < REFLECTION_HEIGHT; i++) {
259                 scrollScaleTable[i] = distanceScale(i);
260                 scrollTable[i] = 0.0f;
261                 scrollTableRounded[i] = 0;
262         }
263 }
264
265
266 static void updateScrollTables(float dt) {
267         int i = 0;
268         
269         nearScrollAmount += dt * NEAR_SCROLL_SPEED;
270         nearScrollAmount = (float) fmod(nearScrollAmount, 512.0f);
271
272         for (i = 0; i < REFLECTION_HEIGHT; i++) {
273                 scrollTable[i] = nearScrollAmount / scrollScaleTable[i];
274                 scrollTableRounded[i] = (int)(scrollTable[i] + 0.5f) % scrollModTable[i];
275         }
276 }
277
278 static void rleEncode(unsigned char *pixels, unsigned int w, unsigned int h) {
279         int scanline;
280         int i;
281         int penActive = 0;
282         int counter = 0;
283
284         for (scanline = 0; scanline < h; scanline++) {
285                 for (i = 0; i < w; i++) {
286                         if (*pixels++) {
287                                 if (penActive) {
288                                         if (counter >= PIXEL_PADDING) {
289                                                 printf("W %d ", counter);
290                                                 counter = 0;
291                                                 printf("I %d ", counter);
292                                         }
293                                         counter++;
294                                 } else {
295                                         counter++;
296                                         printf("I %d ", counter);
297                                         counter = 1;
298                                         penActive = 1;
299                                 }
300                         } else {
301                                 if (penActive) {
302                                         printf("W %d ", counter);
303                                         counter = 0;
304                                         penActive = 0;
305                                 } else {
306                                         counter++;
307                                 }
308                         }
309                 }
310
311                 if (penActive) {
312                         printf("W %d ", counter);
313                 }
314                 penActive = 0;
315                 counter = 0;
316                 printf(" CR\n");
317         }
318 }