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