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

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

raycast: precompute ymax

2087497 -> 2021308 cycles/frame (-3.2%) 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    int ymax = (y1 < FBUF_Y_SIZE ? y1 : FBUF_Y_SIZE);
40
41    for (; y < ymax; y++) {
42        // Find texture coordinate
43        int ty = (y - y0) * TEX_SIZE / (y1 - y0);
44
45        buf[cur_buf][y * FBUF_X_SIZE + x] = line[ty];
46    }
47}
48
49static void dispDrawColumnSolid(int x, int y0, int y1, unsigned char color)
50{
51    int y = (y0 >= 0 ? y0 : 0);
52    int ymax = (y1 < FBUF_Y_SIZE ? y1 : FBUF_Y_SIZE);
53
54    for (; y < ymax; y++) {
55        buf[cur_buf][y * FBUF_X_SIZE + x] = color;
56    }
57}
58
59static void dispDrawSlice(Game *game, int x, int height, int type, int tx)
60{
61    // Ceiling
62    dispDrawColumnSolid(x,
63                        0,
64                        (FBUF_Y_SIZE - height) / 2,
65                        CEILING_COLOR);
66
67    // Wall
68    unsigned char *tex = g_tex[type];
69
70    if (tex) {
71        // Draw a texture slice
72        dispDrawColumnTex(x,
73                          (FBUF_Y_SIZE - height) / 2,
74                          (FBUF_Y_SIZE + height) / 2,
75                          &tex[tx * TEX_SIZE]);
76    }
77    else {
78        // Draw a solid color slice
79        dispDrawColumnSolid(x,
80                            (FBUF_Y_SIZE - height) / 2,
81                            (FBUF_Y_SIZE + height) / 2,
82                            0xFF);
83    }
84
85    // Floor
86    dispDrawColumnSolid(x,
87                        (FBUF_Y_SIZE + height) / 2,
88                        FBUF_Y_SIZE,
89                        FLOOR_COLOR);
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
164static void dispMirror(unsigned char *buf, unsigned int size)
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
181static unsigned char *dispLoadTexture(char *path)
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
193    giet_fat_read(fd, tex, TEX_SIZE * TEX_SIZE);
194    giet_fat_close(fd);
195
196    dispMirror(tex, TEX_SIZE);
197
198    giet_tty_printf("[RAYCAST] loaded tex %s\n", path);
199
200    return tex;
201}
202
203// Exported functions
204
205void dispInit()
206{
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
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);
218
219    // Initialize framebuffer
220    giet_fbf_cma_alloc();
221    giet_fbf_cma_init_buf(buf[0], buf[1], sts[0], sts[1]);
222    giet_fbf_cma_start(FBUF_X_SIZE * FBUF_Y_SIZE);
223
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");
229
230    cur_buf = 0;
231    slice_cnt = 0;
232    slice_x = FBUF_X_SIZE;
233}
234
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
265    dispDrawSlice(game, x, FBUF_Y_SIZE / dist, type, tx * TEX_SIZE);
266
267    // Signal this slice is done
268    atomic_increment((unsigned int*)&slice_cnt, 1);
269
270    return 1;
271}
272
273void dispRender(Game *game)
274{
275    int start = giet_proctime();
276
277    // Start rendering
278    slice_cnt = 0;
279    slice_x = 0;
280
281    // Render slices
282    while (dispRenderSlice(game));
283
284    // Wait for completion
285    while (slice_cnt != FBUF_X_SIZE);
286
287    // Flip framebuffer
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);
291}
292
Note: See TracBrowser for help on using the repository browser.