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)
This commit is contained in:
2026-10-28 22:09:47 +01:00
parent a36ed3a902
commit f416cd38ef
2 changed files with 103 additions and 9 deletions

View File

@@ -105,7 +105,10 @@ Rc<Scene> create_sponza_scene(const Rc<Shader>& shader);
Rc<Scene> create_car_scene(const Rc<Shader>& shader); Rc<Scene> create_car_scene(const Rc<Shader>& shader);
Rc<Scene> create_backpack_scene(const Rc<Shader>& shader, const Rc<Mesh>& cube_mesh); Rc<Scene> create_backpack_scene(const Rc<Shader>& shader, const Rc<Mesh>& cube_mesh);
Rc<Scene> create_cubes_scene(const Rc<Shader>& shader, const Rc<Mesh>& cube_mesh); Rc<Scene> create_cubes_scene(const Rc<Shader>& shader, const Rc<Mesh>& cube_mesh);
Rc<Material> create_textured_material(const Rc<Shader>& shader, const std::string& texture_path); Rc<Scene> create_containers_scene(const Rc<Shader>& shader, const Rc<Mesh>& cube_mesh);
Entity create_floor(const Rc<Shader>& shader, const Rc<Mesh>& cube_mesh, float top_y);
Rc<Material> create_textured_material(const Rc<Shader>& 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); void add_random_lights(Scene& scene, int point_light_count, int spot_light_count, unsigned int seed);
// Frame // Frame
@@ -210,12 +213,14 @@ std::vector<Rc<Scene>> create_scenes(const Rc<Shader>& shader, const Rc<Mesh>& c
create_car_scene(shader), create_car_scene(shader),
create_backpack_scene(shader, cube_mesh), create_backpack_scene(shader, cube_mesh),
create_cubes_scene(shader, cube_mesh), create_cubes_scene(shader, cube_mesh),
create_containers_scene(shader, cube_mesh),
}; };
} }
Rc<Scene> create_sponza_scene(const Rc<Shader>& shader) Rc<Scene> create_sponza_scene(const Rc<Shader>& shader)
{ {
auto scene = make_rc<Scene>(); auto scene = make_rc<Scene>();
scene->name = "Sponza";
Entity sponza = load_obj("./assets/sponza/sponza.obj", shader); Entity sponza = load_obj("./assets/sponza/sponza.obj", shader);
sponza.transform.position = {0.0f, -0.5f, 0.0f}; sponza.transform.position = {0.0f, -0.5f, 0.0f};
@@ -229,6 +234,7 @@ Rc<Scene> create_sponza_scene(const Rc<Shader>& shader)
Rc<Scene> create_car_scene(const Rc<Shader>& shader) Rc<Scene> create_car_scene(const Rc<Shader>& shader)
{ {
auto scene = make_rc<Scene>(); auto scene = make_rc<Scene>();
scene->name = "Audi R8";
scene->add_entity(load_obj("./assets/audi_r8/Audi_R8_2017.obj", shader)); scene->add_entity(load_obj("./assets/audi_r8/Audi_R8_2017.obj", shader));
@@ -239,13 +245,10 @@ Rc<Scene> create_car_scene(const Rc<Shader>& shader)
Rc<Scene> create_backpack_scene(const Rc<Shader>& shader, const Rc<Mesh>& cube_mesh) Rc<Scene> create_backpack_scene(const Rc<Shader>& shader, const Rc<Mesh>& cube_mesh)
{ {
auto scene = make_rc<Scene>(); auto scene = make_rc<Scene>();
scene->name = "Backpack";
scene->add_entity(load_obj("./assets/backpack/backpack.obj", shader, true)); scene->add_entity(load_obj("./assets/backpack/backpack.obj", shader, true));
scene->add_entity(create_floor(shader, cube_mesh, -2.5f));
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);
add_random_lights(*scene, 4, 4, 37); add_random_lights(*scene, 4, 4, 37);
return scene; return scene;
@@ -254,6 +257,8 @@ Rc<Scene> create_backpack_scene(const Rc<Shader>& shader, const Rc<Mesh>& cube_m
Rc<Scene> create_cubes_scene(const Rc<Shader>& shader, const Rc<Mesh>& cube_mesh) Rc<Scene> create_cubes_scene(const Rc<Shader>& shader, const Rc<Mesh>& cube_mesh)
{ {
auto scene = make_rc<Scene>(); auto scene = make_rc<Scene>();
scene->name = "Cubes";
auto material = create_textured_material(shader, "./assets/textures/container.jpg"); auto material = create_textured_material(shader, "./assets/textures/container.jpg");
const std::vector<glm::vec3> positions{ const std::vector<glm::vec3> positions{
@@ -274,11 +279,88 @@ Rc<Scene> create_cubes_scene(const Rc<Shader>& shader, const Rc<Mesh>& cube_mesh
return scene; return scene;
} }
Rc<Material> create_textured_material(const Rc<Shader>& shader, const std::string& texture_path) // Containers resting on a floor, for testing specular maps: the steel frame
// shines, the wood doesn't
Rc<Scene> create_containers_scene(const Rc<Shader>& shader, const Rc<Mesh>& cube_mesh)
{
auto scene = make_rc<Scene>();
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<Placement> 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>& shader, const Rc<Mesh>& 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<Material> create_textured_material(const Rc<Shader>& shader, const std::string& diffuse_path,
const std::string& specular_path/* = ""*/)
{ {
auto material = make_rc<Material>(); auto material = make_rc<Material>();
material->shader = shader; 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; return material;
} }
@@ -468,11 +550,21 @@ void draw_panel_window(AppState& app)
ImGui::SeparatorText("Scene Selector"); ImGui::SeparatorText("Scene Selector");
for (size_t i = 0; i < app.scenes.size(); ++i) { for (size_t i = 0; i < app.scenes.size(); ++i) {
const std::string& name = app.scenes[i]->name;
char label[64]; char label[64];
if (name.empty()) {
ImFormatString(label, IM_ARRAYSIZE(label), "Scene %zu", i); 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<int>(i));
if (ImGui::RadioButton(label, app.selected_scene_index == i)) { if (ImGui::RadioButton(label, app.selected_scene_index == i)) {
app.selected_scene_index = i; app.selected_scene_index = i;
} }
ImGui::PopID();
} }
ImGui::SeparatorText("Camera Settings"); ImGui::SeparatorText("Camera Settings");

View File

@@ -1,6 +1,7 @@
#ifndef FAN_SCENE_HPP_ #ifndef FAN_SCENE_HPP_
#define FAN_SCENE_HPP_ #define FAN_SCENE_HPP_
#include <string>
#include <vector> #include <vector>
#include "entity.hpp" #include "entity.hpp"
@@ -12,6 +13,7 @@ namespace Fan
class Scene class Scene
{ {
public: public:
std::string name{};
LightEnvironment light_environment{}; LightEnvironment light_environment{};
// Stores a copy: later changes to the passed Entity don't affect the scene. // Stores a copy: later changes to the passed Entity don't affect the scene.