From 1e3fcf5b38d67fb54102786be74af42be5c6792f Mon Sep 17 00:00:00 2001 From: 3gg <3gg@shellblade.net> Date: Tue, 3 Jan 2023 08:49:54 -0800 Subject: Initial commit. --- gltfview/src/game.c | 295 ++++++++++++++++++++++++++++++++++++++++++++++++++++ gltfview/src/game.h | 32 ++++++ gltfview/src/main.c | 65 ++++++++++++ 3 files changed, 392 insertions(+) create mode 100644 gltfview/src/game.c create mode 100644 gltfview/src/game.h create mode 100644 gltfview/src/main.c (limited to 'gltfview/src') diff --git a/gltfview/src/game.c b/gltfview/src/game.c new file mode 100644 index 0000000..d7352d4 --- /dev/null +++ b/gltfview/src/game.c @@ -0,0 +1,295 @@ +#include "game.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include // usleep; TODO Remove. + +// 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"; + +#define DEFAULT_SCENE_FILE DAMAGED_HELMET + +static const char* CLOUDS1_TEXTURE = "/assets/skybox/clouds1/clouds1_west.bmp"; + +static ShaderProgram* load_shader(RenderBackend* render_backend, + const char* view_mode) { + ShaderProgram* shader = 0; + if (strcmp(view_mode, "debug") == 0) { + shader = gfx_make_debug3d_shader(render_backend); + } else if (strcmp(view_mode, "normals") == 0) { + shader = gfx_make_view_normals_shader(render_backend); + } else if (strcmp(view_mode, "normal_mapped_normals") == 0) { + shader = gfx_make_view_normal_mapped_normals_shader(render_backend); + } else if (strcmp(view_mode, "tangents") == 0) { + shader = gfx_make_view_tangents_shader(render_backend); + } else { + shader = gfx_make_cook_torrance_shader(render_backend); + } + return shader; +} + +/// Loads 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_north.bmp"), + mstring_make("/assets/skybox/clouds1/clouds1_south.bmp")}}); +} + +/// Creates an object to render the skyquad in the background. +static SceneNode* make_skyquad_object_node(Game* game, + const Texture* environment_map) { + assert(game); + + SceneObject* skyquad_object = + gfx_make_skyquad(game->gfx, game->scene, environment_map); + if (!skyquad_object) { + return 0; + } + SceneNode* skyquad_node = gfx_make_object_node(skyquad_object); + if (!skyquad_node) { + return 0; + } + gfx_set_node_parent(skyquad_node, gfx_get_scene_root(game->scene)); + return skyquad_node; +} + +/// Creates an environment light. +static SceneNode* make_environment_light(Game* game, + const Texture* environment_light) { + assert(game); + + Light* light = gfx_make_light(&(LightDesc){ + .type = EnvironmentLightType, + .light = (EnvironmentLightDesc){.environment_map = environment_light}}); + if (!light) { + return 0; + } + SceneNode* light_node = gfx_make_light_node(light); + if (!light_node) { + return 0; + } + gfx_set_node_parent(light_node, gfx_get_scene_root(game->scene)); + return light_node; +} + +/// Loads the skyquad and returns the SceneNode with the environment light. +static bool load_skyquad(Game* game, SceneNode** node) { + assert(game); + assert(node); + + Texture* environment_map = load_environment_map(game->render_backend); + if (!environment_map) { + return false; + } + + make_skyquad_object_node(game, environment_map); + *node = make_environment_light(game, environment_map); + + return true; +} + +/// Loads the 3D scene. +static bool load_scene(Game* game, const char* scene_filepath, + const char* view_mode) { + assert(game); + + game->camera = gfx_make_camera(); + if (!game->camera) { + return false; + } + Camera* camera = gfx_get_camera_camera(game->camera); + // Sponza. + // spatial3_set_position(&camera->spatial, vec3_make(0, 100, 50)); + // Damaged helmet. + spatial3_set_position(&camera->spatial, vec3_make(0, 0, 2)); + + SceneNode* sky_node = 0; + if (!load_skyquad(game, &sky_node) || !sky_node) { + return false; + } + + ShaderProgram* shader = load_shader(game->render_backend, view_mode); + if (!shader) { + return false; + } + + if (!gfx_load_scene(game->gfx, sky_node, shader, + &(LoadSceneCmd){.origin = SceneFromFile, + .filepath = scene_filepath})) { + return false; + } + + return true; +} + +/// Loads 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.shader = shader; + 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* 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; +} + +bool game_new(Game* game, int argc, const char** argv) { + // TODO: getopt() to implement proper argument parsing. + const char* view_mode = argc > 1 ? argv[1] : ""; + const char* scene_filepath = argc > 2 ? argv[2] : DEFAULT_SCENE_FILE; + + 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(game->gfx); + if (!game->scene) { + goto cleanup; + } + + if (!load_scene(game, scene_filepath, view_mode)) { + goto cleanup; + } + /*if (!load_texture_debugger_scene(game)) { + goto cleanup; + }*/ + + return true; + +cleanup: + LOGE("Gfx error: %s", gfx_get_error()); + if (game->scene) { + gfx_destroy_scene(game->gfx, &game->scene); + } + 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) { + game->elapsed += dt; + while (game->elapsed >= 1.0) { + // LOGD("Tick"); + usleep(1000); + game->elapsed -= 1.0; + } + Camera* camera = gfx_get_camera_camera(game->camera); + spatial3_orbit(&camera->spatial, vec3_make(0, 0, 0), + /*radius=*/2, + /*azimuth=*/t * 0.5, /*zenith=*/0); + spatial3_lookat(&camera->spatial, vec3_make(0, 0, 0)); +} + +void game_render(const Game* game) { + gfx_render_scene(game->renderer, game->render_backend, game->scene, + game->camera); +} + +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; +} diff --git a/gltfview/src/game.h b/gltfview/src/game.h new file mode 100644 index 0000000..92c0885 --- /dev/null +++ b/gltfview/src/game.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include + +/// The delta time the game should be updated with. +static const double game_dt = 1.0 / 60.0; + +/// Game state. +typedef struct { + Gfx* gfx; + RenderBackend* render_backend; + Renderer* renderer; + Scene* scene; + SceneCamera* camera; + double elapsed; +} Game; + +bool game_new(Game*, int argc, const char** argv); + +void game_end(Game*); + +void game_update(Game*, double t, double dt); + +void game_render(const Game*); + +void game_set_viewport(Game*, int width, int height); diff --git a/gltfview/src/main.c b/gltfview/src/main.c new file mode 100644 index 0000000..f7c372c --- /dev/null +++ b/gltfview/src/main.c @@ -0,0 +1,65 @@ +#include "game.h" + +#include +#include + +#include + +static bool init(const GfxAppDesc* desc, void** app_state) { + Game* game = calloc(1, sizeof(Game)); + if (!game) { + LOGE("Failed to allocate game state"); + return false; + } + if (!game_new(game, desc->argc, desc->argv)) { + LOGE("Failed to initialize game\n"); + return false; + } + *app_state = game; + return true; +} + +static void shutdown(void* app_state) { + assert(app_state); + Game* game = (Game*)(app_state); + game_end(game); +} + +static void update(void* app_state, double t, double dt) { + assert(app_state); + Game* game = (Game*)(app_state); + game_update(game, t, dt); +} + +static void render(void* app_state) { + assert(app_state); + Game* game = (Game*)(app_state); + game_render(game); +} + +static void resize(void* app_state, int width, int height) { + assert(app_state); + Game* game = (Game*)(app_state); + game_set_viewport(game, width, height); +} + +int main(int argc, const char** argv) { + const int initial_width = 1350; + const int initial_height = 900; + const int max_fps = 60; + + gfx_app_run(&(GfxAppDesc){.argc = argc, + .argv = argv, + .width = initial_width, + .height = initial_height, + .max_fps = max_fps, + .update_delta_time = + max_fps > 0 ? 1.0 / (double)max_fps : 0.0}, + &(GfxAppCallbacks){.init = init, + .update = update, + .render = render, + .resize = resize, + .shutdown = shutdown}); + + return 0; +} -- cgit v1.2.3