#define __BSD_VISIBLE 1 #include #include #include #include #include "chaos-drop.h" #if defined(AZUR_TOOLKIT_SDL) #include #include #include #include "backend/linux/programs.h" static uint16_t vram[VWIDTH * VHEIGHT]; std::unique_ptr shader_texture = nullptr; static GLuint tex_vram; static void view_update(void) { SDL_Window *window = azur_sdl_window(); int width, height; SDL_GetWindowSize(window, &width, &height); /* Transform from pixel coordinates within the winfow to GL coordinates */ glm::mat3 tr_pixel2gl( 2.0f / width, 0.0f, 0.0f, 0.0f, -2.0f / height, 0.0f, -1.0f, 1.0f, 1.0f); glUseProgram(shader_texture->prog); shader_texture->set_uniform("u_transform", tr_pixel2gl); } static void init(void) { shader_texture = std::make_unique(); view_update(); memset(vram, 0x55, sizeof vram); glGenTextures(1, &tex_vram); glBindTexture(GL_TEXTURE_2D, tex_vram); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, VWIDTH, VHEIGHT, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, vram); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } static void quit(void) { } static int platform_update(struct input *input) { SDL_Event e; *input = (struct input){}; while(SDL_PollEvent(&e)) { // ImGui_ImplSDL2_ProcessEvent(&e); // render_needed = std::max(render_needed, 1); if(e.type == SDL_QUIT) return 1; if(e.type == SDL_WINDOWEVENT && e.window.event == SDL_WINDOWEVENT_SIZE_CHANGED) { view_update(); } } Uint8 const *state = SDL_GetKeyboardState(NULL); input->up = state[SDL_SCANCODE_UP]; input->down = state[SDL_SCANCODE_DOWN]; input->left = state[SDL_SCANCODE_LEFT]; input->right = state[SDL_SCANCODE_RIGHT]; input->roll_left = state[SDL_SCANCODE_F1]; input->roll_right = state[SDL_SCANCODE_F2]; return 0; } static void platform_render(void) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, VWIDTH, VHEIGHT, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, vram); SDL_Window *window = azur_sdl_window(); shader_texture->vertices.clear(); shader_texture->add_texture(0, 0, 3*VWIDTH, 3*VHEIGHT); shader_texture->draw(); SDL_GL_SwapWindow(window); } static void draw_text(int x, int y, char const *str, int size) { // TODO: SDL: Text rendering } static int text_size(char const *str, int length) { // TODO: SDL: Text rendering return 0; } #elif defined(AZUR_TOOLKIT_GINT) #include #include #include #include #include #include #define vram gint_vram static int platform_update(struct input *input) { key_event_t e; while((e = pollevent()).type != KEYEV_NONE) { if(e.type == KEYEV_UP) continue; if(e.key == KEY_EXIT) return 1; if(e.key == KEY_MENU) gint_osmenu(); if(e.key == KEY_OPTN) input->OPTN = true; if(e.key == KEY_F1) input->roll_left = true; if(e.key == KEY_F2) input->roll_right = true; if(e.key == KEY_EXE && keydown(KEY_VARS)) input->RESET_LEVEL = true; if(e.key == KEY_MINUS && keydown(KEY_VARS)) input->PREV_LEVEL = true; if(e.key == KEY_PLUS && keydown(KEY_VARS)) input->NEXT_LEVEL = true; } input->left = keydown(KEY_LEFT); input->right = keydown(KEY_RIGHT); input->up = keydown(KEY_UP); input->down = keydown(KEY_DOWN); return 0; } static void platform_render(void) { azrp_update(); } static void init(void) { prof_init(); azrp_config_scale(2); cd_raytrace_configure(); azrp_shader_clear_configure(); azrp_shader_image_p8_configure(); } static void quit(void) { prof_quit(); } static uint8_t const font_glyph_width[96] = { 3,1,3,5,5,4,5,2,2,2,3,5,2,5,2,3, 4,4,4,4,4,4,4,4,4,4,2,2,3,4,3,4, 5,4,4,4,4,4,4,4,4,1,4,4,4,5,4,4, 4,4,4,4,5,4,5,5,5,5,4,2,3,2,5,4, 2,4,4,4,4,4,4,4,4,1,2,4,3,5,4,4, 4,4,4,4,4,4,5,5,5,4,4,3,1,3,5,4, }; /* Very bad text renderer */ static void draw_text(int x, int y, char const *str, int size) { extern bopti_image_t img_font; for(int i = 0; str[i] && i < size; i++) { if(str[i] < 32 || str[i] >= 0x7f) continue; int row = (str[i] - 32) >> 4; int col = (str[i] - 32) & 15; int gw = font_glyph_width[str[i] - 32]; azrp_subimage(x, y, &img_font, 7 * col + 1, 9 * row + 1, gw, 8, DIMAGE_NONE); x += gw + 1; } } static int text_size(char const *str, int length) { int total = 0; for(int i = 0; str[i] && i < length; i++) { if(str[i] < 32 || str[i] >= 0x7f) continue; total += font_glyph_width[str[i] - 32] + 1; } return total - (total > 0); } #endif mat3 operator *(mat3 const &A, mat3 const &B) { mat3 C; C.x11 = A.x11 * B.x11 + A.x12 * B.x21 + A.x13 * B.x31; C.x12 = A.x11 * B.x12 + A.x12 * B.x22 + A.x13 * B.x32; C.x13 = A.x11 * B.x13 + A.x12 * B.x23 + A.x13 * B.x33; C.x21 = A.x21 * B.x11 + A.x22 * B.x21 + A.x23 * B.x31; C.x22 = A.x21 * B.x12 + A.x22 * B.x22 + A.x23 * B.x32; C.x23 = A.x21 * B.x13 + A.x22 * B.x23 + A.x23 * B.x33; C.x31 = A.x31 * B.x11 + A.x32 * B.x21 + A.x33 * B.x31; C.x32 = A.x31 * B.x12 + A.x32 * B.x22 + A.x33 * B.x32; C.x33 = A.x31 * B.x13 + A.x32 * B.x23 + A.x33 * B.x33; return C; } vec3 operator * (mat3 const &M, vec3 const &u) { vec3 v; v.x = M.x11 * u.x + M.x12 * u.y + M.x13 * u.z; v.y = M.x21 * u.x + M.x22 * u.y + M.x23 * u.z; v.z = M.x31 * u.x + M.x32 * u.y + M.x33 * u.z; return v; } __attribute__((unused)) static void print_text(int x, int y, char const *fmt, ...) { static char str[128]; va_list args; va_start(args, fmt); vsnprintf(str, sizeof str, fmt, args); va_end(args); draw_text(x, y, str, strlen(str)); } void render_centered_text(char const *str) { /* Count lines */ int lines = 1; for(int i = 0; str[i]; i++) lines += str[i] == '\n'; static int const line_height = 10; int y = (VHEIGHT - line_height * lines) / 2; /* Draw each line centered */ char const *line = str; char const *endline; while(*line) { endline = strchrnul(line, '\n'); int w = text_size(line, endline - line); draw_text((VWIDTH - w) / 2, y, line, endline - line); line = endline + (*endline != 0); y += line_height; } } static struct camera *camera = NULL; static struct world *world = NULL; static int reset_frames = 0; static bool debug = false; #ifdef AZUR_TOOLKIT_GINT extern struct level level_1_1, level_1_2, level_1_3; extern struct level level_2_1, level_2_2, level_2_3; extern struct level level_3_1, level_3_2, level_3_3; static struct level const *levels[] = { &level_1_1, &level_1_2, &level_1_3, &level_2_1, &level_2_2, &level_2_3, }; #else /* Basic basic level */ static struct level const lv_test = { .name = "", .finish = num(1024), .mirror_count = 1, .mirrors = (struct element_mirror[]){ { .begin = 256, .end = 768 }, }, .plane_count = 2, .planes = (struct element_plane[]){ { .y = 384, .shape = 0b01010'10101'01010'10101'01010, .type = ELEMENT_PLANE_DAMAGE, .data = 0 }, { .y = 384+64, .shape = 0b11111'10001'10001'10001'11111, .type = ELEMENT_PLANE_DAMAGE, .data = 0 }, }, .text_count = 1, .texts = (struct element_text[]){ { .begin = 32, .end = 256, .str = "Red stuff bad!\nDon't touch the red stuff!", }, }, }; static struct level const *levels[] = { &lv_test }; #endif static int current_level = 0; void load_level_number(int number) { int total_number = sizeof levels / sizeof levels[0]; if(number < 0) number = 0; if(number > total_number) number = total_number - 1; current_level = number; } bool is_final_level(int number) { int total_number = sizeof levels / sizeof levels[0]; return number >= total_number - 1; } void world_reset(struct world *world) { world->neon_position = 0; world->depth = 0; world->neon_period = 64; world->mirror = NULL; world->plane = NULL; world->text = NULL; world->mirror_index = 0; world->plane_index = 0; world->text_index = 0; } void world_level_transition(struct world *world) { world->depth = world->depth % num(world->neon_period); world->mirror = NULL; world->plane = NULL; world->text = NULL; world->mirror_index = 0; world->plane_index = 0; world->text_index = 0; } void render(void) { struct level const *level = levels[current_level]; #ifdef AZUR_TOOLKIT_GINT azrp_perf_clear(); if(reset_frames > 0) { reset_frames--; azrp_clear(0xffff); } else { cd_raytrace(camera, world); if(world->text) render_centered_text(world->text->str); draw_text(1, 1, level->name, strlen(level->name)); } platform_render(); if(debug) { drect(0, DHEIGHT-20, DWIDTH-1, DHEIGHT-1, C_WHITE); dprint(4, 209, C_BLACK, "render:%4d+%4dus", prof_time(azrp_perf_render) - prof_time(azrp_perf_r61524), prof_time(azrp_perf_r61524)); r61524_display(gint_vram, DHEIGHT-20, 20, R61524_DMA_WAIT); } #else if(reset_frames > 0) { reset_frames--; memset(vram, 0xff, sizeof vram); } else { render_fragment(camera, world, vram, 0, VHEIGHT); if(world->text) render_centered_text(world->text->str); draw_text(1, 1, level->name, strlen(level->name)); } platform_render(); #endif } int update(void) { struct input input = {}; if(platform_update(&input)) return 1; /* Debug contros */ if(input.OPTN) debug = !debug; if(input.RESET_LEVEL) { world_reset(world); reset_frames = 2; } if(input.NEXT_LEVEL && !is_final_level(current_level)) { world_level_transition(world); load_level_number(current_level+1); reset_frames = 2; } if(input.PREV_LEVEL && current_level > 0) { world_level_transition(world); load_level_number(current_level-1); reset_frames = 2; } if(reset_frames > 0) return 0; static num const mv_speed = 0.45; static num const wall = WORLD_SIZE * 0.45; /* margin */ static float const rot_speed = 0.01; static float const rot_snap = 0.6; static float const rot_max = 0.2; static num const fall_speed = 2; static float const barrel_snap = 0.5; if(input.left) { camera->pos -= mv_speed * camera->right; camera->yaw = fmaxf(camera->yaw - rot_speed, -rot_max); } else if(input.right) { camera->pos += mv_speed * camera->right; camera->yaw = fminf(camera->yaw + rot_speed, rot_max); } else { camera->yaw *= rot_snap; } if(input.up) { camera->pos += mv_speed * camera->up; camera->pitch = fmaxf(camera->pitch - rot_speed, -rot_max); } else if(input.down) { camera->pos -= mv_speed * camera->up; camera->pitch = fminf(camera->pitch + rot_speed, rot_max); } else { camera->pitch *= rot_snap; } camera->pos.x = clamp(camera->pos.x, -wall, wall); camera->pos.z = clamp(camera->pos.z, -wall, wall); if(input.roll_left && !camera_rolling(camera)) { camera->roll_quadrant = (camera->roll_quadrant + 4 - 1) % 4; } else if(input.roll_right && !camera_rolling(camera)) { camera->roll_quadrant = (camera->roll_quadrant + 1) % 4; } camera_roll(camera, barrel_snap); camera_update_angles(camera); world->depth += fall_speed; world->neon_position += world->neon_period - num16(fall_speed); world->neon_position %= world->neon_period; // Remove old world elements struct world *w = world; if(w->mirror && w->depth > w->mirror->end) w->mirror = NULL; if(w->plane && w->depth > w->plane->y - num(4)) { /* Apply plane effect */ if((w->plane->type == ELEMENT_PLANE_DAMAGE || w->plane->type == ELEMENT_PLANE_INVIS) && plane_collides(num16(camera->pos.x), num16(camera->pos.z), w->plane->shape)) { world_reset(w); reset_frames = 2; } else if(w->plane->type == ELEMENT_PLANE_ARROW && camera->roll_quadrant != w->plane->data) { world_reset(w); reset_frames = 2; } w->plane = NULL; } if(w->text && w->depth > w->text->end) w->text = NULL; // Load incoming world elements struct level const *level = levels[current_level]; if(!w->mirror && w->mirror_index < level->mirror_count && level->mirrors[w->mirror_index].begin < w->depth + num(64)) { w->mirror = &level->mirrors[w->mirror_index++]; } if(!w->plane && w->plane_index < level->plane_count && level->planes[w->plane_index].y < w->depth + num(64)) { w->plane = &level->planes[w->plane_index++]; } if(!w->text && w->text_index < level->text_count && level->texts[w->text_index].begin < w->depth + num(64)) { w->text = &level->texts[w->text_index++]; } // End of level if(!w->mirror && !w->plane && !w->text && w->depth >= level->finish && !reset_frames) { if(is_final_level(current_level)) { // TODO: Main menu return 1; } else { world_level_transition(w); load_level_number(current_level + 1); } } return 0; } int main(void) { if(azur_init("Chaos Drop!", 3*VWIDTH, 3*VHEIGHT)) return 1; struct camera c = {}; camera = &c; /* TODO: Why do I need such a low FOV?! */ camera_set_fov(camera, 80.0); camera_update_angles(camera); struct world w = {}; load_level_number(0); world_reset(&w); world = &w; init(); int rc = azur_main_loop(render, 30, update, -1, AZUR_MAIN_LOOP_TIED); quit(); return rc; }