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

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

raycast: dynamically allocate framebuffer

With a 512x512 fb, the generated .elf file was too large for the FAT32
driver to load. Now that we can allocate aligned memory blocks, the .elf
size is greatly reduced.

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