cacb7b55f364327496c6a0c02d6045960aad0117
[dosdemo] / src / scr / thunder.c
1 /* thunder. c */
2 #include <stdio.h>
3 #include <stdlib.h>
4 #include <string.h>
5 #include <math.h>
6 #include <assert.h>
7 #include "imago2.h"
8 #include "demo.h"
9 #include "screen.h"
10
11 /* Render blur in half x half dimenstions. Add one pixel padding in all 
12  * directions (2 pixels horizontally, 2 pixels vertically).
13  */
14 #define BLUR_BUFFER_WIDTH (320/2 + 2)
15 #define BLUR_BUFFER_HEIGHT (240/2 + 2)
16 #define BLUR_BUFFER_SIZE (BLUR_BUFFER_WIDTH * BLUR_BUFFER_HEIGHT)
17 static unsigned char *blurBuffer, *blurBuffer2;
18
19 #define BLUR_DARKEN 4
20
21 #define THUNDER_RECT_SIZE 2
22 #define THUNDER_RANDOMNESS 16
23 #define THUNDER_SECONDS 0.075f
24
25 #define VERTEX_COUNT 12
26 #define PERSPECTIVE_NEUTRAL_DEPTH 0.5f
27 #define NEAR_PLANE 0.01f
28 #define ROTATION_SPEED 1.5f
29
30 #define MESH_RANDOM_SEED 173
31
32 #define MIN_FOGGED 40
33
34 #define CAMERA_DISTANCE 1.1f
35
36 /* TODO: Load palette from file */
37 static unsigned short palette[256];
38
39 typedef unsigned int PointSprite;
40 #define MAX_POINT_SPRITES 1024
41 static PointSprite pointSprites[MAX_POINT_SPRITES];
42 int pointSpriteCount = 0;
43 #define PACK_POINT_SPRITE(x, y, col) ((col << 16) | (x << 8) | y)
44 #define UNPACK_COLOR(ps) (ps >> 16)
45 #define UNPACK_X(ps) ((ps >> 8) & 0xFF)
46 #define UNPACK_Y(ps) (ps & 0xFF)
47
48 typedef struct {
49         float x,y,z;
50 } MyVertex ;
51
52 MyVertex vertexBuffer[VERTEX_COUNT];
53 MyVertex vertexBufferAnimated[VERTEX_COUNT];
54 MyVertex vertexBufferProjected[VERTEX_COUNT];
55
56 void clearBlurBuffer();
57 void applyBlur();
58 void blitEffect();
59 void thunder(int x0, int y0, int x1, int y1, unsigned char c0, unsigned char c1, int seed, int randomness, int depth);
60
61 void initMesh();
62 void projectMesh();
63 void animateMesh();
64 void renderMeshToPointSprites(int seed);
65 void renderPointSprites();
66 unsigned char fog(float z);
67 void sortPointSprites();
68
69 static int init(void);
70 static void destroy(void);
71 static void start(long trans_time);
72 static void stop(long trans_time);
73 static void draw(void);
74
75 static unsigned int lastFrameTime = 0;
76 static float lastFrameDuration = 0.0f;
77 static struct screen scr = {
78         "thunder",
79         init,
80         destroy,
81         start,
82         0,
83         draw
84 };
85
86 struct screen *thunder_screen(void)
87 {
88         return &scr;
89 }
90
91 static int init(void)
92 {
93         int i = 0;
94
95         blurBuffer = malloc(BLUR_BUFFER_SIZE);
96         blurBuffer2 = malloc(BLUR_BUFFER_SIZE);
97
98         clearBlurBuffer();
99
100         /* For now, map to blue */
101         for (i = 0; i < 256; i++) {
102                 palette[i] = (i * i) >> 11;
103         }
104
105         initMesh();
106
107         return 0;
108 }
109
110 static void destroy(void)
111 {
112         free(blurBuffer);
113         blurBuffer = 0;
114         
115         free(blurBuffer2);
116         blurBuffer2 = 0;
117 }
118
119 static void start(long trans_time)
120 {
121         lastFrameTime = time_msec;
122 }
123
124
125 static float remainingThunderDuration = THUNDER_SECONDS;
126 static int thunderPattern = 0;
127
128 static void draw(void)
129 {
130         lastFrameDuration = (time_msec - lastFrameTime) / 1000.0f;
131         lastFrameTime = time_msec;
132
133         remainingThunderDuration -= lastFrameDuration;
134         if (remainingThunderDuration <= 0) {
135                 thunderPattern++;
136                 remainingThunderDuration = THUNDER_SECONDS;
137         }
138         
139         animateMesh();
140         projectMesh();
141         renderMeshToPointSprites(thunderPattern);
142         sortPointSprites();
143         renderPointSprites();
144         
145         
146         applyBlur();
147         blitEffect();
148
149         
150
151         swap_buffers(0);
152 }
153
154 void clearBlurBuffer() {
155         /* Clear the whole buffer (including its padding ) */
156         memset(blurBuffer, 0, BLUR_BUFFER_SIZE);
157         memset(blurBuffer2, 0, BLUR_BUFFER_SIZE);
158 }
159
160
161 void applyBlur() {
162         int i, j;
163         unsigned char *tmp;
164         unsigned char *src = blurBuffer + BLUR_BUFFER_WIDTH + 1;
165         unsigned char *dst = blurBuffer2 + BLUR_BUFFER_WIDTH + 1;
166
167         for (j = 0; j < 120; j++) {
168                 for (i = 0; i < 160; i++) {
169                         *dst = (*(src - 1) + *(src + 1) + *(src - BLUR_BUFFER_WIDTH) + *(src + BLUR_BUFFER_WIDTH)) >> 2;
170                         
171                         if (*dst > BLUR_DARKEN) *dst -= BLUR_DARKEN;
172                         else *dst = 0;
173
174                         dst++;
175                         src++;
176                 }
177                 /* Just skip the padding since we went through the scanline in the inner loop (except from the padding) */
178                 src += 2;
179                 dst += 2;
180         }
181
182         /* Swap blur buffers */
183         tmp = blurBuffer;
184         blurBuffer = blurBuffer2;
185         blurBuffer2 = tmp;
186 }
187
188 void blitEffect() {
189         unsigned int *dst1 = (unsigned int*) fb_pixels;
190         unsigned int *dst2 = dst1 + 160; /* We're writing two pixels at once */
191         unsigned char *src1 = blurBuffer + BLUR_BUFFER_WIDTH + 1;
192         unsigned char *src2 = src1 + BLUR_BUFFER_WIDTH;
193         unsigned char tl, tr, bl, br;
194         int i, j;
195
196         for (j = 0; j < 120; j++) {
197                 for (i = 0; i < 160; i++) {
198                         tl = *src1;
199                         tr = (*src1 + *(src1 + 1)) >> 1;
200                         bl = (*src1 + *src2) >> 1;
201                         br = (tr + ((*src2 + *(src2 + 1)) >> 1)) >> 1;
202
203                         /* Pack 2 pixels in each 32 bit word */
204                         *dst1 = (palette[tr] << 16) | palette[tl];
205                         *dst2 = (palette[br] << 16) | palette[bl];
206
207                         dst1++;
208                         src1++;
209                         dst2++;
210                         src2++;
211                 }
212                 /* Again, skip padding */
213                 src1 += 2;
214                 src2 += 2;
215
216                 /* For now, skip a scanline */
217                 dst1 += 160;
218                 dst2 += 160;
219         }
220
221 }
222
223 void thunder(int x0, int y0, int x1, int y1, unsigned char c0, unsigned char c1, int seed, int randomness, int depth) {
224         int mx, my, i, j;
225         unsigned char *dst;
226         unsigned char mc;
227
228         if (randomness <= 0) randomness = 1;
229         mx = ((x0 + x1) >> 1) + rand() % randomness - randomness / 2;
230         my = ((y0 + y1) >> 1) + rand() % randomness - randomness / 2;
231         mc = (c0 + c1) >> 1;
232
233         if (depth <= 0) return;
234
235         /* Insert a new sprite */
236         if (pointSpriteCount >= MAX_POINT_SPRITES) {
237                 printf("PROBLEM");
238                 return;
239         }
240         pointSprites[pointSpriteCount++] = PACK_POINT_SPRITE(mx, my, mc);
241
242         srand(seed);
243
244         thunder(x0, y0, mx, my, c0, mc, rand(), randomness >> 1, depth-1);
245         thunder(mx, my, x1, y1, mc, c1, rand(), randomness >> 1, depth - 1);
246 }
247
248 MyVertex randomVertex() {
249         MyVertex ret;
250         float l;
251
252         ret.x = rand() % 200 - 100; if (ret.x == 0) ret.x = 1;
253         ret.y = rand() % 200 - 100; if (ret.y == 0) ret.y = 1;
254         ret.z = rand() % 200 - 100; if (ret.z == 0) ret.z = 1;
255         
256         // Normalize
257         l = sqrt(ret.x * ret.x + ret.y * ret.y + ret.z * ret.z);
258         ret.x /= l;
259         ret.y /= l;
260         ret.z /= l;
261
262         return ret;
263 }
264
265 void initMesh() {
266         int i;
267
268         srand(MESH_RANDOM_SEED);
269
270         for (i = 0; i < VERTEX_COUNT; i++) {
271                 vertexBuffer[i] = randomVertex();
272         }
273 }
274
275 void animateMesh() {
276         int i = 0;
277         MyVertex bx, by, bz;
278         float yRot;
279
280         yRot = ROTATION_SPEED * time_msec / 1000.0f;
281
282         /* Create rotated basis */
283         bx.x = cos(yRot);
284         bx.y = 0.0f;
285         bx.z = sin(yRot);
286
287         by.x = 0.0f;
288         by.y = 1.0f;
289         by.z = 0.0f;
290
291         bz.x = cos(yRot + M_PI/2.0f);
292         bz.y = 0.0f;
293         bz.z = sin(yRot + M_PI/2.0f);
294
295         for (i = 0; i < VERTEX_COUNT; i++) {
296                 MyVertex v1, v2;
297                 v1 = vertexBuffer[i];
298
299
300                 v1.y *= sin(time_msec / 1000.0f + v1.x + v1.z);
301
302                 /* O re panaia mou */
303                 v2.x = v1.x * bx.x + v1.y * by.x + v1.z * bz.x;
304                 v2.y = v1.x * bx.y + v1.y * by.y + v1.z * bz.y;
305                 v2.z = v1.x * bx.z + v1.y * by.z + v1.z * bz.z;
306
307                 v2.z += CAMERA_DISTANCE;
308
309                 vertexBufferAnimated[i] = v2;
310         }
311 }
312
313 void projectMesh() {
314         int i = 0;
315
316         for (i = 0; i < VERTEX_COUNT; i++) {
317
318                 if (vertexBufferAnimated[i].z <= NEAR_PLANE) {
319                         vertexBufferProjected[i].x = vertexBufferProjected[i].y = 1000.0f;
320                         vertexBufferProjected[i].z = -1.0f;
321                         continue;
322                 }
323
324                 vertexBufferProjected[i].x = vertexBufferAnimated[i].x * PERSPECTIVE_NEUTRAL_DEPTH / vertexBufferAnimated[i].z;
325                 vertexBufferProjected[i].y = vertexBufferAnimated[i].y * PERSPECTIVE_NEUTRAL_DEPTH / vertexBufferAnimated[i].z;
326         }
327 }
328
329 void renderMeshToPointSprites(int seed) {
330         int vertex, j;
331         int sx, sy;
332         unsigned char color;
333         unsigned char *dst;
334         unsigned char fogAtOrigin;
335
336         fogAtOrigin = fog(CAMERA_DISTANCE);
337
338         pointSpriteCount = 0;
339         srand(seed);
340
341         for (vertex = 0; vertex < VERTEX_COUNT; vertex++) {
342                 sx = (int)(vertexBufferProjected[vertex].x * 80) + 80;
343                 sy = (int)(vertexBufferProjected[vertex].y * 60) + 60;
344
345                 thunder(80, 60, sx, sy, fogAtOrigin, fog(vertexBufferAnimated[vertex].z), rand(), THUNDER_RANDOMNESS, 5);
346         }
347 }
348
349 void renderPointSprites() {
350         int i,j;
351         PointSprite sprite;
352         unsigned char *dst;
353         int sx, sy;
354         unsigned char color;
355
356         for (i = 0; i < pointSpriteCount; i++) {
357                 sprite = pointSprites[i];
358
359                 sx = UNPACK_X(sprite);
360                 sy = UNPACK_Y(sprite);
361
362                 if (sx < THUNDER_RECT_SIZE || sx >= 160 - THUNDER_RECT_SIZE || sy < THUNDER_RECT_SIZE || sy >= 120 - THUNDER_RECT_SIZE) continue;
363
364                 dst = blurBuffer + BLUR_BUFFER_WIDTH + 1 + sx + sy * BLUR_BUFFER_WIDTH;
365
366                 color = UNPACK_COLOR(sprite);
367
368                 for (j = 0; j < THUNDER_RECT_SIZE; j++) {
369                         memset(dst, color, THUNDER_RECT_SIZE);
370                         dst += BLUR_BUFFER_WIDTH;
371                 }
372         }
373 }
374
375 unsigned char fog(float z) {
376         unsigned int ret = (unsigned int) (((-(z - CAMERA_DISTANCE)) * 0.5f + 0.5f) * (255.0f - MIN_FOGGED)) + MIN_FOGGED;
377         if (ret > 255) ret = 255;
378         return (unsigned char)ret;
379 }
380
381 void sort(PointSprite *begin, PointSprite *end) {\r
382         PointSprite pivotValue;\r
383         size_t sz;\r
384         PointSprite *left, *right;\r
385         int leftCond, rightCond;\r
386         PointSprite tmp;\r
387 \r
388         sz = end - begin;\r
389 \r
390         if (sz < 2) return; /* already sorted */\r
391         if (sz == 2) {\r
392                 /* trivial case */\r
393                 if (begin[1] < begin[0]) {\r
394                         tmp = begin[0];\r
395                         begin[0] = begin[1];\r
396                         begin[1] = tmp;\r
397                         return;\r
398                 }\r
399         }\r
400 \r
401         /* minimum 3 elements from now on */\r
402 \r
403         /* choose a pivot near the middle, since we frequently sort already sorted arrays */\r
404         pivotValue = begin[sz / 2];\r
405 \r
406         left = begin;\r
407         right = end - 1;\r
408 \r
409         while (right > left) {\r
410                 /* check if left and right elements meet the conditions */\r
411                 leftCond = pivotValue >= *left;\r
412                 rightCond = pivotValue < *right;\r
413 \r
414                 if (!leftCond && !rightCond) {\r
415                         tmp = *left;\r
416                         *left = *right;\r
417                         *right = tmp;\r
418                         left++;\r
419                         right--;\r
420                 }\r
421                 else if (leftCond && rightCond) {\r
422                         left++;\r
423                         right--;\r
424                 }\r
425                 else if (leftCond) {\r
426                         left++;\r
427                 }\r
428                 else {\r
429                         right--;\r
430                 }\r
431         }\r
432 \r
433         /* recursion */\r
434         sort(begin, left);\r
435         sort(left, end);\r
436 }\r
437
438 void sortPointSprites() {
439         sort(pointSprites, pointSprites + pointSpriteCount);
440 }
441