From bd57f345ed9dbed1d81683e48199626de2ea9044 Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Fri, 27 Jun 2025 10:18:39 -0700 Subject: Restructure project --- game/CMakeLists.txt | 22 --- game/src/game.c | 218 ----------------------- game/src/game.h | 21 --- game/src/plugins/CMakeLists.txt | 29 ---- game/src/plugins/plugin.h | 52 ------ game/src/plugins/pong.c | 237 ------------------------- game/src/plugins/texture_view.c | 144 ---------------- game/src/plugins/viewer.c | 374 ---------------------------------------- 8 files changed, 1097 deletions(-) delete mode 100644 game/CMakeLists.txt delete mode 100644 game/src/game.c delete mode 100644 game/src/game.h delete mode 100644 game/src/plugins/CMakeLists.txt delete mode 100644 game/src/plugins/plugin.h delete mode 100644 game/src/plugins/pong.c delete mode 100644 game/src/plugins/texture_view.c delete mode 100644 game/src/plugins/viewer.c (limited to 'game') diff --git a/game/CMakeLists.txt b/game/CMakeLists.txt deleted file mode 100644 index 3a88bb7..0000000 --- a/game/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -cmake_minimum_required(VERSION 3.0) - -add_subdirectory(src/plugins) - -project(game) - -add_executable(game - src/game.c) - -target_include_directories(game PRIVATE - src/) - -target_link_libraries(game PRIVATE - cstring - error - gfx - gfx-app - list - log - math - mempool - plugin) diff --git a/game/src/game.c b/game/src/game.c deleted file mode 100644 index 51f5cbe..0000000 --- a/game/src/game.c +++ /dev/null @@ -1,218 +0,0 @@ -/* - * Main game module with entry point and game loop. - * - * The game module sets up the window and GL context and defers the core game - * logic to a plugin. - */ -#define _GNU_SOURCE 200112L // For readlink() - -#include "game.h" - -#include "plugins/plugin.h" - -#include -#include -#include -#include - -#include -#include -#include - -#include -#include -#include -#include - -#include - -#include - -#undef _GNU_SOURCE - -static const int WIDTH = 1350; -static const int HEIGHT = 900; -static const int MAX_FPS = 60; - -typedef struct GfxAppState { - Game game; -} GfxAppState; - -/// Initialize the game's plugin. -static bool init_plugin(Game* game) { - assert(game); - assert(game->plugin); - // Plugin state is allowed to be null, either when the plugin does not - // expose an init() or when init() does not initialize a state. - if (plugin_resolve(game->plugin, plugin_init, "init")) { - State* plugin_state = 0; - if (!plugin_call(game->plugin, plugin_init, "init", game, &plugin_state)) { - return false; - } - set_plugin_state(game->plugin, plugin_state); - } - return true; // Plugin does not need to expose an init(). -} - -/// Shutdown the game's plugin. -/// The game's plugin is allowed to be null in the call to this function. -static void shutdown_plugin(Game* game) { - assert(game); - if (game->plugin && - (plugin_resolve(game->plugin, plugin_shutdown, "shutdown"))) { - void* plugin_state = get_plugin_state(game->plugin); - plugin_call(game->plugin, plugin_shutdown, "shutdown", game, plugin_state); - set_plugin_state(game->plugin, 0); - } -} - -/// Boot the game's plugin. -static bool boot_plugin(Game* game) { - assert(game); - assert(game->plugin); - if (plugin_resolve(game->plugin, plugin_boot, "boot")) { - void* plugin_state = get_plugin_state(game->plugin); - return plugin_call(game->plugin, plugin_boot, "boot", game, plugin_state); - } - return true; // Plugin does not need to expose a boot(). -} - -/// Update the plugin's state. -static void update_plugin(Game* game, double t, double dt) { - assert(game); - assert(game->plugin); - if (plugin_resolve(game->plugin, plugin_update, "update")) { - void* plugin_state = get_plugin_state(game->plugin); - plugin_call( - game->plugin, plugin_update, "update", game, plugin_state, t, dt); - } -} - -/// Plugin render. -static void render_plugin(const Game* game) { - assert(game); - assert(game->plugin); - if (plugin_resolve(game->plugin, plugin_render, "render")) { - void* plugin_state = get_plugin_state(game->plugin); - plugin_call(game->plugin, plugin_render, "render", game, plugin_state); - } -} - -/// Plugin resize. -static void resize_plugin(Game* game, int width, int height) { - assert(game); - assert(game->plugin); - if (plugin_resolve(game->plugin, plugin_resize, "resize")) { - void* plugin_state = get_plugin_state(game->plugin); - plugin_call( - game->plugin, plugin_resize, "resize", game, plugin_state, width, - height); - } -} - -static void Shutdown(Game* game); - -static bool Init(Game* game, int argc, const char** argv) { - assert(game); - - if (argc <= 1) { - LOGE("Usage: %s [plugin args]", argv[0]); - return false; - } - - // Syntax: game [plugin args] - // - // Here we consume the arg so that plugins receive the remainder - // args starting from 0. - game->argc = argc - 1; - game->argv = argv + 1; - - char exe_path_buf[NAME_MAX] = {0}; - if (readlink("/proc/self/exe", exe_path_buf, sizeof(exe_path_buf)) == -1) { - LOGE("readlink(/proc/self/exe) failed"); - goto cleanup; - } - - // Replace the last / with a null terminator to remove the exe file from the - // path. This gets the file's parent directory. - *strrchr(exe_path_buf, '/') = 0; - - const mstring exe_dir = mstring_make(exe_path_buf); - const mstring plugins_path = mstring_concat_cstr(exe_dir, "/src/plugins"); - - if (!(game->plugin_engine = new_plugin_engine( - &(PluginEngineDesc){.plugins_dir = mstring_cstr(&plugins_path)}))) { - goto cleanup; - } - - const char* plugin = argv[1]; - if (!(game->plugin = load_plugin(game->plugin_engine, plugin))) { - goto cleanup; - } - - if (!(game->gfx = gfx_init())) { - goto cleanup; - } - - if (!init_plugin(game)) { - goto cleanup; - } - if (!boot_plugin(game)) { - goto cleanup; - } - - return true; - -cleanup: - LOGE("Gfx error: %s", get_error()); - Shutdown(game); - return false; -} - -static void Shutdown(Game* game) { - assert(game); - shutdown_plugin(game); - if (game->gfx) { - gfx_destroy(&game->gfx); - } - if (game->plugin) { - delete_plugin(&game->plugin); - } - if (game->plugin_engine) { - delete_plugin_engine(&game->plugin_engine); - } -} - -static void Update(Game* game, double t, double dt) { - plugin_engine_update(game->plugin_engine); - if (plugin_reloaded(game->plugin)) { - shutdown_plugin(game); - const bool result = init_plugin(game); - assert(result); // TODO: handle error better. - - // Trigger a resize just like the initial resize that occurs when the gfx - // application starts. - resize_plugin(game, game->width, game->height); - } - - update_plugin(game, t, dt); -} - -static void Render(const Game* game) { - GfxCore* gfxcore = gfx_get_core(game->gfx); - gfx_start_frame(gfxcore); - render_plugin(game); - gfx_end_frame(gfxcore); -} - -static void Resize(Game* game, int width, int height) { - game->width = width; - game->height = height; - - GfxCore* gfxcore = gfx_get_core(game->gfx); - gfx_set_viewport(gfxcore, 0, 0, width, height); - - resize_plugin(game, width, height); -} - -GFX_APP_MAIN(WIDTH, HEIGHT, MAX_FPS, "Game"); diff --git a/game/src/game.h b/game/src/game.h deleted file mode 100644 index 579ba3c..0000000 --- a/game/src/game.h +++ /dev/null @@ -1,21 +0,0 @@ -/* - * Header file defining the game state, included by plugins. - */ -#pragma once - -typedef struct PluginEngine PluginEngine; -typedef struct Plugin Plugin; -typedef struct Gfx Gfx; -typedef struct Scene Scene; -typedef struct SceneCamera SceneCamera; - -/// Game state. -typedef struct { - int argc; - const char** argv; - PluginEngine* plugin_engine; - Plugin* plugin; - Gfx* gfx; - int width; - int height; -} Game; diff --git a/game/src/plugins/CMakeLists.txt b/game/src/plugins/CMakeLists.txt deleted file mode 100644 index 8661598..0000000 --- a/game/src/plugins/CMakeLists.txt +++ /dev/null @@ -1,29 +0,0 @@ -cmake_minimum_required(VERSION 3.0) - -project(plugins) - -set(LINK_LIBRARIES cstring math gfx gfx-app) - -# Viewer - -add_library(viewer SHARED - viewer.c) - -target_link_libraries(viewer PUBLIC - ${LINK_LIBRARIES}) - -# Texture viewer - -add_library(texture_view SHARED - texture_view.c) - -target_link_libraries(texture_view PUBLIC - ${LINK_LIBRARIES}) - -# Pong - -add_library(pong SHARED - pong.c) - -target_link_libraries(pong PUBLIC - ${LINK_LIBRARIES}) diff --git a/game/src/plugins/plugin.h b/game/src/plugins/plugin.h deleted file mode 100644 index f7219c6..0000000 --- a/game/src/plugins/plugin.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * Game plugin. - */ -#pragma once - -#include "../game.h" - -#include -#include - -#include - -typedef struct State State; - -/// Initialize the plugin, which may optionally return a state object. -/// -/// This function is called every time the plugin is (re)loaded. -/// -/// It is assumed that the plugin's state is fully encapsulated in the returned -/// state object. The plugin should not store any (mutable) state outside of the -/// returned state object (e.g., no mutable global variables.) -bool init(Game*, State**); - -/// Shut down the plugin. -/// -/// This function is called before the plugin is unloaded. -/// -/// The plugin should perform any destruction needed, but not free the state -/// object; freeing the state object's memory is handled by the caller. -void shutdown(Game*, State*); - -/// Function called the first time the plugin is loaded throughout the -/// application's lifetime. This allows the plugin to do one-time initialization -/// of the game state. -bool boot(Game*, State*); - -/// Update the plugin's and the game's state. -void update(Game*, State*, double t, double dt); - -/// Render hook. -void render(const Game*, const State*); - -/// Called when the game's window is resized. -void resize(Game*, State*, int width, int height); - -// Signatures for the plugin's exposed functions. -typedef bool (*plugin_init)(Game*, State**); -typedef bool (*plugin_shutdown)(Game*, State*); -typedef bool (*plugin_boot)(Game*, State*); -typedef void (*plugin_update)(Game*, State*, double t, double dt); -typedef void (*plugin_render)(const Game*, const State*); -typedef void (*plugin_resize)(Game* game, State* state, int width, int height); diff --git a/game/src/plugins/pong.c b/game/src/plugins/pong.c deleted file mode 100644 index c1c55be..0000000 --- a/game/src/plugins/pong.c +++ /dev/null @@ -1,237 +0,0 @@ -#include "plugin.h" - -#include -#include -#include - -#include -#include -#include - -#include - -static const vec2 PAD_SIZE = (vec2){120, 20}; -static const R PLAYER_Y_OFFSET = 50; -static const R PLAYER_SPEED = 800; - -static const R ENEMY_SPEED = 2; - -static const R BALL_SIZE = 18; -static const R BALL_SPEED = 360; // In each dimension. - -static const R EPS = (R)1e-3; - -typedef struct Player { - vec2 position; -} Player; - -typedef struct Ball { - vec2 position; - vec2 velocity; -} Ball; - -typedef struct State { - bool game_started; - Player human; - Player enemy; - Ball ball; - mat4 viewProjection; -} State; - -bool init(Game* game, State** pp_state) { - assert(game); - - State* state = calloc(1, sizeof(State)); - if (!state) { - return false; - } - - *pp_state = state; - return true; - -cleanup: - free(state); - return false; -} - -void shutdown(Game* game, State* state) { - assert(game); - assert(state); -} - -static void move_ball(Ball* ball, R dt, int width, int height) { - assert(ball); - - const R offset = BALL_SIZE / 2; - - ball->position = vec2_add(ball->position, vec2_scale(ball->velocity, dt)); - - // Right wall. - if (ball->position.x + offset > (R)width) { - ball->position.x = (R)width - offset - EPS; - ball->velocity.x = -ball->velocity.x; - } - // Left wall. - else if (ball->position.x - offset < 0) { - ball->position.x = offset + EPS; - ball->velocity.x = -ball->velocity.x; - } - // Top wall. - if (ball->position.y + offset > (R)height) { - ball->position.y = (R)height - offset - EPS; - ball->velocity.y = -ball->velocity.y; - } - // Bottom wall. - else if (ball->position.y - offset < 0) { - ball->position.y = offset + EPS; - ball->velocity.y = -ball->velocity.y; - } -} - -void move_enemy_player(int width, Player* player, R t) { - const R half_width = (R)width / 2; - const R amplitude = half_width - (PAD_SIZE.x / 2); - player->position.x = half_width + amplitude * sinf(t * ENEMY_SPEED); -} - -void move_human_player(Player* player, R dt) { - assert(player); - - R speed = 0; - if (gfx_app_is_key_pressed('a')) { - speed -= PLAYER_SPEED; - } - if (gfx_app_is_key_pressed('d')) { - speed += PLAYER_SPEED; - } - - player->position.x += speed * dt; -} - -void clamp_player(Player* player, int width) { - assert(player); - - const R offset = PAD_SIZE.x / 2; - - // Left wall. - if (player->position.x + offset > (R)width) { - player->position.x = (R)width - offset; - } - // Right wall. - else if (player->position.x - offset < 0) { - player->position.x = offset; - } -} - -void collide_ball(vec2 old_ball_position, const Player* player, Ball* ball) { - assert(player); - assert(ball); - - // Discrete but simple collision. Checks for intersection and moves the ball - // back by a small epsilon. - - // Player bounding box. - const vec2 player_pmin = vec2_make( - player->position.x - PAD_SIZE.x / 2, player->position.y - PAD_SIZE.y / 2); - const vec2 player_pmax = vec2_make( - player->position.x + PAD_SIZE.x / 2, player->position.y + PAD_SIZE.y / 2); - - // Ball bounding box. - const vec2 ball_pmin = vec2_make( - ball->position.x - BALL_SIZE / 2, ball->position.y - BALL_SIZE / 2); - const vec2 ball_pmax = vec2_make( - ball->position.x + BALL_SIZE / 2, ball->position.y + BALL_SIZE / 2); - - // Check for intersection and update ball. - if (!((ball_pmax.x < player_pmin.x) || (ball_pmin.x > player_pmax.x) || - (ball_pmax.y < player_pmin.y) || (ball_pmin.y > player_pmax.y))) { - ball->position = - vec2_add(old_ball_position, vec2_scale(ball->velocity, -EPS)); - ball->velocity.y = -ball->velocity.y; - } -} - -void update(Game* game, State* state, double t, double dt) { - assert(game); - assert(state); - - // TODO: Move game width/height to GfxApp query functions? - const vec2 old_ball_position = state->ball.position; - move_ball(&state->ball, (R)dt, game->width, game->height); - move_human_player(&state->human, (R)dt); - move_enemy_player(game->width, &state->enemy, (R)t); - clamp_player(&state->human, game->width); - collide_ball(old_ball_position, &state->human, &state->ball); - collide_ball(old_ball_position, &state->enemy, &state->ball); -} - -static void draw_player(ImmRenderer* imm, const Player* player) { - assert(imm); - assert(player); - - const vec2 half_box = vec2_div(PAD_SIZE, vec2_make(2, 2)); - - const vec2 pmin = vec2_sub(player->position, half_box); - const vec2 pmax = vec2_add(player->position, half_box); - const aabb2 box = aabb2_make(pmin, pmax); - - gfx_imm_draw_aabb2(imm, box); -} - -static void draw_ball(ImmRenderer* imm, const Ball* ball) { - assert(imm); - assert(ball); - - const vec2 half_box = vec2_make(BALL_SIZE / 2, BALL_SIZE / 2); - const vec2 pmin = vec2_sub(ball->position, half_box); - const vec2 pmax = vec2_add(ball->position, half_box); - const aabb2 box = aabb2_make(pmin, pmax); - - gfx_imm_draw_aabb2(imm, box); -} - -void render(const Game* game, const State* state) { - assert(game); - assert(state); - - ImmRenderer* imm = gfx_get_imm_renderer(game->gfx); - gfx_imm_start(imm); - gfx_imm_set_view_projection_matrix(imm, &state->viewProjection); - gfx_imm_load_identity(imm); - gfx_imm_set_colour(imm, vec4_make(1, 1, 1, 1)); - draw_player(imm, &state->human); - draw_player(imm, &state->enemy); - draw_ball(imm, &state->ball); - gfx_imm_end(imm); -} - -static R clamp_to_width(int width, R x, R extent) { - return min(x, (R)width - extent); -} - -void resize(Game* game, State* state, int width, int height) { - assert(game); - assert(state); - - state->viewProjection = mat4_ortho(0, (R)width, 0, (R)height, -1, 1); - - state->human.position.y = PLAYER_Y_OFFSET; - state->enemy.position.y = (R)height - PLAYER_Y_OFFSET; - - if (!state->game_started) { - state->human.position.x = (R)width / 2; - state->enemy.position.x = (R)width / 2; - - state->ball.position = - vec2_div(vec2_make((R)width, (R)height), vec2_make(2, 2)); - - state->ball.velocity = vec2_make(BALL_SPEED, BALL_SPEED); - - state->game_started = true; - } else { - state->human.position.x = - clamp_to_width(width, state->human.position.x, PAD_SIZE.x / 2); - state->enemy.position.x = - clamp_to_width(width, state->enemy.position.x, PAD_SIZE.x / 2); - } -} diff --git a/game/src/plugins/texture_view.c b/game/src/plugins/texture_view.c deleted file mode 100644 index a8b2a94..0000000 --- a/game/src/plugins/texture_view.c +++ /dev/null @@ -1,144 +0,0 @@ -#include "plugin.h" - -#include -#include -#include -#include -#include -#include - -#include - -#include -#include - -// Default texture to load if no texture is provided. -static const char* DEFAULT_TEXTURE = "/assets/skybox/clouds1/clouds1_west.bmp"; -// static const char* DEFAULT_TEXTURE = "/assets/checkerboard.jpg"; - -struct State { - Scene* scene; - SceneCamera* camera; -}; - -bool init(Game* game, State** pp_state) { - assert(game); - assert(pp_state); - - State* state = calloc(1, sizeof(State)); - if (!state) { - goto cleanup; - } - - // Usage: [texture file] - const char* texture_file = game->argc > 1 ? game->argv[1] : DEFAULT_TEXTURE; - - GfxCore* gfxcore = gfx_get_core(game->gfx); - - const Texture* texture = gfx_load_texture( - game->gfx, &(LoadTextureCmd){ - .origin = AssetFromFile, - .type = LoadTexture, - .filtering = LinearFiltering, - .mipmaps = false, - .data.texture.filepath = mstring_make(texture_file)}); - if (!texture) { - goto cleanup; - } - - ShaderProgram* shader = gfx_make_view_texture_shader(gfxcore); - if (!shader) { - goto cleanup; - } - - Geometry* geometry = gfx_make_quad_11(gfxcore); - if (!geometry) { - goto cleanup; - } - - MaterialDesc material_desc = (MaterialDesc){.num_uniforms = 1}; - material_desc.uniforms[0] = (ShaderUniform){ - .type = UniformTexture, - .value.texture = texture, - .name = sstring_make("Texture")}; - Material* material = gfx_make_material(&material_desc); - if (!material) { - goto cleanup; - } - - const MeshDesc mesh_desc = - (MeshDesc){.geometry = geometry, .material = material, .shader = shader}; - Mesh* mesh = gfx_make_mesh(&mesh_desc); - if (!mesh) { - goto cleanup; - } - - SceneObject* object = - gfx_make_object(&(ObjectDesc){.num_meshes = 1, .meshes = {mesh}}); - if (!object) { - goto cleanup; - } - - if (!(state->scene = gfx_make_scene())) { - goto cleanup; - } - - SceneNode* node = gfx_make_object_node(object); - if (!node) { - goto cleanup; - } - SceneNode* root = gfx_get_scene_root(state->scene); - if (!root) { - goto cleanup; - } - gfx_set_node_parent(node, root); - - if (!(state->camera = gfx_make_camera())) { - goto cleanup; - } - - *pp_state = state; - return true; - -cleanup: - shutdown(game, state); - if (state) { - free(state); - } - return false; -} - -void shutdown(Game* game, State* state) { - assert(game); - if (state) { - gfx_destroy_camera(&state->camera); - gfx_destroy_scene(&state->scene); - // State freed by plugin engine. - } -} - -void render(const Game* game, const State* state) { - assert(game); - assert(state); - - Renderer* renderer = gfx_get_renderer(game->gfx); - gfx_render_scene( - renderer, &(RenderSceneParams){ - .mode = RenderDefault, - .scene = state->scene, - .camera = state->camera}); -} - -void resize(Game* game, State* state, int width, int height) { - assert(game); - assert(state); - - const R fovy = 90 * TO_RAD; - const R aspect = (R)width / (R)height; - const R near = 0.1; - const R far = 1000; - const mat4 projection = mat4_perspective(fovy, aspect, near, far); - - Camera* camera = gfx_get_camera_camera(state->camera); - camera->projection = projection; -} diff --git a/game/src/plugins/viewer.c b/game/src/plugins/viewer.c deleted file mode 100644 index 23b9ffb..0000000 --- a/game/src/plugins/viewer.c +++ /dev/null @@ -1,374 +0,0 @@ -#include "plugin.h" - -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -// Skybox. -static const char* skybox[6] = { - "/home/jeanne/Nextcloud/assets/textures/skybox/clouds1/clouds1_east.bmp", - "/home/jeanne/Nextcloud/assets/textures/skybox/clouds1/clouds1_west.bmp", - "/home/jeanne/Nextcloud/assets/textures/skybox/clouds1/clouds1_up.bmp", - "/home/jeanne/Nextcloud/assets/textures/skybox/clouds1/clouds1_down.bmp", - "/home/jeanne/Nextcloud/assets/textures/skybox/clouds1/clouds1_south.bmp", - "/home/jeanne/Nextcloud/assets/textures/skybox/clouds1/clouds1_north.bmp", -}; - -// Paths to various scene files. -static const char* BOX = "/home/jeanne/Nextcloud/assets/models/box.gltf"; -static const char* SUZANNE = - "/home/jeanne/Nextcloud/assets/models/suzanne.gltf"; -static const char* SPONZA = "/home/jeanne/Nextcloud/assets/glTF-Sample-Models/" - "2.0/Sponza/glTF/Sponza.gltf"; -static const char* FLIGHT_HELMET = - "/home/jeanne/Nextcloud/assets/glTF-Sample-Models/2.0/FlightHelmet/glTF/" - "FlightHelmet.gltf"; -static const char* DAMAGED_HELMET = - "/home/jeanne/Nextcloud/assets/glTF-Sample-Models/2.0/DamagedHelmet/glTF/" - "DamagedHelmet.gltf"; -static const char* GIRL = - "/home/jeanne/Nextcloud/assets/models/girl/girl-with-ground.gltf"; -static const char* BOXES = - "/home/jeanne/Nextcloud/assets/models/boxes/boxes.gltf"; - -#define DEFAULT_SCENE_FILE GIRL - -static const bool RenderBoundingBoxes = false; -static const R DefaultCameraSpeed = (R)6.0; -static const R DefaultMouseSensitivity = (R)(10 * TO_RAD); -static const vec3 DefaultCameraPosition = (vec3){0, 2, 5}; - -typedef struct CameraCommand { - bool CameraMoveLeft : 1; - bool CameraMoveRight : 1; - bool CameraMoveForward : 1; - bool CameraMoveBackward : 1; -} CameraCommand; - -typedef struct CameraController { - R camera_speed; // Camera movement speed. - R mouse_sensitivity; // Controls the degree with which mouse movements - // rotate the camera. - vec2 prev_mouse_position; // Mouse position in the previous frame. - bool rotating; // When true, subsequent mouse movements cause the - // camera to rotate. -} CameraController; - -typedef struct State { - Scene* scene; - Model* model; - SceneCamera* camera; - CameraController camera_controller; -} State; - -/// Load the skyquad texture. -static const Texture* load_environment_map(Gfx* gfx) { - assert(gfx); - return gfx_load_texture( - gfx, &(LoadTextureCmd){ - .origin = AssetFromFile, - .type = LoadCubemap, - .colour_space = sRGB, - .filtering = NearestFiltering, - .mipmaps = false, - .data.cubemap.filepaths = { - mstring_make(skybox[0]), mstring_make(skybox[1]), - mstring_make(skybox[2]), mstring_make(skybox[3]), - mstring_make(skybox[4]), mstring_make(skybox[5])} - }); -} - -/// Load the skyquad and return the environment light node. -static SceneNode* load_skyquad(Gfx* gfx, SceneNode* root) { - assert(gfx); - assert(root); - - GfxCore* gfxcore = gfx_get_core(gfx); - - const Texture* environment_map = load_environment_map(gfx); - if (!environment_map) { - return 0; - } - - return gfx_setup_skyquad(gfxcore, root, environment_map); -} - -/// Load the 3D scene. -/// Return the loaded model. -static Model* load_scene(Game* game, State* state, const char* scene_filepath) { - assert(game); - assert(game->gfx); - assert(state); - assert(state->scene); - - Camera* camera = gfx_get_camera_camera(state->camera); - spatial3_set_position(&camera->spatial, vec3_make(0, 0, 2)); - - SceneNode* root = gfx_get_scene_root(state->scene); - SceneNode* sky_light_node = load_skyquad(game->gfx, root); - if (!sky_light_node) { - return 0; // test - } - - Model* model = gfx_load_model( - game->gfx, &(LoadModelCmd){.origin = AssetFromFile, - .filepath = mstring_make(scene_filepath)}); - if (!model) { - return 0; - } - SceneNode* model_node = gfx_make_model_node(model); - if (!model_node) { - return 0; - } - gfx_set_node_parent(model_node, sky_light_node); - - gfx_log_node_hierarchy(root); - - return model; -} - -bool init(Game* game, State** pp_state) { - assert(game); - - // Usage: - const char* scene_filepath = - game->argc > 1 ? game->argv[1] : DEFAULT_SCENE_FILE; - - State* state = calloc(1, sizeof(State)); - if (!state) { - goto cleanup; - } - - if (!(state->scene = gfx_make_scene())) { - goto cleanup; - } - if (!(state->camera = gfx_make_camera())) { - goto cleanup; - } - - state->model = load_scene(game, state, scene_filepath); - if (!state->model) { - goto cleanup; - } - - Anima* anima = gfx_get_model_anima(state->model); - if (anima) { - gfx_play_animation( - anima, &(AnimationPlaySettings){.name = "Walk", .loop = true}); - // TODO: Interpolate animations. - /*gfx_play_animation( - anima, - &(AnimationPlaySettings){.name = "Jumping-jack-lower", .loop = true}); - gfx_play_animation( - anima, &(AnimationPlaySettings){ - .name = "Jumping-jack-arms-mid", .loop = true});*/ - } - - spatial3_set_position( - &gfx_get_camera_camera(state->camera)->spatial, DefaultCameraPosition); - - state->camera_controller.camera_speed = DefaultCameraSpeed; - state->camera_controller.mouse_sensitivity = DefaultMouseSensitivity; - - *pp_state = state; - return true; - -cleanup: - shutdown(game, state); - if (state) { - free(state); - } - return false; -} - -void shutdown(Game* game, State* state) { - assert(game); - if (state) { - gfx_destroy_camera(&state->camera); - gfx_destroy_scene(&state->scene); - // State freed by plugin engine. - } -} - -static void update_camera( - CameraController* controller, R dt, vec2 mouse_position, - CameraCommand command, Spatial3* camera) { - assert(controller); - assert(camera); - - // Translation. - const R move_x = (R)(command.CameraMoveLeft ? -1 : 0) + - (R)(command.CameraMoveRight ? 1 : 0); - const R move_y = (R)(command.CameraMoveForward ? 1 : 0) + - (R)(command.CameraMoveBackward ? -1 : 0); - const vec2 translation = - vec2_scale(vec2_make(move_x, move_y), controller->camera_speed * dt); - spatial3_move_right(camera, translation.x); - spatial3_move_forwards(camera, translation.y); - - // Rotation. - if (controller->rotating) { - const vec2 mouse_delta = - vec2_sub(mouse_position, controller->prev_mouse_position); - - const vec2 rotation = - vec2_scale(mouse_delta, controller->mouse_sensitivity * dt); - - spatial3_global_yaw(camera, -rotation.x); - spatial3_pitch(camera, -rotation.y); - } - - // Update controller state. - controller->prev_mouse_position = mouse_position; -} - -void update(Game* game, State* state, double t, double dt) { - assert(game); - assert(state); - assert(state->scene); - assert(state->camera); - - double mouse_x, mouse_y; - gfx_app_get_mouse_position(&mouse_x, &mouse_y); - const vec2 mouse_position = {(R)mouse_x, (R)mouse_y}; - - const CameraCommand camera_command = (CameraCommand){ - .CameraMoveLeft = gfx_app_is_key_pressed(KeyA), - .CameraMoveRight = gfx_app_is_key_pressed(KeyD), - .CameraMoveForward = gfx_app_is_key_pressed(KeyW), - .CameraMoveBackward = gfx_app_is_key_pressed(KeyS), - }; - - state->camera_controller.rotating = gfx_app_is_mouse_button_pressed(LMB); - - update_camera( - &state->camera_controller, (R)dt, mouse_position, camera_command, - &gfx_get_camera_camera(state->camera)->spatial); - - // const vec3 orbit_point = vec3_make(0, 2, 0); - // Camera* camera = gfx_get_camera_camera(state->camera); - // spatial3_orbit( - // &camera->spatial, orbit_point, - // /*radius=*/5, - // /*azimuth=*/(R)(t * 0.5), /*zenith=*/0); - // spatial3_lookat(&camera->spatial, orbit_point); - - gfx_update(state->scene, state->camera, (R)t); -} - -/// Render the bounding boxes of all scene objects. -static void render_bounding_boxes_rec( - ImmRenderer* imm, const Anima* anima, const mat4* parent_model_matrix, - const SceneNode* node) { - assert(imm); - assert(node); - - const mat4 model_matrix = - mat4_mul(*parent_model_matrix, gfx_get_node_transform(node)); - - const NodeType node_type = gfx_get_node_type(node); - - if (node_type == ModelNode) { - const Model* model = gfx_get_node_model(node); - const SceneNode* root = gfx_get_model_root(model); - render_bounding_boxes_rec(imm, anima, &model_matrix, root); - } else if (node_type == AnimaNode) { - anima = gfx_get_node_anima(node); - } else if (node_type == ObjectNode) { - gfx_imm_set_model_matrix(imm, &model_matrix); - - const SceneObject* obj = gfx_get_node_object(node); - const Skeleton* skeleton = gfx_get_object_skeleton(obj); - - if (skeleton) { // Animated model. - assert(anima); - const size_t num_joints = gfx_get_skeleton_num_joints(skeleton); - for (size_t i = 0; i < num_joints; ++i) { - if (gfx_joint_has_box(anima, skeleton, i)) { - const Box box = gfx_get_joint_box(anima, skeleton, i); - gfx_imm_draw_box3(imm, box.vertices); - } - } - } else { // Static model. - const aabb3 box = gfx_get_object_aabb(obj); - gfx_imm_draw_aabb3(imm, box); - } - } - - // Render children's boxes. - const SceneNode* child = gfx_get_node_child(node); - while (child) { - render_bounding_boxes_rec(imm, anima, &model_matrix, child); - child = gfx_get_node_sibling(child); - } -} - -/// Render the bounding boxes of all scene objects. -static void render_bounding_boxes(const Game* game, const State* state) { - assert(game); - assert(state); - - GfxCore* gfxcore = gfx_get_core(game->gfx); - ImmRenderer* imm = gfx_get_imm_renderer(game->gfx); - assert(gfxcore); - assert(imm); - - const mat4 id = mat4_id(); - Anima* anima = 0; - - gfx_set_blending(gfxcore, true); - gfx_set_depth_mask(gfxcore, false); - gfx_set_polygon_offset(gfxcore, -1.5f, -1.0f); - - gfx_imm_start(imm); - gfx_imm_set_camera(imm, gfx_get_camera_camera(state->camera)); - gfx_imm_set_colour(imm, vec4_make(0.3, 0.3, 0.9, 0.1)); - render_bounding_boxes_rec(imm, anima, &id, gfx_get_scene_root(state->scene)); - gfx_imm_end(imm); - - gfx_reset_polygon_offset(gfxcore); - gfx_set_depth_mask(gfxcore, true); - gfx_set_blending(gfxcore, false); -} - -void render(const Game* game, const State* state) { - assert(state); - assert(game); - assert(game->gfx); - assert(state->scene); - assert(state->camera); - - Renderer* renderer = gfx_get_renderer(game->gfx); - assert(renderer); - - gfx_render_scene( - renderer, &(RenderSceneParams){.mode = RenderDefault, - .scene = state->scene, - .camera = state->camera}); - - if (RenderBoundingBoxes) { - render_bounding_boxes(game, state); - } -} - -void resize(Game* game, State* state, int width, int height) { - assert(game); - assert(state); - - const R fovy = 60 * TO_RAD; - const R aspect = (R)width / (R)height; - const R near = 0.1; - const R far = 1000; - const mat4 projection = mat4_perspective(fovy, aspect, near, far); - - Camera* camera = gfx_get_camera_camera(state->camera); - camera->projection = projection; -} -- cgit v1.2.3