From f416cd38ef00569c3c824debcc26ee607b0af8ee Mon Sep 17 00:00:00 2001 From: Cristian Gora Date: Wed, 28 Oct 2026 22:09:47 +0100 Subject: [PATCH] Add Containers scene for testing specular maps; name scenes - Add Scene::name and show it in the scene selector (falls back to "Scene N" when empty; ImGui IDs keep equal names apart) - Add a Containers scene: container2 with its specular map, arranged on a floor as a pyramid, a stack, loose crates and one large crate. Lit by 8 point and 4 spot lights, with the directional light's diffuse and specular turned off - create_textured_material() takes an optional specular map, loaded without the checkerboard fallback like in the OBJ loader - Add create_floor() and use it for the Backpack scene too (same position as before) --- src/main.cpp | 110 +++++++++++++++++++++++++++++++++++++++++++++----- src/scene.hpp | 2 + 2 files changed, 103 insertions(+), 9 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index e8ebd7e..b454af6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -105,7 +105,10 @@ Rc create_sponza_scene(const Rc& shader); Rc create_car_scene(const Rc& shader); Rc create_backpack_scene(const Rc& shader, const Rc& cube_mesh); Rc create_cubes_scene(const Rc& shader, const Rc& cube_mesh); -Rc create_textured_material(const Rc& shader, const std::string& texture_path); +Rc create_containers_scene(const Rc& shader, const Rc& cube_mesh); +Entity create_floor(const Rc& shader, const Rc& cube_mesh, float top_y); +Rc create_textured_material(const Rc& shader, const std::string& diffuse_path, + const std::string& specular_path = ""); void add_random_lights(Scene& scene, int point_light_count, int spot_light_count, unsigned int seed); // Frame @@ -210,12 +213,14 @@ std::vector> create_scenes(const Rc& shader, const Rc& c create_car_scene(shader), create_backpack_scene(shader, cube_mesh), create_cubes_scene(shader, cube_mesh), + create_containers_scene(shader, cube_mesh), }; } Rc create_sponza_scene(const Rc& shader) { auto scene = make_rc(); + scene->name = "Sponza"; Entity sponza = load_obj("./assets/sponza/sponza.obj", shader); sponza.transform.position = {0.0f, -0.5f, 0.0f}; @@ -229,6 +234,7 @@ Rc create_sponza_scene(const Rc& shader) Rc create_car_scene(const Rc& shader) { auto scene = make_rc(); + scene->name = "Audi R8"; scene->add_entity(load_obj("./assets/audi_r8/Audi_R8_2017.obj", shader)); @@ -239,13 +245,10 @@ Rc create_car_scene(const Rc& shader) Rc create_backpack_scene(const Rc& shader, const Rc& cube_mesh) { auto scene = make_rc(); + scene->name = "Backpack"; scene->add_entity(load_obj("./assets/backpack/backpack.obj", shader, true)); - - Entity floor{cube_mesh, create_textured_material(shader, "./assets/textures/wall.jpg")}; - floor.transform.position = {0.0f, -3.0f, 0.0f}; - floor.transform.scale = {20.0f, 1.0f, 20.0f}; - scene->add_entity(floor); + scene->add_entity(create_floor(shader, cube_mesh, -2.5f)); add_random_lights(*scene, 4, 4, 37); return scene; @@ -254,6 +257,8 @@ Rc create_backpack_scene(const Rc& shader, const Rc& cube_m Rc create_cubes_scene(const Rc& shader, const Rc& cube_mesh) { auto scene = make_rc(); + scene->name = "Cubes"; + auto material = create_textured_material(shader, "./assets/textures/container.jpg"); const std::vector positions{ @@ -274,11 +279,88 @@ Rc create_cubes_scene(const Rc& shader, const Rc& cube_mesh return scene; } -Rc create_textured_material(const Rc& shader, const std::string& texture_path) +// Containers resting on a floor, for testing specular maps: the steel frame +// shines, the wood doesn't +Rc create_containers_scene(const Rc& shader, const Rc& cube_mesh) +{ + auto scene = make_rc(); + scene->name = "Containers"; + + scene->add_entity(create_floor(shader, cube_mesh, -0.5f)); + + auto material = create_textured_material(shader, "./assets/textures/container2.png", + "./assets/textures/container2_specular.png"); + + struct Placement + { + glm::vec3 position; // Center of the container + float yaw_degrees; + float size; + }; + + // Unit containers on the floor have their center at y = 0 + const std::vector placements{ + // Pyramid in the middle + {{-1.05f, 0.0f, 0.0f}, 0.0f, 1.0f}, + {{ 0.0f, 0.0f, 0.0f}, 0.0f, 1.0f}, + {{ 1.05f, 0.0f, 0.0f}, 0.0f, 1.0f}, + {{-0.56f, 1.0f, 0.0f}, 4.0f, 1.0f}, + {{ 0.56f, 1.0f, 0.0f}, -3.0f, 1.0f}, + {{ 0.0f, 2.0f, 0.0f}, 12.0f, 1.0f}, + + // Stack of two on the left + {{-3.2f, 0.0f, -0.5f}, 15.0f, 1.0f}, + {{-3.2f, 1.0f, -0.5f}, 35.0f, 1.0f}, + + // Loose containers around + {{-3.0f, 0.0f, 2.0f}, -20.0f, 1.0f}, + {{ 2.0f, 0.0f, -2.5f}, 45.0f, 1.0f}, + {{ 1.5f, 0.0f, 3.0f}, 70.0f, 1.0f}, + + // A big one in the back + {{-3.5f, 0.5f, -3.5f}, 25.0f, 2.0f}, + }; + + for (const Placement& placement : placements) { + Entity container{cube_mesh, material}; + container.transform.position = placement.position; + container.transform.set_rotation(glm::radians(placement.yaw_degrees), {0.0f, 1.0f, 0.0f}); + container.transform.scale = glm::vec3(placement.size); + scene->add_entity(container); + } + + // Only the local lights shine here, so their highlights are easy to see. + // The directional light's ambient stays to keep unlit sides from going black + DirectionalLight& sun = scene->light_environment.directional_light; + sun.diffuse = glm::vec3(0.0f); + sun.specular = glm::vec3(0.0f); + + // More point lights than the other scenes, for more highlights to compare + add_random_lights(*scene, 8, 4, 2718); + return scene; +} + +// 20x20 slab with the wall texture, its top surface at height top_y +Entity create_floor(const Rc& shader, const Rc& cube_mesh, float top_y) +{ + Entity floor{cube_mesh, create_textured_material(shader, "./assets/textures/wall.jpg")}; + floor.transform.position = {0.0f, top_y - 0.5f, 0.0f}; + floor.transform.scale = {20.0f, 1.0f, 20.0f}; + return floor; +} + +Rc create_textured_material(const Rc& shader, const std::string& diffuse_path, + const std::string& specular_path/* = ""*/) { auto material = make_rc(); material->shader = shader; - material->diffuse_map = ResourceCache::load_texture(texture_path); + material->diffuse_map = ResourceCache::load_texture(diffuse_path); + + // Optional, like in the OBJ loader: without it specular_color is used alone + if (!specular_path.empty()) { + material->specular_map = ResourceCache::load_texture(specular_path, false); + } + return material; } @@ -468,11 +550,21 @@ void draw_panel_window(AppState& app) ImGui::SeparatorText("Scene Selector"); for (size_t i = 0; i < app.scenes.size(); ++i) { + const std::string& name = app.scenes[i]->name; + char label[64]; - ImFormatString(label, IM_ARRAYSIZE(label), "Scene %zu", i); + if (name.empty()) { + ImFormatString(label, IM_ARRAYSIZE(label), "Scene %zu", i); + } else { + ImFormatString(label, IM_ARRAYSIZE(label), "%s", name.c_str()); + } + + // ImGui identifies widgets by label, the index keeps equal names apart + ImGui::PushID(static_cast(i)); if (ImGui::RadioButton(label, app.selected_scene_index == i)) { app.selected_scene_index = i; } + ImGui::PopID(); } ImGui::SeparatorText("Camera Settings"); diff --git a/src/scene.hpp b/src/scene.hpp index 7315993..c3f87ca 100644 --- a/src/scene.hpp +++ b/src/scene.hpp @@ -1,6 +1,7 @@ #ifndef FAN_SCENE_HPP_ #define FAN_SCENE_HPP_ +#include #include #include "entity.hpp" @@ -12,6 +13,7 @@ namespace Fan class Scene { public: + std::string name{}; LightEnvironment light_environment{}; // Stores a copy: later changes to the passed Entity don't affect the scene.