84a953bf20f63cbb3716fa3f3b5c195fc073535b
[dosdemo] / src / bump.c
1 // Bump effect (not moving yet of course, I have many ideas on this to commit before it's ready)
2
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <math.h>
7
8 #include "demo.h"
9 #include "screen.h"
10 #include "tinyfps.h"
11
12 static int init(void);
13 static void destroy(void);
14 static void start(long trans_time);
15 static void stop(long trans_time);
16 static void draw(void);
17
18 static struct screen scr = {
19         "bump",
20         init,
21         destroy,
22         start,
23         stop,
24         draw
25 };
26
27 static struct point {
28         int x, y;
29 };
30
31 #define LIGHT_WIDTH 128
32 #define LIGHT_HEIGHT LIGHT_WIDTH
33
34 static unsigned long startingTime;
35
36 static unsigned char *heightmap;
37 static unsigned short *lightmap;
38 static int *bumpOffset;
39
40 static unsigned short *lightR;
41 static unsigned short *lightG;
42 static unsigned short *lightB;
43 static struct point pointR, pointG, pointB;
44
45 //#define FUNKY_COLORS
46
47
48 struct screen *bump_screen(void)
49 {
50         return &scr;
51 }
52
53 static int init(void)
54 {
55         int i, j, x, y, c, r, g, b;
56
57         const int numBlurs = 3;
58         const int lightRadius = LIGHT_WIDTH / 2;
59
60         const int lightSize = LIGHT_WIDTH * LIGHT_HEIGHT;
61         const int fb_size = fb_width * fb_height;
62
63         // Just some parameters to temporary test the colors of 3 lights
64         #ifdef FUNKY_COLORS
65                 // I see some artifacts if I mix channels, not sure if ORing is fine
66                 const float r0 = 1.0f, g0 = 0.6f, b0 = 0.0f;
67                 const float r1 = 0.5f, g1 = 1.0f, b1 = 0.2f;
68                 const float r2 = 0.6f, g2 = 0.4f, b2 = 1.0f;
69         #else
70                 // if every light uses it's own channel bits, it's better
71                 const float r0 = 1.0f, g0 = 0.0f, b0 = 0.0f;
72                 const float r1 = 0.0f, g1 = 1.0f, b1 = 0.0f;
73                 const float r2 = 0.0f, g2 = 0.0f, b2 = 1.0f;
74         #endif
75
76         initFpsFonts();
77
78         heightmap = malloc(sizeof(*heightmap) * fb_size);
79         lightmap = malloc(sizeof(*lightmap) * fb_size);
80         bumpOffset = malloc(sizeof(*bumpOffset) * fb_size);
81
82         lightR = malloc(sizeof(*lightR) * lightSize);
83         lightG = malloc(sizeof(*lightG) * lightSize);
84         lightB = malloc(sizeof(*lightB) * lightSize);
85
86         memset(lightmap, 0, sizeof(*lightmap) * fb_size);
87         memset(bumpOffset, 0, sizeof(*bumpOffset) * fb_size);
88
89         // Create random junk
90         for (i = 0; i < fb_size; i++)
91                 heightmap[i] = rand() & 255;
92
93         // Blur to smooth
94         for (j = 0; j < numBlurs; j++)
95                 for (i = 0; i < fb_size; i++)
96                         heightmap[i] = (heightmap[abs((i - 1) % fb_size)] + heightmap[abs((i + 1) % fb_size)] + heightmap[abs((i - fb_width) % fb_size)] + heightmap[abs((i + fb_width) % fb_size)]) >> 2;
97
98         // Inclination precalculation
99         i = 0;
100         for (y = 0; y < fb_height; y++)
101         {
102                 for (x = 0; x < fb_width; x++)
103                 {
104                         const float offsetPower = 2.0f;
105                         int dx, dy, xp, yp;
106
107                         dx = (int)((heightmap[i] - heightmap[i + 1]) * offsetPower);
108                         dy = (int)((heightmap[i] - heightmap[i + fb_width]) * offsetPower);
109
110                         xp = x + dx;
111                         if (xp < 0) xp = 0;
112                         if (xp > fb_width - 1) xp = fb_width - 1;
113
114                         yp = y + dy;
115                         if (yp < 0) yp = 0;
116                         if (yp > fb_height - 1) yp = fb_height - 1;
117
118                         bumpOffset[i++] = yp * fb_width + xp;
119                 }
120         }
121
122         // Generate three lights
123         i = 0;
124         for (y = 0; y < LIGHT_HEIGHT; y++)
125         {
126                 int yc = y - (LIGHT_HEIGHT / 2);
127                 for (x = 0; x < LIGHT_WIDTH; x++)
128                 {
129                         int xc = x - (LIGHT_WIDTH / 2);
130                         float d = (float)sqrt(xc * xc + yc * yc);
131                         float invDist = ((float)lightRadius - (float)sqrt(xc * xc + yc * yc)) / (float)lightRadius;
132                         if (invDist < 0.0f) invDist = 0.0f;
133
134                         c = (int)(invDist * 63);
135                         r = c >> 1;
136                         g = c;
137                         b = c >> 1;
138
139                         lightR[i] = ((int)(r * r0) << 11) | ((int)(g * g0) << 5) | (int)(b * b0);
140                         lightG[i] = ((int)(r * r1) << 11) | ((int)(g * g1) << 5) | (int)(b * b1);
141                         lightB[i] = ((int)(r * r2) << 11) | ((int)(g * g2) << 5) | (int)(b * b2);
142                         i++;
143                 }
144         }
145
146         return 0;
147 }
148
149 static void destroy(void)
150 {
151 }
152
153 static void start(long trans_time)
154 {
155         startingTime = time_msec;
156 }
157
158 static void stop(long trans_time)
159 {
160 }
161
162 static void eraseArea(struct point *p, int width, int height)
163 {
164         int x, y;
165         unsigned short *dst;
166
167         int x0 = p->x;
168         int y0 = p->y;
169         int x1 = p->x + width;
170         int y1 = p->y + height;
171
172         int dx, dy;
173
174         if (x0 < 0) x0 = 0;
175         if (y0 < 0) y0 = 0;
176         if (x1 > fb_width) x1 = fb_width;
177         if (y1 > fb_height) y1 = fb_height;
178
179         dx = x1 - x0;
180         //dy = y1 - y0;
181
182         dst = (unsigned short*)lightmap + y0 * fb_width + x0;
183
184         for (y = y0; y < y1; y++)
185         {
186                 for (x = x0; x < x1; x++)
187                 {
188                         *dst++ = 0;
189                 }
190                 dst += fb_width - dx;
191         }
192 }
193
194
195 static void renderLight(struct point *p, int width, int height, unsigned short *light)
196 {
197         // Check for boundaries is missing atm, will add soon
198         int x, y;
199         unsigned short *dst = (unsigned short*)lightmap + p->y * fb_width + p->x;
200         for (y = 0; y < height; y++)
201         {
202                 for (x = 0; x < width; x++)
203                 {
204                         *dst++ |= *light++;
205                 }
206                 dst += fb_width - width;
207         }
208 }
209
210 static void eraseLights()
211 {
212         eraseArea(&pointR, LIGHT_WIDTH, LIGHT_HEIGHT);
213         eraseArea(&pointG, LIGHT_WIDTH, LIGHT_HEIGHT);
214         eraseArea(&pointB, LIGHT_WIDTH, LIGHT_HEIGHT);
215 }
216
217 static void renderLights()
218 {
219         renderLight(&pointR, LIGHT_WIDTH, LIGHT_HEIGHT, lightR);
220         renderLight(&pointG, LIGHT_WIDTH, LIGHT_HEIGHT, lightG);
221         renderLight(&pointB, LIGHT_WIDTH, LIGHT_HEIGHT, lightB);
222 }
223
224 static void animateLights()
225 {
226         struct point center;
227         float dt = (float)(time_msec - startingTime) / 1000.0f;
228
229         center.x = (fb_width >> 1) - (LIGHT_WIDTH / 2);
230         center.y = (fb_height >> 1) - (LIGHT_HEIGHT / 2);
231
232         // This test condition will crash because I also need to add boundaries to renderLight (tomorrow)
233         //pointR.x = center.x + sin(1.2f * dt) * 144.0f;
234         //pointR.y = center.y + sin(0.8f * dt) * 148.0f;
235
236         pointR.x = center.x + sin(1.2f * dt) * 96.0f;
237         pointR.y = center.y + sin(0.8f * dt) * 48.0f;
238
239         pointG.x = center.x + sin(1.5f * dt) * 56.0f;
240         pointG.y = center.y + sin(1.1f * dt) * 42.0f;
241
242         pointB.x = center.x + sin(2.0f * dt) * 80.0f;
243         pointB.y = center.y + sin(1.3f * dt) * 46.0f;
244 }
245
246 static void renderBump(unsigned short *vram)
247 {
248         int i;
249         for (i = 0; i < fb_width * fb_height; i++)
250         {
251                 unsigned short c = lightmap[bumpOffset[i]];
252                 *vram++ = c;
253         }
254 }
255
256 static void draw(void)
257 {
258         eraseLights();
259         animateLights();
260         renderLights();
261         renderBump((unsigned short*)vmem_back);
262
263         drawFps((unsigned short*)vmem_back);
264
265         swap_buffers(0);
266 }