2 blender for the Gameboy Advance
3 Copyright (C) 2021 John Tsiombikas <nuclear@member.fsf.org>
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.
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.
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/>.
33 SIDX_DEL0, SIDX_DEL1, SIDX_DEL2,
44 #define SIDX_ICONS_BASE SIDX_ICON_ZOOM
46 #define SNAM_START 512
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
62 #define MENU_HEIGHT 17
63 #define TRACK_HEIGHT 18
64 #define VP_HEIGHT (160 - MENU_HEIGHT - TRACK_HEIGHT)
66 static void handle_keys(void);
67 static void upd_rotation(void);
68 static void show_msgbox(int en);
70 extern struct { unsigned char r, g, b; } bgimg_cmap[];
71 extern unsigned char bgimg_pixels[];
73 static int32_t cam_theta = 0x10000, cam_phi = -0x8000;
74 static int32_t cam_pan_x, cam_pan_y;
76 static int show_obj = 1, show_del;
77 static int32_t rot_matrix[16];
80 static int snam_time[26];
81 static int spos_time[104];
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},
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},
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}
117 static uint16_t oam[4 * MAX_SIDX];
124 unsigned int nframes = 0, backbuf;
126 unsigned char r, g, b;
127 unsigned char *fbptr[2], *fb;
133 /* mode 4: 240x160 8bpp */
134 REG_DISPCNT = DISPCNT_BG2 | DISPCNT_OBJ | 4;
136 fbptr[0] = (unsigned char*)VRAM_LFB_FB0_ADDR;
137 fbptr[1] = (unsigned char*)VRAM_LFB_FB1_ADDR;
139 set_bg_color(0xff, 31, 31, 31);
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);
148 for(i=0; i<128; i++) {
150 *cptr++ = r | (r << 5) | (r << 10);
152 dma_copy16(3, fbptr[0], bgimg_pixels, 240 * 160 / 2, 0);
153 dma_copy16(3, fbptr[1], bgimg_pixels, 240 * 160 / 2, 0);
156 REG_BLDCNT = BLDCNT_ALPHA | BLDCNT_B_BG2;
157 REG_BLDALPHA = 0x040c;
159 for(i=0; i<13; i++) {
160 snam_time[i] = SNAM_TIME0 + (i << 1);
161 snam_time[i + 13] = SNAM_TIME1 + (i << 1);
163 for(i=0; i<104; i++) {
164 int t = (i << 16) / 103;
165 spos_time[i] = (t * 223) >> 16;
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);
174 xgl_enable(XGL_LIGHTING);
178 key_repeat(75, 75, KEY_LEFT | KEY_RIGHT | KEY_DOWN | KEY_UP | KEY_LT | KEY_RT);
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);*/
187 backbuf = ++nframes & 1;
189 fb = fbptr[backbuf] + 240 * MENU_HEIGHT;
190 polyfill_framebuffer(fb, 240, VP_HEIGHT);
191 dma_fill16(3, fb, 0x0e0e, 240 * VP_HEIGHT / 2);
193 xgl_viewport(0, 0, 240, VP_HEIGHT);
195 xgl_translate(-cam_pan_x, -cam_pan_y, 8 << 16);
196 xgl_mult_matrix(rot_matrix);
199 if(cam_theta < X_PI) {
200 xgl_draw(XGL_LINES, gridaxes + 2, 2); /* -X */
202 xgl_draw(XGL_LINES, gridaxes, 2); /* +X */
204 if(cam_theta < X_HPI || cam_theta > (3 * X_HPI)) {
205 xgl_draw(XGL_LINES, gridaxes + 4, 2); /* +Z */
207 xgl_draw(XGL_LINES, gridaxes + 6, 2); /* -Z */
210 xgl_draw(XGL_QUADS, cube, sizeof cube / sizeof *cube);
212 xgl_draw(XGL_TRIANGLES, suzanne, sizeof suzanne / sizeof *suzanne);
214 if(cam_theta < X_PI) {
215 xgl_draw(XGL_LINES, gridaxes, 2); /* +X */
217 xgl_draw(XGL_LINES, gridaxes + 2, 2); /* -X */
219 if(cam_theta < X_HPI || cam_theta > (3 * X_HPI)) {
220 xgl_draw(XGL_LINES, gridaxes + 6, 2); /* -Z */
222 xgl_draw(XGL_LINES, gridaxes + 4, 2); /* +Z */
225 xgl_draw(XGL_LINES, gridaxes + 8, 8);
229 xgl_viewport(176, 2, 64, 32);
231 xgl_draw(XGL_LINES, small_axes, 6);
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);
240 anm = (timer_msec >> 5) % 104;
241 set_sprite(oam, SIDX_TIME, snam_time[anm >> 2], spos_time[anm], 144, 5, SPR_SZ16);
245 dma_copy16(3, (void*)OAM_ADDR, oam, sizeof oam / 2, 0);
251 #define PAN_SPEED 0x4000
253 static void handle_keys(void)
257 if(KEYPRESS(KEY_UP)) {
259 cam_pan_y += PAN_SPEED;
260 if(cam_pan_y > 0x1c000) cam_pan_y = 0x1c000;
263 if(cam_phi > X_HPI) cam_phi = X_HPI;
267 if(KEYPRESS(KEY_DOWN)) {
269 cam_pan_y -= PAN_SPEED;
270 if(cam_pan_y < -0x1c000) cam_pan_y = -0x1c000;
273 if(cam_phi < -X_HPI) cam_phi = -X_HPI;
277 if(KEYPRESS(KEY_LEFT)) {
279 cam_pan_x -= PAN_SPEED;
280 if(cam_pan_x < -0x50000) cam_pan_x = -0x50000;
283 if(cam_theta > X_2PI) cam_theta -= X_2PI;
287 if(KEYPRESS(KEY_RIGHT)) {
289 cam_pan_x += PAN_SPEED;
290 if(cam_pan_x > 0x50000) cam_pan_x = 0x50000;
293 if(cam_theta < 0) cam_theta += X_2PI;
298 if(KEYPRESS(KEY_START)) {
301 if(KEYPRESS(KEY_SELECT)) {
303 show_obj = ((show_obj - 1) ^ 1) + 1;
309 if(KEYPRESS(KEY_RT)) {
310 cam_pan_x += PAN_SPEED;
312 if(KEYPRESS(KEY_LT)) {
313 cam_pan_x -= PAN_SPEED;
316 if(KEYPRESS(KEY_A)) {
318 if(show_obj) show_del = 1;
322 show_msgbox(show_del);
324 if(KEYPRESS(KEY_B)) {
333 static void upd_rotation(void)
336 xgl_rotate_x(cam_phi);
337 xgl_rotate_y(cam_theta);
338 xgl_get_matrix(rot_matrix);
341 static void show_msgbox(int en)
347 set_sprite(oam, SIDX_DEL0 + i, SNAM_DEL0 + i * 8, 42 + i * 64, 50, 0,
348 SPR_SZ64 | SPR_BLEND);
352 set_sprite(oam, SIDX_DEL0 + i, 0, 0, 0, 0, 0);