minor control tweaks and fixes
[gba_blender] / src / main.c
1 /*
2 blender for the Gameboy Advance
3 Copyright (C) 2021  John Tsiombikas <nuclear@member.fsf.org>
4
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <https://www.gnu.org/licenses/>.
17 */
18 #include <stdlib.h>
19 #include <string.h>
20 #include "gbaregs.h"
21 #include "timer.h"
22 #include "keyb.h"
23 #include "intr.h"
24 #include "gfx.h"
25 #include "xgl.h"
26 #include "polyfill.h"
27 #include "debug.h"
28 #include "meshdata.h"
29 #include "sprites.h"
30 #include "dma.h"
31
32 enum {
33         SIDX_DEL0, SIDX_DEL1, SIDX_DEL2,
34         SIDX_TIME,
35         SIDX_ICON_ZOOM,
36         SIDX_ICON_PAN,
37         SIDX_ICON_ORBIT,
38         SIDX_ICON_X,
39         SIDX_ICON_Y,
40         SIDX_ICON_Z,
41         SIDX_DIRTY
42 };
43
44 #define SIDX_ICONS_BASE         SIDX_ICON_ZOOM
45
46 #define SNAM_START      512
47 enum {
48         SNAM_DEL0               = SNAM_START,
49         SNAM_DEL1               = SNAM_DEL0 + 8,
50         SNAM_DEL2               = SNAM_DEL1 + 8,
51         SNAM_ICON_ZOOM  = SNAM_START + 24,
52         SNAM_ICON_PAN   = SNAM_ICON_ZOOM + 4,
53         SNAM_ICON_ORBIT = SNAM_START + 32 * 4 + 24,     /* for tiles down, 24 across */
54         SNAM_ICON_X             = SNAM_START + 32 * 8 + 26,
55         SNAM_ICON_Y             = SNAM_ICON_X + 2,
56         SNAM_ICON_Z             = SNAM_ICON_Y + 2,
57         SNAM_DIRTY              = SNAM_ICON_ORBIT + 4,
58         SNAM_TIME0              = SNAM_START + 32 * 8,
59         SNAM_TIME1              = SNAM_TIME0 + 32 * 2
60 };
61
62 #define MENU_HEIGHT             17
63 #define TRACK_HEIGHT    18
64 #define VP_HEIGHT               (160 - MENU_HEIGHT - TRACK_HEIGHT)
65
66 static void handle_keys(void);
67 static void upd_rotation(void);
68 static void show_msgbox(int en);
69
70 extern struct { unsigned char r, g, b; } bgimg_cmap[];
71 extern unsigned char bgimg_pixels[];
72
73 static int32_t cam_theta = 0x10000, cam_phi = -0x8000;
74 static int32_t cam_pan_x, cam_pan_y;
75
76 static int show_obj = 1, show_del;
77 static int32_t rot_matrix[16];
78 static int pan_mode;
79
80 static int snam_time[26];
81 static int spos_time[104];
82
83 #define AXIS0   0x10000
84 #define AXIS1   0x80000
85 #define AXIS3   0x2c000
86
87 static struct xvertex gridaxes[] = {
88         {AXIS0, 0, 0,   0, 0, 0, 92},
89         {AXIS1, 0, 0,   0, 0, 0, 92},
90         {-AXIS0, 0, 0,  0, 0, 0, 92},
91         {-AXIS1, 0, 0,  0, 0, 0, 92},
92         {0, 0, AXIS0,   0, 0, 0, 93},
93         {0, 0, AXIS1,   0, 0, 0, 93},
94         {0, 0, -AXIS0,  0, 0, 0, 93},
95         {0, 0, -AXIS1,  0, 0, 0, 93},
96
97         {0, 0, 0,               0, 0, 0, 92},
98         {AXIS1, 0, 0,   0, 0, 0, 92},
99         {0, 0, 0,               0, 0, 0, 92},
100         {-AXIS1, 0, 0,  0, 0, 0, 92},
101         {0, 0, 0,               0, 0, 0, 93},
102         {0, 0, AXIS1,   0, 0, 0, 93},
103         {0, 0, -0,              0, 0, 0, 93},
104         {0, 0, -AXIS1,  0, 0, 0, 93},
105 };
106
107 static struct xvertex small_axes[] = {
108         {0, 0, 0,               0, 0, 0, 92},
109         {AXIS3, 0, 0,   0, 0, 0, 92},
110         {0, 0, 0,               0, 0, 0, 93},
111         {0, 0, AXIS3,   0, 0, 0, 93},
112         {0, 0, 0,               0, 0, 0, 94},
113         {0, AXIS3, 0,   0, 0, 0, 94}
114 };
115
116 #define MAX_SIDX        16
117 static uint16_t oam[4 * MAX_SIDX];
118
119
120 int main(void)
121 {
122         int i;
123         int x, y, anm;
124         unsigned int nframes = 0, backbuf;
125         uint16_t *cptr;
126         unsigned char r, g, b;
127         unsigned char *fbptr[2], *fb;
128
129         intr_init();
130         reset_msec_timer();
131         set_intr();
132
133         /* mode 4: 240x160 8bpp */
134         REG_DISPCNT = DISPCNT_BG2 | DISPCNT_OBJ | 4;
135
136         fbptr[0] = (unsigned char*)VRAM_LFB_FB0_ADDR;
137         fbptr[1] = (unsigned char*)VRAM_LFB_FB1_ADDR;
138
139         set_bg_color(0xff, 31, 31, 31);
140
141         cptr = (uint16_t*)CRAM_BG_ADDR;
142         for(i=0; i<128; i++) {
143                 r = bgimg_cmap[i].r >> 3;
144                 g = bgimg_cmap[i].g >> 3;
145                 b = bgimg_cmap[i].b >> 3;
146                 *cptr++ = r | (g << 5) | (b << 10);
147         }
148         for(i=0; i<128; i++) {
149                 r = i / 5 + 6;
150                 *cptr++ = r | (r << 5) | (r << 10);
151         }
152         dma_copy16(3, fbptr[0], bgimg_pixels, 240 * 160 / 2, 0);
153         dma_copy16(3, fbptr[1], bgimg_pixels, 240 * 160 / 2, 0);
154
155         init_sprites();
156         REG_BLDCNT = BLDCNT_ALPHA | BLDCNT_B_BG2;
157         REG_BLDALPHA = 0x040c;
158
159         for(i=0; i<13; i++) {
160                 snam_time[i] = SNAM_TIME0 + (i << 1);
161                 snam_time[i + 13] = SNAM_TIME1 + (i << 1);
162         }
163         for(i=0; i<104; i++) {
164                 int t = (i << 16) / 103;
165                 spos_time[i] = (t * 223) >> 16;
166         }
167
168         set_sprite(oam, SIDX_ICONS_BASE, SNAM_ICON_ZOOM, 213, 57, 4, SPR_SZ32 | SPR_BLEND);
169         set_sprite(oam, SIDX_ICONS_BASE + 1, SNAM_ICON_PAN, 213, 81, 4, SPR_SZ32 | SPR_BLEND);
170         set_sprite(oam, SIDX_ICONS_BASE + 2, SNAM_ICON_ORBIT, 213, 103, 4, SPR_SZ32 | SPR_BLEND);
171         set_sprite(oam, SIDX_DIRTY, SNAM_DIRTY, 192, 9, 4, SPR_SZ16 | SPR_HRECT);
172
173         xgl_init();
174         xgl_enable(XGL_LIGHTING);
175
176         upd_rotation();
177
178         key_repeat(75, 75, KEY_LEFT | KEY_RIGHT | KEY_DOWN | KEY_UP | KEY_LT | KEY_RT);
179
180         /* every vblank, copy the shadow OAM automatically */
181         /*dma_copy16(3, (void*)OAM_ADDR, oam, sizeof oam / 2, DMACNT_VBLANK |
182                         DMACNT_REPEAT | DMACNT_INC_RELOAD);*/
183
184         for(;;) {
185                 handle_keys();
186
187                 backbuf = ++nframes & 1;
188
189                 fb = fbptr[backbuf] + 240 * MENU_HEIGHT;
190                 polyfill_framebuffer(fb, 240, VP_HEIGHT);
191                 dma_fill16(3, fb, 0x0e0e, 240 * VP_HEIGHT / 2);
192
193                 xgl_viewport(0, 0, 240, VP_HEIGHT);
194                 xgl_load_identity();
195                 xgl_translate(-cam_pan_x, -cam_pan_y, 8 << 16);
196                 xgl_mult_matrix(rot_matrix);
197
198                 if(show_obj) {
199                         if(cam_theta < X_PI) {
200                                 xgl_draw(XGL_LINES, gridaxes + 2, 2);   /* -X */
201                         } else {
202                                 xgl_draw(XGL_LINES, gridaxes, 2);               /* +X */
203                         }
204                         if(cam_theta < X_HPI || cam_theta > (3 * X_HPI)) {
205                                 xgl_draw(XGL_LINES, gridaxes + 4, 2);   /* +Z */
206                         } else {
207                                 xgl_draw(XGL_LINES, gridaxes + 6, 2);   /* -Z */
208                         }
209                         if(show_obj == 1) {
210                                 xgl_draw(XGL_QUADS, cube, sizeof cube / sizeof *cube);
211                         } else {
212                                 xgl_draw(XGL_TRIANGLES, suzanne, sizeof suzanne / sizeof *suzanne);
213                         }
214                         if(cam_theta < X_PI) {
215                                 xgl_draw(XGL_LINES, gridaxes, 2);               /* +X */
216                         } else {
217                                 xgl_draw(XGL_LINES, gridaxes + 2, 2);   /* -X */
218                         }
219                         if(cam_theta < X_HPI || cam_theta > (3 * X_HPI)) {
220                                 xgl_draw(XGL_LINES, gridaxes + 6, 2);   /* -Z */
221                         } else {
222                                 xgl_draw(XGL_LINES, gridaxes + 4, 2);   /* +Z */
223                         }
224                 } else {
225                         xgl_draw(XGL_LINES, gridaxes + 8, 8);
226                 }
227
228                 /* small axes */
229                 xgl_viewport(176, 2, 64, 32);
230                 xgl_xyzzy();
231                 xgl_draw(XGL_LINES, small_axes, 6);
232
233                 xgl_transform(small_axes + 5, &x, &y);
234                 set_sprite(oam, SIDX_ICONS_BASE + 3, SNAM_ICON_Z, x - 8, y + 8, 3, SPR_SZ16);
235                 xgl_transform(small_axes + 1, &x, &y);
236                 set_sprite(oam, SIDX_ICONS_BASE + 4, SNAM_ICON_X, x - 8, y + 8, 1, SPR_SZ16);
237                 xgl_transform(small_axes + 3, &x, &y);
238                 set_sprite(oam, SIDX_ICONS_BASE + 5, SNAM_ICON_Y, x - 8, y + 8, 2, SPR_SZ16);
239
240                 anm = (timer_msec >> 5) % 104;
241                 set_sprite(oam, SIDX_TIME, snam_time[anm >> 2], spos_time[anm], 144, 5, SPR_SZ16);
242
243                 wait_vblank();
244                 present(backbuf);
245                 dma_copy16(3, (void*)OAM_ADDR, oam, sizeof oam / 2, 0);
246         }
247
248         return 0;
249 }
250
251 #define PAN_SPEED       0x4000
252 #define MAX_XPAN        0x50000
253 #define MAX_YPAN        0x1c000
254
255 static void handle_keys(void)
256 {
257         update_keyb();
258
259         if(KEYPRESS(KEY_UP)) {
260                 if(pan_mode) {
261                         cam_pan_y += PAN_SPEED;
262                         if(cam_pan_y > MAX_YPAN) cam_pan_y = MAX_YPAN;
263                 } else {
264                         cam_phi += 0x2000;
265                         if(cam_phi > X_HPI) cam_phi = X_HPI;
266                         upd_rotation();
267                 }
268         }
269         if(KEYPRESS(KEY_DOWN)) {
270                 if(pan_mode) {
271                         cam_pan_y -= PAN_SPEED;
272                         if(cam_pan_y < -MAX_YPAN) cam_pan_y = -MAX_YPAN;
273                 } else {
274                         cam_phi -= 0x2000;
275                         if(cam_phi < -X_HPI) cam_phi = -X_HPI;
276                         upd_rotation();
277                 }
278         }
279         if(KEYPRESS(KEY_LEFT)) {
280                 if(pan_mode) {
281                         cam_pan_x -= PAN_SPEED;
282                         if(cam_pan_x < -MAX_XPAN) cam_pan_x = -MAX_XPAN;
283                 } else {
284                         cam_theta += 0x2000;
285                         if(cam_theta > X_2PI) cam_theta -= X_2PI;
286                         upd_rotation();
287                 }
288         }
289         if(KEYPRESS(KEY_RIGHT)) {
290                 if(pan_mode) {
291                         cam_pan_x += PAN_SPEED;
292                         if(cam_pan_x > MAX_XPAN) cam_pan_x = MAX_XPAN;
293                 } else {
294                         cam_theta -= 0x2000;
295                         if(cam_theta < 0) cam_theta += X_2PI;
296                         upd_rotation();
297                 }
298         }
299
300         if(KEYPRESS(KEY_START)) {
301                 pan_mode ^= 1;
302         }
303         if(KEYPRESS(KEY_SELECT)) {
304                 if(show_obj) {
305                         show_obj = ((show_obj - 1) ^ 1) + 1;
306                 } else {
307                         show_obj = 1;
308                 }
309         }
310
311         if(KEYPRESS(KEY_RT)) {
312                 cam_pan_x += PAN_SPEED;
313                 if(cam_pan_x > 0x50000) cam_pan_x = 0x50000;
314         }
315         if(KEYPRESS(KEY_LT)) {
316                 cam_pan_x -= PAN_SPEED;
317                 if(cam_pan_x < -0x50000) cam_pan_x = -0x50000;
318         }
319
320         if(KEYPRESS(KEY_A)) {
321                 if(!show_del) {
322                         if(show_obj) {
323                                 show_del = 1;
324                         } else {
325                                 show_obj = 1;
326                         }
327                 } else {
328                         show_del = 0;
329                 }
330                 show_msgbox(show_del);
331         }
332         if(KEYPRESS(KEY_B)) {
333                 if(show_del) {
334                         show_obj = 0;
335                         show_del = 0;
336                         show_msgbox(0);
337                 }
338         }
339 }
340
341 static void upd_rotation(void)
342 {
343         xgl_load_identity();
344         xgl_rotate_x(cam_phi);
345         xgl_rotate_y(cam_theta);
346         xgl_get_matrix(rot_matrix);
347 }
348
349 static void show_msgbox(int en)
350 {
351         int i;
352
353         if(en) {
354                 for(i=0; i<3; i++) {
355                         set_sprite(oam, SIDX_DEL0 + i, SNAM_DEL0 + i * 8, 42 + i * 64, 50, 0,
356                                         SPR_SZ64 | SPR_BLEND);
357                 }
358         } else {
359                 for(i=0; i<3; i++) {
360                         set_sprite(oam, SIDX_DEL0 + i, 0, 0, 0, 0, 0);
361                 }
362         }
363 }