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

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

raycast: remove branch in draw loops

2364988 -> 2170519 cycles/frame (-8.9%) w/ 4 cpus, 512x512

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