source: soft/giet_vm/applications/raycast/disp.c @ 683

Last change on this file since 683 was 683, checked in by guerin, 9 years ago

raycast: initial distributed implementation

The main task runs on processor (0,0,0). It manages game logic and
initialization, and participates in the rendering process.
The render task runs on all other processors. It tries to render a frame
slice when available, which is raycasting + pixel drawing for a pixel
column of the frame.

File size: 6.8 KB
Line 
1#include "disp.h"
2#include "game.h"
3#include <stdio.h>
4#include <stdlib.h>
5#include <malloc.h>
6#include <math.h>
7#include <hard_config.h>
8#include <user_sqt_lock.h>
9
10#define FIELD_OF_VIEW   (70.f * M_PI / 180.f)   // Camera field of view
11#define TEX_SIZE        (32)                    // Texture size in pixels
12#define CEILING_COLOR   (0xBB)                  // lite gray
13#define FLOOR_COLOR     (0x33)                  // dark gray
14
15// Globals
16
17static unsigned char*           buf[2];         // framebuffer
18static void *                   sts[2];         // for fbf_cma
19static unsigned int             cur_buf;        // current framebuffer
20static volatile unsigned int    slice_x;        // slice index (shared)
21static sqt_lock_t               slice_x_lock;   // slice index lock
22static volatile unsigned int    slice_cnt;      // slice count (shared)
23
24// Textures indexed by block number
25static unsigned char *g_tex[] =
26{
27    NULL, // 0
28    NULL, // rock
29    NULL, // door
30    NULL, // handle
31    NULL, // wood
32};
33
34// Local functions
35
36static void dispDrawColumnTex(int x, int y0, int y1, unsigned char *tex, int tx)
37{
38    int y;
39
40    if (x < 0 || x >= FBUF_X_SIZE)
41        return;
42
43    for (y = y0; y < y1 && y < FBUF_Y_SIZE; y++) {
44        if (y < 0)
45            y = 0;
46
47        int ty = (y - y0) * TEX_SIZE / (y1 - y0);
48
49        buf[cur_buf][y * FBUF_X_SIZE + x] = tex[ty * TEX_SIZE + tx];
50    }
51}
52
53static void dispDrawColumnSolid(int x, int y0, int y1, unsigned char color)
54{
55    int y;
56
57    if (x < 0 || x >= FBUF_X_SIZE)
58        return;
59
60    for (y = y0; y < y1 && y < FBUF_Y_SIZE; y++) {
61        if (y < 0)
62            y = 0;
63
64        buf[cur_buf][y * FBUF_X_SIZE + x] = color;
65    }
66}
67
68static void dispDrawSlice(Game *game, int x, int height, int type, float tx)
69{
70    // Ceiling
71    dispDrawColumnSolid(x,
72                        0,
73                        (FBUF_Y_SIZE - height) / 2,
74                        CEILING_COLOR);
75
76    // Wall
77    unsigned char *tex = g_tex[type];
78
79    if (tex) {
80        // Draw a texture slice
81        dispDrawColumnTex(x,
82                          (FBUF_Y_SIZE - height) / 2,
83                          (FBUF_Y_SIZE + height) / 2,
84                          tex,
85                          (int)(tx * TEX_SIZE));
86    }
87    else {
88        // Draw a solid color slice
89        dispDrawColumnSolid(x,
90                            (FBUF_Y_SIZE - height) / 2,
91                            (FBUF_Y_SIZE + height) / 2,
92                            0xFF);
93    }
94
95    // Floor
96    dispDrawColumnSolid(x,
97                        (FBUF_Y_SIZE + height) / 2,
98                        FBUF_Y_SIZE,
99                        FLOOR_COLOR);
100}
101
102static float dispRaycast(Game *game, int *type, float *tx, float angle)
103{
104    float x = game->player.x;
105    float y = game->player.y;
106
107    // Camera is inside a block.
108    // Return a minimal distance to avoid a division by zero.
109    if ((gameLocate(floor(x), floor(y))) != 0) {
110        *type = 0;
111        *tx = 0.f;
112        return 0.0001f;
113    }
114
115    // Precompute
116    float vsin = sin(angle);
117    float vcos = cos(angle);
118    float vtan = vsin / vcos;
119
120    // Calculate increments
121    int incix = (vcos > 0.f) - (vcos < 0.f);
122    int inciy = (vsin > 0.f) - (vsin < 0.f);
123    float incfx = inciy / vtan;
124    float incfy = incix * vtan;
125
126    // Calculate start position
127    int ix = floor(x) + (incix > 0);
128    int iy = floor(y) + (inciy > 0);
129    float fx = x + incfx * fabs(floor(y) + (inciy > 0) - y);
130    float fy = y + incfy * fabs(floor(x) + (incix > 0) - x);
131
132    // Find the first colliding tile in each direction
133    while (incix && gameLocate(ix - (incix < 0), fy) == 0)
134    {
135        ix += incix;
136        fy += incfy;
137    }
138    while (inciy && gameLocate(fx, iy - (inciy < 0)) == 0)
139    {
140        fx += incfx;
141        iy += inciy;
142    }
143
144    // Find the shortest ray
145    float dx = (incix) ? ((ix - x) / vcos) : 0xFFFF;
146    float dy = (inciy) ? ((iy - y) / vsin) : 0xFFFF;
147
148    if (dx < dy)
149    {
150        // Get block type
151        *type = gameLocate(ix - (incix < 0), floor(fy));
152
153        // Get wall texture coordinate [0;1]
154        *tx = fy - floor(fy);
155        if (incix < 0)
156            *tx = 1 - *tx;
157
158        return dx;
159    }
160    else
161    {
162        // Get block type
163        *type = gameLocate(floor(fx), iy - (inciy < 0));
164
165        // Get wall texture coordinate [0;1]
166        *tx = fx - floor(fx);
167        if (inciy > 0)
168            *tx = 1 - *tx;
169
170        return dy;
171    }
172}
173
174static unsigned char *dispLoadTexture(char *path)
175{
176    int fd;
177    unsigned char *tex;
178
179    tex = malloc(TEX_SIZE * TEX_SIZE);
180    fd = giet_fat_open(path, O_RDONLY);
181    if (fd < 0) {
182        free(tex);
183        return NULL;
184    }
185
186    giet_fat_read(fd, tex, TEX_SIZE * TEX_SIZE);
187    giet_fat_close(fd);
188    giet_tty_printf("[RAYCAST] loaded tex %s\n", path);
189
190    return tex;
191}
192
193// Exported functions
194
195void dispInit()
196{
197    unsigned int w, h, p;
198
199    // Initialize lock
200    giet_procs_number(&w, &h, &p);
201    sqt_lock_init(&slice_x_lock, w, h, p);
202
203    // Allocate framebuffer
204    buf[0] = almalloc(64, FBUF_X_SIZE * FBUF_Y_SIZE);
205    buf[1] = almalloc(64, FBUF_X_SIZE * FBUF_Y_SIZE);
206    sts[0] = almalloc(64, 64);
207    sts[1] = almalloc(64, 64);
208
209    // Initialize framebuffer
210    giet_fbf_cma_alloc();
211    giet_fbf_cma_init_buf(buf[0], buf[1], sts[0], sts[1]);
212    giet_fbf_cma_start(FBUF_X_SIZE * FBUF_Y_SIZE);
213
214    // Load textures
215    g_tex[1] = dispLoadTexture("misc/rock_32.raw");
216    g_tex[2] = dispLoadTexture("misc/door_32.raw");
217    g_tex[3] = dispLoadTexture("misc/handle_32.raw");
218    g_tex[4] = dispLoadTexture("misc/wood_32.raw");
219
220    cur_buf = 0;
221    slice_cnt = 0;
222    slice_x = FBUF_X_SIZE;
223}
224
225int dispRenderSlice(Game *game)
226{
227    unsigned int x;
228    int type;
229    float angle, dist, tx;
230
231    sqt_lock_acquire(&slice_x_lock);
232
233    if (slice_x == FBUF_X_SIZE) {
234        // No more work to do for this frame
235        sqt_lock_release(&slice_x_lock);
236        return 0;
237    }
238    else {
239        // Keep slice coordinate
240        x = slice_x++;
241    }
242
243    sqt_lock_release(&slice_x_lock);
244
245    angle = game->player.dir - FIELD_OF_VIEW / 2.f +
246            x * FIELD_OF_VIEW / FBUF_X_SIZE;
247
248    // Cast a ray to get wall distance
249    dist = dispRaycast(game, &type, &tx, angle);
250
251    // Perspective correction
252    dist *= cos(game->player.dir - angle);
253
254    // Draw ceiling, wall and floor
255    dispDrawSlice(game, x, FBUF_Y_SIZE / dist, type, tx);
256
257    // Signal this slice is done
258    atomic_increment((unsigned int*)&slice_cnt, 1);
259
260    return 1;
261}
262
263void dispRender(Game *game)
264{
265    int start = giet_proctime();
266
267    // Start rendering
268    slice_cnt = 0;
269    slice_x = 0;
270
271    // Render slices
272    while (dispRenderSlice(game));
273
274    // Wait for completion
275    while (slice_cnt != FBUF_X_SIZE);
276
277    // Flip framebuffer
278    giet_fbf_cma_display(cur_buf);
279    cur_buf = !cur_buf;
280    giet_tty_printf("[RAYCAST] flip (took %d cycles)\n", giet_proctime() - start);
281}
282
Note: See TracBrowser for help on using the repository browser.