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