a1e141e7f89ba4e8a4246ed64c5130d010dff921
[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_height;
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_height = himg;
61         vox_color = cimg;
62
63         vox_vheight = 80;
64
65         return 0;
66 }
67
68 void vox_destroy(void)
69 {
70         free(vox_color);
71         free(vox_height);
72         free(vox_coltop);
73         free(vox_slicelen);
74 }
75
76 #define H(x, y) \
77         vox_height[((((y) >> 16) & YMASK) << XSHIFT) + (((x) >> 16) & XMASK)]
78 #define C(x, y) \
79         vox_color[((((y) >> 16) & YMASK) << XSHIFT) + (((x) >> 16) & XMASK)]
80
81 void vox_framebuf(int xres, int yres, void *fb, int horizon)
82 {
83         if(!vox_coltop) {
84                 if(!(vox_coltop = iwram_sbrk(xres * sizeof *vox_coltop))) {
85                         panic(get_pc(), "vox_framebuf: failed to allocate column table (%d)\n", xres);
86                 }
87         }
88         vox_fb = fb;
89         vox_horizon = horizon >= 0 ? horizon : (FBHEIGHT >> 1);
90 }
91
92 void vox_view(int32_t x, int32_t y, int h, int32_t angle)
93 {
94         if(h < 0) {
95                 h = H(x, y) - h;
96         }
97
98         vox_x = x;
99         vox_y = y;
100         vox_vheight = h;
101         vox_angle = angle;
102
103         vox_valid &= ~SLICELEN;
104 }
105
106 void vox_proj(int fov, int znear, int zfar)
107 {
108         vox_fov = fov;
109         vox_znear = znear;
110         vox_zfar = zfar;
111
112         vox_nslices = vox_zfar - vox_znear;
113         if(!vox_slicelen) {
114                 if(!(vox_slicelen = iwram_sbrk(vox_nslices * sizeof *vox_slicelen))) {
115                         panic(get_pc(), "vox_proj: failed to allocate slice length table (%d)\n", vox_nslices);
116                 }
117                 if(!(projlut = iwram_sbrk(vox_nslices * sizeof *projlut))) {
118                         panic(get_pc(), "vox_framebuf: failed to allocate projection table (%d)\n", vox_nslices);
119                 }
120         }
121
122         vox_valid &= ~SLICELEN;
123 }
124
125 /* algorithm:
126  * calculate extents of horizontal equidistant line from the viewer based on fov
127  * for each column step along this line and compute height for each pixel
128  * fill the visible (top) part of each column
129  */
130 ARM_IWRAM
131 void vox_render(void)
132 {
133         int i;
134
135         vox_begin();
136
137         for(i=0; i<vox_nslices; i++) {
138                 vox_render_slice(i);
139         }
140 }
141
142 ARM_IWRAM
143 void vox_begin(void)
144 {
145         int i;
146
147         memset(vox_coltop, 0, FBWIDTH * sizeof *vox_coltop);
148
149         if(!(vox_valid & SLICELEN)) {
150                 float theta = (float)vox_fov * M_PI / 360.0f;   /* half angle */
151                 for(i=0; i<vox_nslices; i++) {
152                         vox_slicelen[i] = (int32_t)((vox_znear + i) * tan(theta) * 4.0f * 65536.0f);
153                         projlut[i] = (HSCALE << 8) / (vox_znear + i);
154                 }
155                 vox_valid |= SLICELEN;
156         }
157 }
158
159 ARM_IWRAM
160 void vox_render_slice(int n)
161 {
162         int i, j, hval, last_hval, colstart, colheight, col, z, offs, last_offs = -1;
163         int32_t x, y, len, xstep, ystep;
164         uint8_t color, last_col;
165         uint16_t *fbptr;
166         /*int proj;*/
167         struct vox_object *obj;
168
169         z = vox_znear + n;
170
171         len = vox_slicelen[n] >> 8;
172         xstep = (((COS(vox_angle) >> 4) * len) >> 4) / (FBWIDTH / 2);
173         ystep = (((SIN(vox_angle) >> 4) * len) >> 4) / (FBWIDTH / 2);
174
175         x = vox_x - SIN(vox_angle) * z - xstep * (FBWIDTH / 4);
176         y = vox_y + COS(vox_angle) * z - ystep * (FBWIDTH / 4);
177
178         /*proj = (HSCALE << 8) / (vox_znear + n);*/
179
180         for(i=0; i<FBWIDTH/2; i++) {
181                 col = i << 1;
182                 offs = (((y >> 16) & YMASK) << XSHIFT) + ((x >> 16) & XMASK);
183                 if(offs == last_offs) {
184                         hval = last_hval;
185                         color = last_col;
186                 } else {
187                         hval = vox_height[offs] - vox_vheight;
188                         hval = ((hval * projlut[n]) >> 8) + vox_horizon;
189                         if(hval > FBHEIGHT) hval = FBHEIGHT;
190                         color = vox_color[offs];
191                         last_offs = offs;
192                         last_hval = hval;
193                         last_col = color;
194                 }
195                 if(hval >= vox_coltop[col]) {
196                         colstart = FBHEIGHT - hval;
197                         colheight = hval - vox_coltop[col];
198                         fbptr = vox_fb + colstart * (FBPITCH / 2) + i;
199
200                         for(j=0; j<colheight; j++) {
201                                 *fbptr = color | ((uint16_t)color << 8);
202                                 fbptr += FBPITCH / 2;
203                         }
204                         vox_coltop[col] = hval;
205
206                         /* check to see if there's an object here */
207                         if(color >= CMAP_SPAWN0) {
208                                 int idx = color - CMAP_SPAWN0;
209                                 obj = (struct vox_object*)((char*)vox_obj + (idx << OBJ_STRIDE_SHIFT));
210                                 obj->px = col;
211                                 obj->py = colstart;
212                                 obj->scale = projlut[n];
213                         }
214                 }
215                 x += xstep;
216                 y += ystep;
217         }
218 }
219
220 ARM_IWRAM
221 void vox_sky_solid(uint8_t color)
222 {
223         int i, j, colheight;
224         uint16_t *fbptr;
225
226         for(i=0; i<FBWIDTH / 2; i++) {
227                 fbptr = vox_fb + i;
228                 colheight = FBHEIGHT - vox_coltop[i << 1];
229
230                 for(j=0; j<colheight; j++) {
231                         *fbptr = color | ((uint16_t)color << 8);
232                         fbptr += FBPITCH / 2;
233                 }
234         }
235 }
236
237 ARM_IWRAM
238 void vox_sky_grad(uint8_t chor, uint8_t ctop)
239 {
240         int i, j, colheight, t;
241         int d = FBHEIGHT - vox_horizon;
242         uint8_t grad[FBHEIGHT];
243         uint16_t *fbptr;
244
245         for(i=0; i<d; i++) {
246                 t = (i << 16) / d;
247                 grad[i] = XLERP(ctop, chor, t, 16);
248         }
249         for(i=d; i<FBHEIGHT; i++) {
250                 grad[i] = chor;
251         }
252
253         for(i=0; i<FBWIDTH / 2; i++) {
254                 fbptr = vox_fb + i;
255                 colheight = FBHEIGHT - vox_coltop[i << 1];
256
257                 for(j=0; j<colheight; j++) {
258                         *fbptr = grad[j] | ((uint16_t)grad[j] << 8);
259                         fbptr += FBPITCH / 2;
260                 }
261         }
262 }
263
264 void vox_objects(struct vox_object *ptr, int count, int stride)
265 {
266         int i;
267         struct vox_object *obj;
268
269         if(stride != 1 << OBJ_STRIDE_SHIFT) {
270                 panic(get_pc(), "optimization requires %d byte vox obj (got %d)",
271                                 1 << OBJ_STRIDE_SHIFT, stride);
272         }
273
274         vox_obj = ptr;
275         vox_num_obj = count;
276         vox_obj_stride = stride;
277
278         obj = ptr;
279         for(i=0; i<count; i++) {
280                 obj->offs = obj->y * XSZ + obj->x;
281                 obj = (struct vox_object*)((char*)obj + stride);
282         }
283 }