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

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

raycast: mirror textures

Mirror textures internally to use cache more efficiently. Instead of
accessing columns, we are now accessing rows which are contiguous.

2453934 -> 2364988 cycles/frame (-3.7%) w/ 4 cpus, 512x512

File size: 7.1 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 *line)
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] = line[ty];
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, int 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[tx * TEX_SIZE]);
85    }
86    else {
87        // Draw a solid color slice
88        dispDrawColumnSolid(x,
89                            (FBUF_Y_SIZE - height) / 2,
90                            (FBUF_Y_SIZE + height) / 2,
91                            0xFF);
92    }
93
94    // Floor
95    dispDrawColumnSolid(x,
96                        (FBUF_Y_SIZE + height) / 2,
97                        FBUF_Y_SIZE,
98                        FLOOR_COLOR);
99}
100
101static float dispRaycast(Game *game, int *type, float *tx, float angle)
102{
103    float x = game->player.x;
104    float y = game->player.y;
105
106    // Camera is inside a block.
107    // Return a minimal distance to avoid a division by zero.
108    if ((gameLocate(floor(x), floor(y))) != 0) {
109        *type = 0;
110        *tx = 0.f;
111        return 0.0001f;
112    }
113
114    // Precompute
115    float vsin = sin(angle);
116    float vcos = cos(angle);
117    float vtan = vsin / vcos;
118
119    // Calculate increments
120    int incix = (vcos > 0.f) - (vcos < 0.f);
121    int inciy = (vsin > 0.f) - (vsin < 0.f);
122    float incfx = inciy / vtan;
123    float incfy = incix * vtan;
124
125    // Calculate start position
126    int ix = floor(x) + (incix > 0);
127    int iy = floor(y) + (inciy > 0);
128    float fx = x + incfx * fabs(floor(y) + (inciy > 0) - y);
129    float fy = y + incfy * fabs(floor(x) + (incix > 0) - x);
130
131    // Find the first colliding tile in each direction
132    while (incix && gameLocate(ix - (incix < 0), fy) == 0)
133    {
134        ix += incix;
135        fy += incfy;
136    }
137    while (inciy && gameLocate(fx, iy - (inciy < 0)) == 0)
138    {
139        fx += incfx;
140        iy += inciy;
141    }
142
143    // Find the shortest ray
144    float dx = (incix) ? ((ix - x) / vcos) : 0xFFFF;
145    float dy = (inciy) ? ((iy - y) / vsin) : 0xFFFF;
146
147    if (dx < dy)
148    {
149        // Get block type
150        *type = gameLocate(ix - (incix < 0), floor(fy));
151
152        // Get wall texture coordinate [0;1]
153        *tx = fy - floor(fy);
154        if (incix < 0)
155            *tx = 1 - *tx;
156
157        return dx;
158    }
159    else
160    {
161        // Get block type
162        *type = gameLocate(floor(fx), iy - (inciy < 0));
163
164        // Get wall texture coordinate [0;1]
165        *tx = fx - floor(fx);
166        if (inciy > 0)
167            *tx = 1 - *tx;
168
169        return dy;
170    }
171}
172
173static void dispMirror(unsigned char *buf, unsigned int size)
174{
175    int i, j;
176    unsigned char pixel;
177
178    for (i = 0; i < size; i++) {
179        for (j = i + 1; j < size; j++) {
180            int ai = i * size + j;
181            int bi = j * size + i;
182
183            pixel = buf[ai];
184            buf[ai] = buf[bi];
185            buf[bi] = pixel;
186        }
187    }
188}
189
190static unsigned char *dispLoadTexture(char *path)
191{
192    int fd;
193    unsigned char *tex;
194
195    tex = malloc(TEX_SIZE * TEX_SIZE);
196    fd = giet_fat_open(path, O_RDONLY);
197    if (fd < 0) {
198        free(tex);
199        return NULL;
200    }
201
202    giet_fat_read(fd, tex, TEX_SIZE * TEX_SIZE);
203    giet_fat_close(fd);
204
205    dispMirror(tex, TEX_SIZE);
206
207    giet_tty_printf("[RAYCAST] loaded tex %s\n", path);
208
209    return tex;
210}
211
212// Exported functions
213
214void dispInit()
215{
216    unsigned int w, h, p;
217
218    // Initialize lock
219    giet_procs_number(&w, &h, &p);
220    sqt_lock_init(&slice_x_lock, w, h, p);
221
222    // Allocate framebuffer
223    buf[0] = almalloc(64, FBUF_X_SIZE * FBUF_Y_SIZE);
224    buf[1] = almalloc(64, FBUF_X_SIZE * FBUF_Y_SIZE);
225    sts[0] = almalloc(64, 64);
226    sts[1] = almalloc(64, 64);
227
228    // Initialize framebuffer
229    giet_fbf_cma_alloc();
230    giet_fbf_cma_init_buf(buf[0], buf[1], sts[0], sts[1]);
231    giet_fbf_cma_start(FBUF_X_SIZE * FBUF_Y_SIZE);
232
233    // Load textures
234    g_tex[1] = dispLoadTexture("misc/rock_32.raw");
235    g_tex[2] = dispLoadTexture("misc/door_32.raw");
236    g_tex[3] = dispLoadTexture("misc/handle_32.raw");
237    g_tex[4] = dispLoadTexture("misc/wood_32.raw");
238
239    cur_buf = 0;
240    slice_cnt = 0;
241    slice_x = FBUF_X_SIZE;
242}
243
244int dispRenderSlice(Game *game)
245{
246    unsigned int x;
247    int type;
248    float angle, dist, tx;
249
250    sqt_lock_acquire(&slice_x_lock);
251
252    if (slice_x == FBUF_X_SIZE) {
253        // No more work to do for this frame
254        sqt_lock_release(&slice_x_lock);
255        return 0;
256    }
257    else {
258        // Keep slice coordinate
259        x = slice_x++;
260    }
261
262    sqt_lock_release(&slice_x_lock);
263
264    angle = game->player.dir - FIELD_OF_VIEW / 2.f +
265            x * FIELD_OF_VIEW / FBUF_X_SIZE;
266
267    // Cast a ray to get wall distance
268    dist = dispRaycast(game, &type, &tx, angle);
269
270    // Perspective correction
271    dist *= cos(game->player.dir - angle);
272
273    // Draw ceiling, wall and floor
274    dispDrawSlice(game, x, FBUF_Y_SIZE / dist, type, tx * TEX_SIZE);
275
276    // Signal this slice is done
277    atomic_increment((unsigned int*)&slice_cnt, 1);
278
279    return 1;
280}
281
282void dispRender(Game *game)
283{
284    int start = giet_proctime();
285
286    // Start rendering
287    slice_cnt = 0;
288    slice_x = 0;
289
290    // Render slices
291    while (dispRenderSlice(game));
292
293    // Wait for completion
294    while (slice_cnt != FBUF_X_SIZE);
295
296    // Flip framebuffer
297    giet_fbf_cma_display(cur_buf);
298    cur_buf = !cur_buf;
299    giet_tty_printf("[RAYCAST] flip (took %d cycles)\n", giet_proctime() - start);
300}
301
Note: See TracBrowser for help on using the repository browser.