#include "game.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // Paths to various scene files. static const char* BOX = "/assets/models/box.gltf"; static const char* SUZANNE = "/assets/models/suzanne.gltf"; static const char* SPONZA = "/assets/glTF-Sample-Models/2.0/Sponza/glTF/Sponza.gltf"; static const char* FLIGHT_HELMET = "/assets/glTF-Sample-Models/2.0/FlightHelmet/glTF/FlightHelmet.gltf"; static const char* DAMAGED_HELMET = "/assets/glTF-Sample-Models/2.0/DamagedHelmet/glTF/DamagedHelmet.gltf"; static const char* GIRL = "/home/jeanne/Nextcloud/assets/models/girl/girl-with-ground.gltf"; #define DEFAULT_SCENE_FILE GIRL static const char* CLOUDS1_TEXTURE = "/assets/skybox/clouds1/clouds1_west.bmp"; /// Load the skyquad texture. static Texture* load_environment_map(RenderBackend* render_backend) { return gfx_load_texture( render_backend, &(LoadTextureCmd){ .origin = TextureFromFile, .type = LoadCubemap, .colour_space = sRGB, .filtering = NearestFiltering, .mipmaps = false, .data.cubemap.filepaths = { mstring_make("/assets/skybox/clouds1/clouds1_east.bmp"), mstring_make("/assets/skybox/clouds1/clouds1_west.bmp"), mstring_make("/assets/skybox/clouds1/clouds1_up.bmp"), mstring_make("/assets/skybox/clouds1/clouds1_down.bmp"), mstring_make("/assets/skybox/clouds1/clouds1_south.bmp"), mstring_make("/assets/skybox/clouds1/clouds1_north.bmp")} }); } /// Load the skyquad and return the environment light node. static SceneNode* load_skyquad(Game* game) { assert(game); Texture* environment_map = load_environment_map(game->render_backend); if (!environment_map) { return 0; } return gfx_setup_skyquad( game->gfx, gfx_get_scene_root(game->scene), environment_map); } /// Load the 3D scene. static SceneNode* load_scene( Game* game, const char* scene_filepath, const char* view_mode) { assert(game); game->camera = gfx_make_camera(); if (!game->camera) { return 0; } Camera* camera = gfx_get_camera_camera(game->camera); spatial3_set_position(&camera->spatial, vec3_make(0, 0, 2)); SceneNode* sky_light_node = load_skyquad(game); if (!sky_light_node) { return 0; } SceneNode* scene_node = gfx_load_scene( game->gfx, sky_light_node, &(LoadSceneCmd){.origin = SceneFromFile, .filepath = scene_filepath}); if (!scene_node) { return 0; } gfx_log_node_hierarchy(gfx_get_scene_root(game->scene)); return scene_node; } /// Load a scene for debugging textures. static bool load_texture_debugger_scene(Game* game) { assert(game); Texture* texture = gfx_load_texture( game->render_backend, &(LoadTextureCmd){ .origin = TextureFromFile, .type = LoadTexture, .filtering = LinearFiltering, .mipmaps = false, .data.texture.filepath = mstring_make(CLOUDS1_TEXTURE)}); game->camera = gfx_make_camera(); if (!game->camera) { return false; } Camera* camera = gfx_get_camera_camera(game->camera); spatial3_set_position(&camera->spatial, vec3_make(0, 0, 1)); ShaderProgram* shader = gfx_make_view_texture_shader(game->render_backend); if (!shader) { return false; } Geometry* geometry = gfx_make_quad_11(game->render_backend); if (!geometry) { return false; } MaterialDesc material_desc = (MaterialDesc){0}; material_desc.uniforms[0] = (ShaderUniform){ .type = UniformTexture, .value.texture = texture, .name = sstring_make("Texture")}; material_desc.num_uniforms = 1; Material* material = gfx_make_material(&material_desc); if (!material) { return false; } MeshDesc mesh_desc = (MeshDesc){0}; mesh_desc.geometry = geometry; mesh_desc.material = material; mesh_desc.shader = shader; Mesh* mesh = gfx_make_mesh(&mesh_desc); if (!mesh) { return false; } SceneObject* object = gfx_make_object(); if (!object) { return false; } gfx_add_object_mesh(object, mesh); SceneNode* node = gfx_make_object_node(object); SceneNode* root = gfx_get_scene_root(game->scene); gfx_set_node_parent(node, root); return true; } /// Render the bounding boxes of all scene objects. static void render_bounding_boxes(ImmRenderer* imm, const SceneNode* node) { if (gfx_get_node_type(node) == ObjectNode) { // TODO: Look at the scene log. The JointNodes are detached from the // ObjectNodes. This is why the boxes are not being transformed as expected // here. Anima needs to animate boxes? Use OOBB in addition to AABB? const mat4 model = gfx_get_node_global_transform(node); const SceneObject* obj = gfx_get_node_object(node); const aabb3 box = gfx_calc_object_aabb(obj); gfx_imm_set_model_matrix(imm, &model); gfx_imm_draw_aabb(imm, box); } // Render children's boxes. for (NodeIter it = gfx_get_node_child(node); it; it = gfx_get_next_child(it)) { render_bounding_boxes(imm, gfx_get_iter_node(it)); } } bool game_new(Game* game, int argc, const char** argv) { // TODO: getopt() to implement proper argument parsing. const char* scene_filepath = argc > 1 ? argv[1] : DEFAULT_SCENE_FILE; const char* view_mode = argc > 2 ? argv[2] : ""; game->gfx = gfx_init(); if (!game->gfx) { goto cleanup; } game->render_backend = gfx_get_render_backend(game->gfx); game->renderer = gfx_get_renderer(game->gfx); game->scene = gfx_make_scene(); if (!game->scene) { goto cleanup; } game->root_node = load_scene(game, scene_filepath, view_mode); if (!game->root_node) { goto cleanup; } /*if (!load_texture_debugger_scene(game)) { goto cleanup; }*/ Anima* anima = gfx_get_node_anima(game->root_node); // const bool play_result = gfx_play_animation( // anima, &(AnimationPlaySettings){.name = "Defiant stance", .loop = // false}); const bool play_result = gfx_play_animation( anima, &(AnimationPlaySettings){.name = "Walk", .loop = true}); // assert(play_result); return true; cleanup: LOGE("Gfx error: %s", gfx_get_error()); if (game->gfx) { gfx_destroy(&game->gfx); } return false; } void game_end(Game* game) { gfx_destroy(&game->gfx); } void game_update(Game* game, double t, double dt) { // LOGD("Tick"); gfx_animate_scene(game->scene, (R)t); // TODO: Compute bounding boxes to then find a good orbit point and radius // for each scene. const vec3 orbit_point = vec3_make(0, 2, 0); Camera* camera = gfx_get_camera_camera(game->camera); spatial3_orbit( &camera->spatial, orbit_point, /*radius=*/2.5, /*azimuth=*/t * 0.5, /*zenith=*/0); spatial3_lookat(&camera->spatial, orbit_point); // spatial3_set_position(&camera->spatial, vec3_make(0, 0, 2)); // spatial3_lookat(&camera->spatial, vec3_make(0, 0, -1)); // spatial3_orbit( // &camera->spatial, vec3_make(0, 0, 0), // /*radius=*/2.5, // /*azimuth=*/t * 0.2, /*zenith=*/0); // spatial3_lookat(&camera->spatial, vec3_make(0, 0, 0)); } void game_render(const Game* game) { gfx_render_scene( game->renderer, &(RenderSceneParams){ .mode = RenderDefault, .scene = game->scene, .camera = game->camera}); ImmRenderer* imm = gfx_get_imm_renderer(game->gfx); assert(imm); gfx_imm_start(imm); gfx_imm_set_camera(imm, gfx_get_camera_camera(game->camera)); gfx_imm_set_colour(imm, vec4_make(0.2, 0.2, 1.0, 0.3)); render_bounding_boxes(imm, gfx_get_scene_root(game->scene)); gfx_imm_end(imm); } void game_set_viewport(Game* game, int width, int height) { gfx_set_viewport(game->render_backend, width, height); 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(game->camera); camera->projection = projection; }