10 /* APPROX. 170 FPS Minimum */
12 #define BG_FILENAME "data/grise.png"
14 #define BB_SIZE 512 /* Let's use a power of 2. Maybe we'll zoom/rotate the effect */
16 /* Every backBuffer scanline is guaranteed to have that many dummy pixels before and after */
17 #define PIXEL_PADDING 32
19 #define MIN_SCROLL PIXEL_PADDING
20 #define MAX_SCROLL (backgroundW - fb_width - MIN_SCROLL)
22 #define FAR_SCROLL_SPEED 50.0f
23 #define NEAR_SCROLL_SPEED 400.0f
25 #define HORIZON_HEIGHT 100
26 #define REFLECTION_HEIGHT (240 - HORIZON_HEIGHT)
28 #define NORMALMAP_SCANLINE 372
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);
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);
41 static unsigned short *background = 0;
42 static unsigned int backgroundW = 0;
43 static unsigned int backgroundH = 0;
45 static unsigned int lastFrameTime = 0;
46 static float lastFrameDuration = 0.0f;
48 static short *displacementMap;
50 static unsigned short *backBuffer;
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;
58 static struct screen scr = {
67 struct screen *mike_screen(void)
75 /* Allocate back buffer */
76 backBuffer = (unsigned short*) malloc(BB_SIZE * BB_SIZE * sizeof(unsigned short));
78 if (!(background = img_load_pixels(BG_FILENAME, &backgroundW, &backgroundH, IMG_FMT_RGBA32))) {
79 fprintf(stderr, "failed to load image " BG_FILENAME "\n");
83 /* Convert to 16bpp */
84 convert32To16((unsigned int*)background, background, backgroundW * NORMALMAP_SCANLINE); /* Normalmap will keep its 32 bit color */
93 static void destroy(void)
98 img_free_pixels(background);
101 static void start(long trans_time)
103 lastFrameTime = time_msec;
106 static void stop(long trans_time)
110 static void draw(void)
112 int scroll = MIN_SCROLL + (MAX_SCROLL - MIN_SCROLL) * mouse_x / fb_width;
113 unsigned short *dst = backBuffer + PIXEL_PADDING;
114 unsigned short *src = background + scroll;
120 lastFrameDuration = (time_msec - lastFrameTime) / 1000.0f;
121 lastFrameTime = time_msec;
123 /* First, render the horizon */
124 for (scanline = 0; scanline < HORIZON_HEIGHT; scanline++) {
125 memcpy(dst, src, fb_width * 2);
130 /* Create scroll opffsets for all scanlines of the normalmap */
131 updateScrollTables(lastFrameDuration);
133 /* Then, render the reflection under the horizon */
134 /* dst is already in place */
135 src = background + HORIZON_HEIGHT * backgroundW;
136 dispScanline = displacementMap;
137 for (scanline = 0; scanline < REFLECTION_HEIGHT; scanline++) {
138 for (i = 0; i < fb_width; i++) {
139 d = dispScanline[(i + scrollTableRounded[scanline]) % scrollModTable[scanline]];
140 *dst++ = src[i + scroll + d];
143 dst += BB_SIZE - fb_width;
144 dispScanline += backgroundW;
147 /* Blit effect to framebuffer */
148 src = backBuffer + PIXEL_PADDING;
150 for (scanline = 0; scanline < fb_height; scanline++) {
151 memcpy(dst, src, fb_width * 2);
157 /* src and dst can be the same */
158 static void convert32To16(unsigned int *src32, unsigned short *dst16, unsigned int pixelCount) {
162 *dst16++ = ((p << 8) & 0xF800) /* R */
163 | ((p >> 5) & 0x07E0) /* G */
164 | ((p >> 19) & 0x001F); /* B */
169 /* Normal map preprocessing */
170 /* Scale normal with depth and unpack R component (horizontal component) */
171 static void processNormal() {
175 short maxDisplacement = 0;
176 short minDisplacement = 256;
179 unsigned int *normalmap = (unsigned int*)background;
180 normalmap += NORMALMAP_SCANLINE * backgroundW;
181 dst = (unsigned short*)normalmap;
182 displacementMap = (short*)dst;
183 dst2 = displacementMap;
185 for (scanline = 0; scanline < REFLECTION_HEIGHT; scanline++) {
186 scrollModTable[scanline] = (int) (backgroundW / scrollScaleTable[scanline] + 0.5f);
187 for (i = 0; i < backgroundW; i++) {
188 x = (int)(i * scrollScaleTable[scanline] + 0.5f);
189 if (x < backgroundW) {
190 *dst = (unsigned short)(normalmap[x] >> 8) & 0xFF;
191 if ((short)*dst > maxDisplacement) maxDisplacement = (short)(*dst);
192 if ((short)*dst < minDisplacement) minDisplacement = (short)(*dst);
198 normalmap += backgroundW;
201 if (maxDisplacement == minDisplacement) {
202 printf("Warning: grise normalmap fucked up\n");
206 /* Second pass - subtract half maximum displacement to displace in both directions */
207 for (scanline = 0; scanline < REFLECTION_HEIGHT; scanline++) {
208 for (i = 0; i < backgroundW; i++) {
209 /* Remember that MIN_SCROLL is the padding around the screen, so ti's the maximum displacement we can get (positive & negative) */
210 *dst2 = 2 * MIN_SCROLL * (*dst2 - minDisplacement) / (maxDisplacement - minDisplacement) - MIN_SCROLL;
211 *dst2 = (short)((float)*dst2 / scrollScaleTable[scanline] + 0.5f); /* Displacements must also scale with distance*/
217 static float distanceScale(int scanline) {
219 farScale = (float)NEAR_SCROLL_SPEED / (float)FAR_SCROLL_SPEED;
220 t = (float)scanline / ((float)REFLECTION_HEIGHT - 1);
221 return 1.0f / (1.0f / farScale + (1.0f - 1.0f / farScale) * t);
224 static void initScrollTables() {
226 for (i = 0; i < REFLECTION_HEIGHT; i++) {
227 scrollScaleTable[i] = distanceScale(i);
228 scrollTable[i] = 0.0f;
229 scrollTableRounded[i] = 0;
234 static void updateScrollTables(float dt) {
237 nearScrollAmount += dt * NEAR_SCROLL_SPEED;
238 nearScrollAmount = (float) fmod(nearScrollAmount, 512.0f);
240 for (i = 0; i < REFLECTION_HEIGHT; i++) {
241 scrollTable[i] = nearScrollAmount / scrollScaleTable[i];
242 scrollTableRounded[i] = (int)(scrollTable[i] + 0.5f) % scrollModTable[i];