10 /* APPROX. 170 FPS Minimum */
12 #define BG_FILENAME "data/grise.png"
13 #define GROBJ_01_FILENAME "data/grobj_01.png"
15 #define BB_SIZE 512 /* Let's use a power of 2. Maybe we'll zoom/rotate the effect */
17 /* Every backBuffer scanline is guaranteed to have that many dummy pixels before and after */
18 #define PIXEL_PADDING 32
20 #define MIN_SCROLL PIXEL_PADDING
21 #define MAX_SCROLL (backgroundW - fb_width - MIN_SCROLL)
23 #define FAR_SCROLL_SPEED 50.0f
24 #define NEAR_SCROLL_SPEED 400.0f
26 #define HORIZON_HEIGHT 100
27 #define REFLECTION_HEIGHT (240 - HORIZON_HEIGHT)
29 #define NORMALMAP_SCANLINE 372
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);
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);
42 static void rleEncode(unsigned char *pixels, unsigned int w, unsigned int h);
44 static unsigned short *background = 0;
45 static unsigned int backgroundW = 0;
46 static unsigned int backgroundH = 0;
48 static unsigned int lastFrameTime = 0;
49 static float lastFrameDuration = 0.0f;
51 static short *displacementMap;
53 static unsigned short *backBuffer;
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;
61 static struct screen scr = {
70 struct screen *grise_screen(void)
78 unsigned char *reflectedObject;
79 int reflectedObjectW, reflectedObjectH;
81 /* Allocate back buffer */
82 backBuffer = (unsigned short*) malloc(BB_SIZE * BB_SIZE * sizeof(unsigned short));
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");
90 /* Convert to 16bpp */
91 convert32To16((unsigned int*)background, background, backgroundW * NORMALMAP_SCANLINE); /* Normalmap will keep its 32 bit color */
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");
99 rleEncode(reflectedObject, reflectedObjectW, reflectedObjectH);
101 img_free_pixels(reflectedObject);
114 static void destroy(void)
119 img_free_pixels(background);
122 static void start(long trans_time)
124 lastFrameTime = time_msec;
127 static void stop(long trans_time)
131 static void draw(void)
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;
141 lastFrameDuration = (time_msec - lastFrameTime) / 1000.0f;
142 lastFrameTime = time_msec;
144 /* First, render the horizon */
145 for (scanline = 0; scanline < HORIZON_HEIGHT; scanline++) {
146 memcpy(dst, src, fb_width * 2);
151 /* Create scroll offsets for all scanlines of the normalmap */
152 updateScrollTables(lastFrameDuration);
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
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);
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]];
175 dst += BB_SIZE - fb_width;
176 dispScanline += backgroundW;
179 /* Blit effect to framebuffer */
180 src = backBuffer + PIXEL_PADDING;
182 for (scanline = 0; scanline < fb_height; scanline++) {
183 memcpy(dst, src, fb_width * 2);
189 /* src and dst can be the same */
190 static void convert32To16(unsigned int *src32, unsigned short *dst16, unsigned int pixelCount) {
194 *dst16++ = ((p << 8) & 0xF800) /* R */
195 | ((p >> 5) & 0x07E0) /* G */
196 | ((p >> 19) & 0x001F); /* B */
201 /* Normal map preprocessing */
202 /* Scale normal with depth and unpack R component (horizontal component) */
203 static void processNormal() {
207 short maxDisplacement = 0;
208 short minDisplacement = 256;
211 unsigned int *normalmap = (unsigned int*)background;
212 normalmap += NORMALMAP_SCANLINE * backgroundW;
213 dst = (unsigned short*)normalmap;
214 displacementMap = (short*)dst;
215 dst2 = displacementMap;
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);
230 normalmap += backgroundW;
233 if (maxDisplacement == minDisplacement) {
234 printf("Warning: grise normalmap fucked up\n");
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*/
249 static float distanceScale(int scanline) {
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);
256 static void initScrollTables() {
258 for (i = 0; i < REFLECTION_HEIGHT; i++) {
259 scrollScaleTable[i] = distanceScale(i);
260 scrollTable[i] = 0.0f;
261 scrollTableRounded[i] = 0;
266 static void updateScrollTables(float dt) {
269 nearScrollAmount += dt * NEAR_SCROLL_SPEED;
270 nearScrollAmount = (float) fmod(nearScrollAmount, 512.0f);
272 for (i = 0; i < REFLECTION_HEIGHT; i++) {
273 scrollTable[i] = nearScrollAmount / scrollScaleTable[i];
274 scrollTableRounded[i] = (int)(scrollTable[i] + 0.5f) % scrollModTable[i];
278 static void rleEncode(unsigned char *pixels, unsigned int w, unsigned int h) {
284 for (scanline = 0; scanline < h; scanline++) {
285 for (i = 0; i < w; i++) {
288 if (counter >= PIXEL_PADDING) {
289 printf("W %d ", counter);
291 printf("I %d ", counter);
296 printf("I %d ", counter);
302 printf("W %d ", counter);
312 printf("W %d ", counter);