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

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

raycast: not mirroring, but transposing

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