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

Last change on this file since 680 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
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>
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
[679]16static unsigned char* buf[2];
17static void *         sts[2];
18static unsigned int   cur_buf;
[673]19
20// Textures indexed by block number
[676]21static unsigned char *g_tex[] =
[673]22{
23    NULL, // 0
[676]24    NULL, // rock
25    NULL, // door
26    NULL, // handle
27    NULL, // wood
[673]28};
29
30// Local functions
31
[677]32static void dispDrawColumnTex(int x, int y0, int y1, unsigned char *tex, int tx)
[673]33{
[677]34    int y;
[673]35
[677]36    if (x < 0 || x >= FBUF_X_SIZE)
37        return;
[673]38
[677]39    for (y = y0; y < y1 && y < FBUF_Y_SIZE; y++) {
40        if (y < 0)
41            y = 0;
[673]42
[677]43        int ty = (y - y0) * TEX_SIZE / (y1 - y0);
44
[679]45        buf[cur_buf][y * FBUF_X_SIZE + x] = tex[ty * TEX_SIZE + tx];
[677]46    }
[673]47}
48
[677]49static void dispDrawColumnSolid(int x, int y0, int y1, unsigned char color)
[673]50{
[677]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
[679]60        buf[cur_buf][y * FBUF_X_SIZE + x] = color;
[677]61    }
62}
63
64static void dispDrawSlice(Game *game, int x, int height, int type, float tx)
65{
[673]66    // Ceiling
[677]67    dispDrawColumnSolid(x,
68                        0,
69                        (FBUF_Y_SIZE - height) / 2,
70                        CEILING_COLOR);
[673]71
72    // Wall
[676]73    unsigned char *tex = g_tex[type];
[673]74
75    if (tex) {
76        // Draw a texture slice
[677]77        dispDrawColumnTex(x,
78                          (FBUF_Y_SIZE - height) / 2,
79                          (FBUF_Y_SIZE + height) / 2,
80                          tex,
81                          (int)(tx * TEX_SIZE));
[673]82    }
83    else {
84        // Draw a solid color slice
[677]85        dispDrawColumnSolid(x,
86                            (FBUF_Y_SIZE - height) / 2,
87                            (FBUF_Y_SIZE + height) / 2,
88                            0xFF);
[673]89    }
90
91    // Floor
[677]92    dispDrawColumnSolid(x,
93                        (FBUF_Y_SIZE + height) / 2,
94                        FBUF_Y_SIZE,
95                        FLOOR_COLOR);
[673]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
[677]170static unsigned char *loadTexture(char *path)
[676]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
[677]182    giet_fat_read(fd, tex, TEX_SIZE * TEX_SIZE);
[676]183    giet_fat_close(fd);
184    giet_tty_printf("[RAYCAST] loaded tex %s\n", path);
185
186    return tex;
187}
188
[673]189// Exported functions
190
191void dispInit()
192{
[679]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
[676]199    // initialize framebuffer
[673]200    giet_fbf_cma_alloc();
[679]201    giet_fbf_cma_init_buf(buf[0], buf[1], sts[0], sts[1]);
[673]202    giet_fbf_cma_start(FBUF_X_SIZE * FBUF_Y_SIZE);
203
[676]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
[677]210    cur_buf = 0;
[673]211}
212
213void dispRender(Game *game)
214{
[677]215    int start = giet_proctime();
[673]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
[677]232        dispDrawSlice(game, i, FBUF_Y_SIZE / dist, type, tx);
[673]233
234        angle += FIELD_OF_VIEW / FBUF_X_SIZE;
235    }
236
[677]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);
[673]240}
Note: See TracBrowser for help on using the repository browser.