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

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

raycast: draw textures

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