Now all configurable with json.

This commit is contained in:
Zed A. Shaw 2026-08-26 19:56:01 -04:00
parent 3041ae0172
commit d3169d3eca
25 changed files with 720 additions and 244 deletions

View file

@ -3,27 +3,27 @@
#include <glm/gtc/type_ptr.hpp>
glm::mat4 Camera::look_at() {
return glm::lookAt(pos, pos + front, up);
return glm::lookAt(position, position + front, up);
}
void Camera::forward() {
pos += speed * front;
position += speed * front;
}
void Camera::back() {
pos -= speed * front;
position -= speed * front;
}
void Camera::left() {
pos -= glm::normalize(glm::cross(front, up)) * speed;
position -= glm::normalize(glm::cross(front, up)) * speed;
}
void Camera::right() {
pos += glm::normalize(glm::cross(front, up)) * speed;
position += glm::normalize(glm::cross(front, up)) * speed;
}
void Camera::update(float deltaTime) {
speed = movementSpeed * deltaTime;
speed = movement_speed * deltaTime;
}
void Camera::mouse_move(double xpos, double ypos) {

View file

@ -2,11 +2,11 @@
#include <glm/glm.hpp>
struct Camera {
glm::vec3 pos = glm::vec3(0.0f, 0.0f, 3.0f);
glm::vec3 front = glm::vec3(0.0f, 0.0f, -1.0f);
glm::vec3 up = glm::vec3(0.0f, 1.0f, 0.0f);
glm::vec3 direction = glm::vec3(0.0f, 0.0f, 0.0f);
float movementSpeed = 20.0f;
glm::vec3 position{0.0f, 0.0f, 3.0f};
glm::vec3 front{0.0f, 0.0f, -1.0f};
glm::vec3 up{0.0f, 1.0f, 0.0f};
glm::vec3 direction{0.0f, 0.0f, 0.0f};
float movement_speed = 20.0f;
float pitch = 0.0f;
float yaw = -90.0f;

View file

@ -0,0 +1,105 @@
#pragma once
#include <glm/glm.hpp>
#include <vector>
#include <map>
#include "json.hpp"
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
#define MAX_LIGHTS 4
namespace components {
template <typename T> struct NameOf;
struct Material {
glm::vec3 ambient{0.1f,0.1f,0.1f};
float shininess{32.0f};
unsigned int diffuseMap = 0;
unsigned int specularMap = 0;
};
ENROLL_COMPONENT(Material, ambient, shininess, diffuseMap, specularMap);
struct Light {
glm::vec3 position{0.0f, 0.0f, 0.0f};
glm::vec3 direction{0.0f, 0.0f, 0.0f};
glm::vec3 ambient{0.0f, 0.0f, 0.0f};
glm::vec3 diffuse{0.0f, 0.0f, 0.0f};
glm::vec3 specular{0.0f, 0.0f, 0.0f};
float constant=0.0f;
float linear=0.0f;
float quadratic=0.0f;
float cut_off=0.0f;
float outer_cut_off=0.0f;
void adjust(float amount) {
ambient += amount;
diffuse += amount;
specular += amount;
}
};
ENROLL_COMPONENT(Light,
position, direction,
ambient, diffuse, specular,
constant, linear, quadratic,
cut_off, outer_cut_off);
struct Lighting {
std::vector<Light> directional;
std::vector<Light> positioned;
std::vector<Light> spot;
Light camera;
};
ENROLL_COMPONENT(Lighting, directional, positioned, spot, camera);
struct Camera {
glm::vec3 position{0.0f, 0.0f, 3.0f};
glm::vec3 front{0.0f, 0.0f, -1.0f};
glm::vec3 up{0.0f, 1.0f, 0.0f};
glm::vec3 direction{0.0f, 0.0f, 0.0f};
float movement_speed = 20.0f;
};
ENROLL_COMPONENT(Camera, position, front, up, direction, movement_speed);
struct Model {
std::string directory;
std::string model_path;
};
ENROLL_COMPONENT(Model, directory, model_path);
using Position = glm::vec3;
struct Thing {
std::string model;
Position position;
std::string material;
};
ENROLL_COMPONENT(Thing, model, position, material);
struct Shader {
std::string vertex_path;
std::string frag_path;
};
ENROLL_COMPONENT(Shader, vertex_path, frag_path);
struct Scene {
components::Shader shader;
components::Shader light_shader;
std::map<std::string, Material> materials;
std::map<std::string, Model> models;
std::vector<Thing> things;
Camera camera;
Lighting light;
};
ENROLL_COMPONENT(Scene, shader, light_shader, materials, models, things, camera, light);
}

View file

@ -1,4 +0,0 @@
#pragma once
#include <glm/glm.hpp>
#define CUBE_COUNT 9

View file

@ -0,0 +1,59 @@
#pragma once
#include <nlohmann/json.hpp>
#include <nlohmann/json_fwd.hpp>
#include <optional>
#include <glm/glm.hpp>
#include <vector>
#define ENROLL_COMPONENT(COMPONENT, ...) \
NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(COMPONENT, __VA_ARGS__); \
template <> struct NameOf<COMPONENT> { \
static constexpr const char *name = #COMPONENT; \
};
// partial specialization (full specialization works too)
namespace nlohmann {
template <>
struct adl_serializer<glm::vec3> {
static void to_json(json& j, const glm::vec3& opt) {
}
static void from_json(const json& j, glm::vec3& opt) {
opt = glm::vec3(j[0], j[1], j[2]);
}
};
template <typename T>
struct adl_serializer<std::optional<T>> {
static void to_json(json& j, const std::optional<T>& opt) {
if (opt == std::nullopt) {
j = nullptr;
} else {
j = *opt; // this will call adl_serializer<T>::to_json which will
// find the free function to_json in T's namespace!
}
}
static void from_json(const json& j, std::optional<T>& opt) {
if (j.is_null() || j == false) {
opt = std::nullopt;
} else {
opt = std::make_optional<T>(j.template get<T>());
// same as above, but with adl_serializer<T>::from_json
}
}
};
template<>
struct adl_serializer<std::chrono::milliseconds> {
static void to_json(json& j, const std::chrono::milliseconds& opt) {
j = opt.count();
}
static void from_json(const json& j, std::chrono::milliseconds& opt) {
opt = std::chrono::milliseconds{int(j)};
}
};
}

View file

@ -1,10 +1,9 @@
#define _USE_MATH_DEFINES
#include <stb_image.h>
#include "scene.hpp"
#include "utils.hpp"
void framebuffer_size_callback(GLFWwindow *window, int width, int height);
void init_glfw() {
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
@ -67,25 +66,23 @@ void process_input(GLFWwindow *window, Scene& scene) {
}
if(glfwGetKey(window, GLFW_KEY_L) == GLFW_PRESS) {
scene.light.directional.adjust(-0.01f);
for(auto& light : scene.light.directional) {
light.adjust(-0.01f);
}
}
if(glfwGetKey(window, GLFW_KEY_P) == GLFW_PRESS) {
scene.light.directional.adjust(0.01f);
for(auto& light : scene.light.directional) {
light.adjust(0.01f);
}
}
}
Scene setup() {
glEnable(GL_DEPTH_TEST);
Scene scene{
.shader={"shaders/14-vert.glsl", "shaders/14-frag.glsl"},
.lightShader={"shaders/14-vert.glsl", "shaders/14-lightsource.frag.glsl"},
.models={
{"assets", "crate.obj"}
},
};
components::Scene config = utils::load_scene_config("config.json");
Scene scene(config);
scene.configure();
return scene;

View file

@ -19,10 +19,10 @@
#include <vector>
struct Model {
std::map<std::string, Texture> textures_loaded;
std::vector<Mesh> meshes;
std::string directory;
std::string model_path;
std::map<std::string, Texture> textures_loaded;
std::vector<Mesh> meshes;
TextureCounts texture_counts;
bool gammaCorrection;

View file

@ -1,49 +0,0 @@
#include "physics.hpp"
struct BoxTest Box2d_setup(b2World &world) {
BoxTest box;
float wall_x[4] = {0.0f, 0.0f, -0.99f, 0.99f};
float wall_y[4] = {-0.99, 0.99f, 0.0f, 0.0f};
float bound_w[4] = {1.0f, 1.0f, 0.1f, 0.1f};
float bound_h[4] = {0.1f, 0.1f, 1.0f, 1.0f};
for(size_t i = 0; i < 4; i++) {
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(wall_x[i], wall_y[i]);
b2Body *groundBody = world.CreateBody(&groundBodyDef);
b2PolygonShape groundBox;
groundBox.SetAsBox(bound_w[i], bound_h[i]);
groundBody->CreateFixture(&groundBox, 1.0f);
box.walls[i] = groundBody;
}
for(size_t i = 0; i < BODY_COUNT; i++) {
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.position.Set(0.0f, 0.5f);
box.bodies[i] = world.CreateBody(&bodyDef);
b2PolygonShape dynamicBox;
dynamicBox.SetAsBox(0.1f, 0.1f);
b2FixtureDef fixtureDef;
fixtureDef.shape = &dynamicBox;
fixtureDef.density = 1.0f;
fixtureDef.friction = 0.3f;
box.bodies[i]->CreateFixture(&fixtureDef);
}
return box;
}
void Box2d_step_world(b2World& world) {
float timeStep = 1.0f / 60.0f;
int velocityIterations = 6;
int positionIterations = 2;
// step the world
world.Step(timeStep, velocityIterations, positionIterations);
}

View file

@ -1,13 +0,0 @@
#pragma once
#include <box2d/box2d.h>
#define BODY_COUNT 1
struct BoxTest {
b2Body *walls[4];
b2Body *bodies[BODY_COUNT];
};
struct BoxTest Box2d_setup(b2World &world);
void Box2d_step_world(b2World& world);

View file

@ -8,18 +8,16 @@ void Scene::updateDelta() {
lastFrame = currentFrame;
}
void Scene::configure() {
// REFACTOR: really this should be done somewhere else
for(auto& model : models) {
for(auto& [name, model] : models) {
model.connect_shader(shader);
}
}
void Scene::draw_model(Model& scene_model, glm::mat4& projection, glm::mat4& view, glm::vec3& position)
void Scene::draw_model(Model& scene_model, Material& material, glm::mat4& projection, glm::mat4& view, glm::vec3& position)
{
glm::vec3 objectColor = {1.0, 0.94, 0.78};
shader.apply_material(cubeMaterial);
shader.apply_material(material);
float time = glfwGetTime();
glm::mat4 model = glm::mat4(1.0f);
@ -51,15 +49,15 @@ void Scene::render(GLFWwindow* window) {
shader.use();
shader.setMat4("view", view);
shader.setMat4("projection", projection);
shader.setVec3("viewPos", camera.pos);
shader.setVec3("viewPos", camera.position);
light.spot.position = camera.pos;
light.spot.direction = camera.front;
light.camera.position = camera.position;
light.camera.direction = camera.front;
shader.apply_lighting(light);
// BUG: no connection between models and positions
for(auto& position : positions) {
draw_model(models[0], projection, view, position);
for(auto& thing : things) {
draw_model(thing.model, thing.material, projection, view, thing.position);
}
}

View file

@ -10,98 +10,55 @@
#include "shader.hpp"
#include "model.hpp"
#include "camera.hpp"
#include "scene.hpp"
#include "components.hpp"
#include <vector>
const unsigned int SCR_WIDTH = 800;
const unsigned int SCR_HEIGHT = 600;
const float AMBIENT_LEVEL = 0.25f;
struct Thing {
Model& model;
components::Position position;
Material& material;
};
struct Scene {
Shader shader;
Shader lightShader;
std::vector<Model> models;
Lighting light{
.directional={
.direction={-0.2f, -1.0f, -0.3f},
.ambient={0.2f, 0.2f, 0.2f},
.diffuse={0.8f, 0.8f, 0.8f},
.specular={1.0f, 1.0f, 1.0f},
},
.positioned={
{
.position={1.2f, 1.0f, 2.0f},
.direction={-0.2f, -1.0f, -0.3f},
.ambient={0.2f, 0.2f, 0.2f},
.diffuse={0.8f, 0.8f, 0.8f},
.specular={1.0f, 1.0f, 1.0f},
.constant=1.0f,
.linear=0.09f,
.quadratic=0.032f,
.cutOff=12.5f,
.outerCutOff=17.5f,
},
{
.position={-1.2f, -1.0f, -2.0f},
.direction={0.2f, 1.0f, 0.3f},
.ambient={0.2f, 0.2f, 0.2f},
.diffuse={0.8f, 0.8f, 0.8f},
.specular={1.0f, 1.0f, 1.0f},
.constant=1.0f,
.linear=0.09f,
.quadratic=0.032f,
.cutOff=12.5f,
.outerCutOff=17.5f,
},
},
.spot={
.position={2.2f, 1.0f, 2.0f},
.direction={0.0f, 0.0f, -1.0f},
.ambient={0.2f, 0.2f, 0.2f},
.diffuse={0.8f, 0.8f, 0.8f},
.specular={1.0f, 1.0f, 1.0f},
.constant=1.0f,
.linear=0.09f,
.quadratic=0.032f,
.cutOff=12.5f,
.outerCutOff=17.5f,
},
};
// positions all containers
std::vector<glm::vec3> positions{
{0.0f, 0.0f, 0.0f},
{2.0f, 5.0f, -15.0f},
{-1.5f, -2.2f, -2.5f},
{-3.8f, -2.0f, -12.3f},
{ 2.4f, -0.4f, -3.5f},
{-1.7f, 3.0f, -7.5f},
{ 1.3f, -2.0f, -2.5f},
{ 1.5f, 2.0f, -2.5f},
{ 1.5f, 0.2f, -1.5f},
{-1.3f, 1.0f, -1.5f}
};
Camera camera{
.pos={2.2f, 1.0f, 2.0f},
.movementSpeed=2.0f
};
Material cubeMaterial{
.ambient = {1.0f, 0.5f, 0.31f},
.shininess = 32.0f,
.diffuseMap = 0,
.specularMap = 0,
};
Shader light_shader;
std::map<std::string, components::Material> materials;
Camera camera;
components::Lighting light;
std::map<std::string, Model> models;
std::vector<Thing> things;
float deltaTime = 0.0f;
float lastFrame = 0.0f;
Scene(components::Scene& config):
shader{config.shader.vertex_path, config.shader.frag_path},
light_shader{config.light_shader.vertex_path, config.shader.frag_path},
materials{config.materials},
camera{
.position=config.camera.position,
.front=config.camera.front,
.up=config.camera.up,
.direction=config.camera.direction,
.movement_speed=config.camera.movement_speed,
},
light{config.light}
{
for(auto& [name, model] : config.models) {
models.try_emplace(name, model.directory, model.model_path);
}
for(auto& thing : config.things) {
things.emplace_back(
models.at(thing.model),
thing.position,
materials.at(thing.material));
}
}
void updateDelta();
void configure();
void draw_model(Model& scene_model, glm::mat4& projection, glm::mat4& view, glm::vec3& position);
void draw_model(Model& scene_model, Material& material, glm::mat4& projection, glm::mat4& view, glm::vec3& position);
void render(GLFWwindow* window);
void cleanup();
};

View file

@ -60,7 +60,7 @@ unsigned int Shader::load_shader(const std::string& filename, GLenum shader_type
return shader_id;
}
Shader::Shader(const char* vertexPath, const char* fragmentPath) {
Shader::Shader(const std::string& vertexPath, const std::string& fragmentPath) {
unsigned int vertex = load_shader(vertexPath, GL_VERTEX_SHADER);
unsigned int fragment = load_shader(fragmentPath, GL_FRAGMENT_SHADER);
@ -131,27 +131,42 @@ void Shader::apply_material(const Material& material) {
}
void Shader::apply_lighting(const Lighting& lighting) {
apply_dir_light(lighting.directional);
setInt("pointLightCount", lighting.positioned.size());
setInt("dirLightCount", lighting.directional.size());
// need +1 for the camera spot light
setInt("spotLightCount", lighting.spot.size() + 1);
for(size_t i = 0; i < lighting.directional.size(); i++) {
apply_dir_light(lighting.directional[i], i);
}
for(size_t i = 0; i < lighting.positioned.size(); i++) {
apply_point_light(lighting.positioned[i], i);
}
apply_spot_light(lighting.spot);
// set the first spotlight to the camera light
apply_spot_light(lighting.camera, 0);
for(size_t i = 0; i < lighting.spot.size(); i++) {
// need to be off by one because the camera is a spot light
apply_spot_light(lighting.spot[i], i+1);
}
}
void Shader::apply_dir_light(const Light& light) {
setVec3("dirLight.direction", light.direction);
setVec3("dirLight.ambient", light.ambient);
setVec3("dirLight.diffuse", light.diffuse);
setVec3("dirLight.specular", light.specular);
void Shader::apply_dir_light(const Light& light, size_t index) {
dbc::check(index < MAX_LIGHTS, "too many directional lights");
setVec3(std::format("dirLights[{}].direction", index), light.direction);
setVec3(std::format("dirLights[{}].ambient", index), light.ambient);
setVec3(std::format("dirLights[{}].diffuse", index), light.diffuse);
setVec3(std::format("dirLights[{}].specular", index), light.specular);
}
void Shader::apply_point_light(const Light& light, size_t index) {
dbc::check(index < NR_POINT_LIGHTS, "too many positioned lights");
dbc::check(index < MAX_LIGHTS, "too many positioned lights");
// disgusting, crafting a string on every render?
setVec3(std::format("pointLights[{}].position", index), light.position);
setVec3(std::format("pointLights[{}].ambient", index), light.ambient);
setVec3(std::format("pointLights[{}].diffuse", index), light.diffuse);
@ -162,15 +177,17 @@ void Shader::apply_point_light(const Light& light, size_t index) {
setFloat(std::format("pointLights[{}].quadratic", index), light.quadratic);
}
void Shader::apply_spot_light(const Light& light) {
setVec3("spotLight.position", light.position);
setVec3("spotLight.direction", light.direction);
setVec3("spotLight.ambient", light.ambient);
setVec3("spotLight.diffuse", light.diffuse);
setVec3("spotLight.specular", light.specular);
setFloat("spotLight.constant", light.constant);
setFloat("spotLight.linear", light.linear);
setFloat("spotLight.quadratic", light.quadratic);
setFloat("spotLight.cutOff", glm::cos(glm::radians(light.cutOff)));
setFloat("spotLight.outerCutOff", glm::cos(glm::radians(light.outerCutOff)));
void Shader::apply_spot_light(const Light& light, size_t index) {
dbc::check(index < MAX_LIGHTS, "too many spot lights");
setVec3(std::format("spotLights[{}].position", index), light.position);
setVec3(std::format("spotLights[{}].direction", index), light.direction);
setVec3(std::format("spotLights[{}].ambient", index), light.ambient);
setVec3(std::format("spotLights[{}].diffuse", index), light.diffuse);
setVec3(std::format("spotLights[{}].specular", index), light.specular);
setFloat(std::format("spotLights[{}].constant", index), light.constant);
setFloat(std::format("spotLights[{}].linear", index), light.linear);
setFloat(std::format("spotLights[{}].quadratic", index), light.quadratic);
setFloat(std::format("spotLights[{}].cut_off", index), glm::cos(glm::radians(light.cut_off)));
setFloat(std::format("spotLights[{}].outer_cut_off", index), glm::cos(glm::radians(light.outer_cut_off)));
}

View file

@ -7,48 +7,16 @@
#include <vector>
#include <format>
#include "dbc.hpp"
#include "components.hpp"
struct Material {
glm::vec3 ambient{0.1f,0.1f,0.1f};
float shininess{32.0f};
unsigned int diffuseMap = 0;
unsigned int specularMap = 0;
};
struct Light {
glm::vec3 position{0.0f, 0.0f, 0.0f};
glm::vec3 direction{0.0f, 0.0f, 0.0f};
glm::vec3 ambient{0.0f, 0.0f, 0.0f};
glm::vec3 diffuse{0.0f, 0.0f, 0.0f};
glm::vec3 specular{0.0f, 0.0f, 0.0f};
float constant=0.0f;
float linear=0.0f;
float quadratic=0.0f;
float cutOff=0.0f;
float outerCutOff=0.0f;
void adjust(float amount) {
ambient += amount;
diffuse += amount;
specular += amount;
}
};
#define NR_POINT_LIGHTS 4
struct Lighting {
Light directional;
std::vector<Light> positioned;
Light spot;
};
using components::Material, components::Lighting, components::Light;
class Shader
{
public:
unsigned int ID = UINT_MAX;
Shader(const char* vertexPath, const char* fragmentPath);
Shader(const std::string& vertexPath, const std::string& fragmentPath);
unsigned int load_shader(const std::string& filename, GLenum shader_type);
void use() const;
void setBool(const std::string &name, bool value) const;
@ -63,7 +31,7 @@ public:
void apply_material(const Material& material);
void apply_lighting(const Lighting& lighting);
void apply_dir_light(const Light& light);
void apply_dir_light(const Light& light, size_t index);
void apply_point_light(const Light& light, size_t index);
void apply_spot_light(const Light& light);
void apply_spot_light(const Light& light, size_t index);
};

View file

@ -0,0 +1,11 @@
#include <fstream>
#include "json.hpp"
#include "utils.hpp"
namespace utils {
components::Scene load_scene_config(const std::string& config_file) {
std::ifstream f{"config.json"};
auto j = json::parse(f);
return j["scene"].template get<components::Scene>();
}
}

View file

@ -0,0 +1,9 @@
#pragma once
#include "components.hpp"
using json = nlohmann::json;
namespace utils {
components::Scene load_scene_config(const std::string& config_file);
}