add missing tools/pngdump to the repo
[gbajam22] / src / voxscape.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <string.h>
4 #include <stdint.h>
5 #include <math.h>
6 #include <assert.h>
7 #include "voxscape.h"
8 #include "debug.h"
9 #include "data.h"
10
11 /* hardcoded dimensions for the GBA */
12 #define FBWIDTH         240
13 #define FBHEIGHT        160
14 #define FBPITCH         240
15 /* map size */
16 #define XSZ                     512
17 #define YSZ                     512
18 #define XSHIFT          9
19 #define XMASK           0x1ff
20 #define YMASK           0x1ff
21 #define HSCALE          40
22
23 /* XXX */
24 #define OBJ_STRIDE_SHIFT        5
25
26 #define NO_LERP
27
28 #define XLERP(a, b, t, fp) \
29         ((((a) << (fp)) + ((b) - (a)) * (t)) >> fp)
30
31 enum {
32         SLICELEN        = 1
33 };
34
35 static unsigned char *vox_hmap;
36 static unsigned char *vox_color;
37 /* framebuffer */
38 static uint16_t *vox_fb;
39 static int *vox_coltop;
40 static int vox_horizon;
41 /* view */
42 static int32_t vox_x, vox_y, vox_angle;
43 static int vox_vheight;
44 /* projection */
45 static int vox_fov, vox_znear, vox_zfar;
46 static int vox_nslices;
47 static int32_t *vox_slicelen;
48
49 static unsigned int vox_valid;
50
51 static struct vox_object *vox_obj;
52 static int vox_num_obj, vox_obj_stride;
53
54 int *projlut;
55
56 int vox_init(int xsz, int ysz, uint8_t *himg, uint8_t *cimg)
57 {
58         assert(xsz == XSZ && ysz == YSZ);
59
60         vox_hmap = himg;
61         vox_color = cimg;
62
63         vox_fb = 0;
64         vox_coltop = 0;
65         vox_horizon = 0;
66         vox_x = vox_y = vox_angle = 0;
67         vox_fov = 0;
68         vox_znear = vox_zfar = 0;
69         vox_nslices = 0;
70         vox_slicelen = 0;
71         vox_valid = 0;
72         projlut = 0;
73
74         vox_vheight = 80;
75
76         return 0;
77 }
78
79 void vox_destroy(void)
80 {
81         /* XXX we rely on the screen to clear up any allocated IWRAM */
82 }
83
84 #define H(x, y) \
85         vox_hmap[((((y) >> 16) & YMASK) << XSHIFT) + (((x) >> 16) & XMASK)]
86 #define C(x, y) \
87         vox_color[((((y) >> 16) & YMASK) << XSHIFT) + (((x) >> 16) & XMASK)]
88
89 void vox_framebuf(int xres, int yres, void *fb, int horizon)
90 {
91         if(!vox_coltop) {
92                 if(!(vox_coltop = iwram_sbrk(xres * sizeof *vox_coltop))) {
93                         panic(get_pc(), "vox_framebuf: failed to allocate column table (%d)\n", xres);
94                 }
95         }
96         vox_fb = fb;
97         vox_horizon = horizon >= 0 ? horizon : (FBHEIGHT >> 1);
98 }
99
100 int vox_view(int32_t x, int32_t y, int h, int32_t angle)
101 {
102         if(h < 0) {
103                 h = H(x, y) - h;
104         }
105
106         vox_x = x;
107         vox_y = y;
108         vox_vheight = h;
109         vox_angle = angle;
110
111         vox_valid &= ~SLICELEN;
112
113         return h;
114 }
115
116 void vox_proj(int fov, int znear, int zfar)
117 {
118         vox_fov = fov;
119         vox_znear = znear;
120         vox_zfar = zfar;
121
122         vox_nslices = vox_zfar - vox_znear;
123         if(!vox_slicelen) {
124                 if(!(vox_slicelen = iwram_sbrk(vox_nslices * sizeof *vox_slicelen))) {
125                         panic(get_pc(), "vox_proj: failed to allocate slice length table (%d)\n", vox_nslices);
126                 }
127                 if(!(projlut = iwram_sbrk(vox_nslices * sizeof *projlut))) {
128                         panic(get_pc(), "vox_framebuf: failed to allocate projection table (%d)\n", vox_nslices);
129                 }
130         }
131
132         vox_valid &= ~SLICELEN;
133 }
134
135 /* algorithm:
136  * calculate extents of horizontal equidistant line from the viewer based on fov
137  * for each column step along this line and compute height for each pixel
138  * fill the visible (top) part of each column
139  */
140 ARM_IWRAM
141 void vox_render(void)
142 {
143         int i;
144
145         vox_begin();
146
147         for(i=0; i<vox_nslices; i++) {
148                 vox_render_slice(i);
149         }
150 }
151
152 ARM_IWRAM
153 void vox_begin(void)
154 {
155         int i;
156
157         memset(vox_coltop, 0, FBWIDTH * sizeof *vox_coltop);
158
159         if(!(vox_valid & SLICELEN)) {
160                 float theta = (float)vox_fov * M_PI / 360.0f;   /* half angle */
161                 for(i=0; i<vox_nslices; i++) {
162                         vox_slicelen[i] = (int32_t)((vox_znear + i) * tan(theta) * 4.0f * 65536.0f);
163                         projlut[i] = (HSCALE << 8) / (vox_znear + i);
164                 }
165                 vox_valid |= SLICELEN;
166         }
167 }
168
169 ARM_IWRAM
170 void vox_render_slice(int n)
171 {
172         int i, j, hval, last_hval, colstart, colheight, col, z, offs, last_offs = -1;
173         int32_t x, y, len, xstep, ystep;
174         uint8_t color, last_col;
175         uint16_t *fbptr;
176         /*int proj;*/
177         struct vox_object *obj;
178
179         z = vox_znear + n;
180
181         len = vox_slicelen[n] >> 8;
182         xstep = (((COS(vox_angle) >> 4) * len) >> 4) / (FBWIDTH / 2);
183         ystep = (((SIN(vox_angle) >> 4) * len) >> 4) / (FBWIDTH / 2);
184
185         x = vox_x - SIN(vox_angle) * z - xstep * (FBWIDTH / 4);
186         y = vox_y + COS(vox_angle) * z - ystep * (FBWIDTH / 4);
187
188         /*proj = (HSCALE << 8) / (vox_znear + n);*/
189
190         for(i=0; i<FBWIDTH/2; i++) {
191                 col = i << 1;
192                 offs = (((y >> 16) & YMASK) << XSHIFT) + ((x >> 16) & XMASK);
193                 if(offs == last_offs) {
194                         hval = last_hval;
195                         color = last_col;
196                 } else {
197                         hval = vox_hmap[offs] - vox_vheight;
198                         hval = ((hval * projlut[n]) >> 8) + vox_horizon;
199                         if(hval > FBHEIGHT) hval = FBHEIGHT;
200                         color = vox_color[offs];
201                         last_offs = offs;
202                         last_hval = hval;
203                         last_col = color;
204                 }
205                 if(hval >= vox_coltop[col]) {
206                         colstart = FBHEIGHT - hval;
207                         colheight = hval - vox_coltop[col];
208                         fbptr = vox_fb + colstart * (FBPITCH / 2) + i;
209
210                         for(j=0; j<colheight; j++) {
211                                 *fbptr = color | ((uint16_t)color << 8);
212                                 fbptr += FBPITCH / 2;
213                         }
214                         vox_coltop[col] = hval;
215
216                         /* check to see if there's an object here */
217                         if(color >= CMAP_SPAWN0) {
218                                 int idx = color - CMAP_SPAWN0;
219                                 obj = (struct vox_object*)((char*)vox_obj + (idx << OBJ_STRIDE_SHIFT));
220                                 obj->px = col;
221                                 obj->py = colstart;
222                                 obj->scale = projlut[n];
223                         }
224                 }
225                 x += xstep;
226                 y += ystep;
227         }
228 }
229
230 ARM_IWRAM
231 void vox_sky_solid(uint8_t color)
232 {
233         int i, j, colheight;
234         uint16_t *fbptr;
235
236         for(i=0; i<FBWIDTH / 2; i++) {
237                 fbptr = vox_fb + i;
238                 colheight = FBHEIGHT - vox_coltop[i << 1];
239
240                 for(j=0; j<colheight; j++) {
241                         *fbptr = color | ((uint16_t)color << 8);
242                         fbptr += FBPITCH / 2;
243                 }
244         }
245 }
246
247 ARM_IWRAM
248 void vox_sky_grad(uint8_t chor, uint8_t ctop)
249 {
250         int i, j, colheight, t;
251         int d = FBHEIGHT - vox_horizon;
252         uint8_t grad[FBHEIGHT];
253         uint16_t *fbptr;
254
255         for(i=0; i<d; i++) {
256                 t = (i << 16) / d;
257                 grad[i] = XLERP(ctop, chor, t, 16);
258         }
259         for(i=d; i<FBHEIGHT; i++) {
260                 grad[i] = chor;
261         }
262
263         for(i=0; i<FBWIDTH / 2; i++) {
264                 fbptr = vox_fb + i;
265                 colheight = FBHEIGHT - vox_coltop[i << 1];
266
267                 for(j=0; j<colheight; j++) {
268                         *fbptr = grad[j] | ((uint16_t)grad[j] << 8);
269                         fbptr += FBPITCH / 2;
270                 }
271         }
272 }
273
274 void vox_objects(struct vox_object *ptr, int count, int stride)
275 {
276         int i;
277         struct vox_object *obj;
278
279         if(stride != 1 << OBJ_STRIDE_SHIFT) {
280                 panic(get_pc(), "optimization requires %d byte vox obj (got %d)",
281                                 1 << OBJ_STRIDE_SHIFT, stride);
282         }
283
284         vox_obj = ptr;
285         vox_num_obj = count;
286         vox_obj_stride = stride;
287
288         obj = ptr;
289         for(i=0; i<count; i++) {
290                 obj->offs = obj->y * XSZ + obj->x;
291                 obj = (struct vox_object*)((char*)obj + stride);
292         }
293 }
294
295 int vox_height(int x, int y)
296 {
297         return H(x, y);
298 }
299
300 int vox_check_vis(int32_t x0, int32_t y0, int32_t x1, int32_t y1)
301 {
302         /* TODO */
303         return 0;
304 }