diff --git a/16-scene-refactor/config.json b/16-scene-refactor/config.json new file mode 100644 index 0000000..4a82221 --- /dev/null +++ b/16-scene-refactor/config.json @@ -0,0 +1,96 @@ +{ + "scene": { + "shader": { + "vertex_path": "shaders/16-vert.glsl", + "frag_path": "shaders/16-frag.glsl" + }, + "light_shader": { + "vertex_path": "shaders/16-vert.glsl", + "frag_path": "shaders/16-lightsource.frag.glsl" + }, + "materials": { + "crate": { + "ambient": [0.5, 0.5, 0.5], + "shininess": 32.0, + "diffuseMap": 0, + "specularMap": 0 + }, + "popcorn": { + "ambient": [0.5, 0.25, 0.31], + "shininess": 10000.0, + "diffuseMap": 0, + "specularMap": 0 + } + }, + "models": { + "crate": { + "directory": "assets", + "model_path": "crate.obj" + } + }, + "things": [ + {"model": "crate", "position": [0.0, 0.0, 0.0], "material": "crate"}, + {"model": "crate", "position": [2.0, 5.0, -15.0], "material": "crate"}, + {"model": "crate", "position": [-1.5, -2.2, -2.5], "material": "crate"}, + {"model": "crate", "position": [-3.8, -2.0, -12.3], "material": "crate"}, + {"model": "crate", "position": [ 2.4, -0.4, -3.5], "material": "crate"}, + {"model": "crate", "position": [-1.7, 3.0, -7.5], "material": "crate"}, + {"model": "crate", "position": [ 1.3, -2.0, -2.5], "material": "crate"}, + {"model": "crate", "position": [ 1.5, 2.0, -2.5], "material": "crate"}, + {"model": "crate", "position": [ 1.5, 0.2, -1.5], "material": "crate"}, + {"model": "crate", "position": [-1.3, 1.0, -1.5], "material": "crate"} + ], + "camera": { + "position": [0.0, 0.0, 3.0], + "front": [0.0, 0.0, -1.0], + "up": [0.0, 1.0, 0.0], + "direction": [0.0, 0.0, 0.0], + "movement_speed": 10.0 + }, + "light": { + "directional": [ + { + "position": [0, 0, 0.0], + "direction":[-0.2, -1.0, -0.3], + "ambient":[0.2, 0.2, 0.2], + "diffuse":[0.8, 0.8, 0.8], + "specular":[1.0, 1.0, 1.0], + "constant": 0.0, + "linear": 0.0, + "quadratic": 0.0, + "cut_off": 0.0, + "outer_cut_off": 0.0 + } + ], + "positioned": [ + { + "position": [1.2, 1.0, 2.0], + "direction":[-0.2, -1.0, -0.3], + "ambient":[0.2, 0.2, 0.2], + "diffuse":[0.8, 0.8, 0.8], + "specular":[1.0, 1.0, 1.0], + "constant": 1.0, + "linear": 0.09, + "quadratic": 0.032, + "cut_off": 12.5, + "outer_cut_off": 17.5 + } + ], + "spot": [ + + ], + "camera": { + "position":[2.2, 1.0, 2.0], + "direction":[0.0, 0.0, -1.0], + "ambient":[0.2, 0.2, 0.2], + "diffuse":[0.8, 0.8, 0.8], + "specular":[1.0, 1.0, 1.0], + "constant": 1.0, + "linear": 0.09, + "quadratic": 0.032, + "cut_off": 12.5, + "outer_cut_off": 17.5 + } + } + } +} diff --git a/16-scene-refactor/meson.build b/16-scene-refactor/meson.build index 72a3c9a..6edf5a7 100644 --- a/16-scene-refactor/meson.build +++ b/16-scene-refactor/meson.build @@ -61,12 +61,12 @@ glfw3 = subproject('glfw').get_variable('glfw_dep') fuc2 = subproject('fuc2').get_variable('fuc2_dep') fmt = subproject('fmt').get_variable('fmt_dep') glm = subproject('glm').get_variable('glm_dep') -box2d = subproject('box2d').get_variable('box2d_dep') +json = subproject('nlohmann_json').get_variable('nlohmann_json_dep') assimp = subproject('assimp').get_variable('assimp_dep') dependencies += [ - glfw3, fuc2, fmt, glm, box2d, assimp + glfw3, fuc2, fmt, glm, assimp, json ] inc_dirs = ['src'] @@ -76,8 +76,8 @@ sources = [ 'src/dbc.cpp', 'src/stb_image.cpp', 'src/shader.cpp', - 'src/physics.cpp', 'src/mesh.cpp', + 'src/utils.cpp', 'src/model.cpp', 'src/scene.cpp', 'src/camera.cpp', diff --git a/16-scene-refactor/shaders/16-frag.glsl b/16-scene-refactor/shaders/16-frag.glsl new file mode 100644 index 0000000..4ca5ff5 --- /dev/null +++ b/16-scene-refactor/shaders/16-frag.glsl @@ -0,0 +1,162 @@ +#version 330 core +struct Material { + sampler2D diffuse; + sampler2D specular; + float shininess; +}; + +out vec4 FragColor; +in vec3 FragPos; +in vec3 Normal; +in vec2 TexCoords; + +uniform vec3 viewPos; +uniform Material material; + +struct DirLight { + vec3 direction; + vec3 ambient; + vec3 diffuse; + vec3 specular; +}; + +struct PointLight { + vec3 position; + vec3 ambient; + vec3 diffuse; + vec3 specular; + float constant; + float linear; + float quadratic; +}; + +struct SpotLight { + vec3 direction; + vec3 position; + vec3 diffuse; + vec3 specular; + vec3 ambient; + + float constant; + float linear; + float quadratic; + float cut_off; + float outer_cut_off; +}; + + +#define MAX_LIGHTS 4 +uniform int pointLightCount = 0; +uniform PointLight pointLights[MAX_LIGHTS]; + +uniform int dirLightCount = 0; +uniform DirLight dirLights[MAX_LIGHTS]; + +uniform int spotLightCount = 0; +uniform SpotLight spotLights[MAX_LIGHTS]; + +vec3 CalcDirLight(DirLight light, vec3 normal, vec3 viewDir) +{ + vec3 mat_tex = vec3(texture(material.diffuse, TexCoords)); + vec3 spec_tex = vec3(texture(material.specular, TexCoords)); + + vec3 lightDir = normalize(-light.direction); + // diffuse shading + float diff = max(dot(normal, lightDir), 0.0); + + // specular shading + vec3 reflectDir = reflect(-lightDir, normal); + float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess); + + vec3 ambient = light.ambient * mat_tex; + vec3 diffuse = light.diffuse * diff * mat_tex; + vec3 specular = light.specular * spec * spec_tex; + + return ambient + diffuse + specular; +} + +vec3 CalcPointLight(PointLight light, vec3 normal, vec3 fragPos, vec3 viewDir) +{ + vec3 mat_tex = vec3(texture(material.diffuse, TexCoords)); + vec3 spec_tex = vec3(texture(material.specular, TexCoords)); + vec3 lightDir = normalize(light.position - fragPos); + + // diffuse shading + float diff = max(dot(normal, lightDir), 0.0); + + // specular shading + vec3 reflectDir = reflect(-lightDir, normal); + float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess); + + // attenuation + float distance = length(light.position - fragPos); + float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance)); + + vec3 ambient = light.ambient * mat_tex; + vec3 diffuse = light.diffuse * diff * mat_tex; + vec3 specular = light.specular * spec * spec_tex; + + ambient *= attenuation; + diffuse *= attenuation; + specular *= attenuation; + + return ambient + diffuse + specular; +} + +vec3 CalcSpotLight(SpotLight light, vec3 normal, vec3 fragPos, vec3 viewDir) +{ + vec3 mat_tex = vec3(texture(material.diffuse, TexCoords)); + vec3 spec_tex = vec3(texture(material.specular, TexCoords)); + vec3 lightDir = normalize(light.position - fragPos); + + // diffuse shading + float diff = max(dot(normal, lightDir), 0.0); + + // specular shading + vec3 reflectDir = reflect(-lightDir, normal); + float spec = pow(max(dot(viewDir, reflectDir), 0.0), material.shininess); + + // attenuation + float distance = length(light.position - fragPos); + float attenuation = 1.0 / (light.constant + light.linear * distance + light.quadratic * (distance * distance)); + + vec3 ambient = light.ambient * mat_tex; + vec3 diffuse = light.diffuse * diff * mat_tex; + vec3 specular = light.specular * spec * spec_tex; + + float theta = dot(lightDir, normalize(-light.direction)); + float epsilon = light.cut_off - light.outer_cut_off; + float intensity = clamp((theta - light.outer_cut_off) / epsilon, 0.0, 1.0); + diffuse *= intensity; + specular *= intensity; + + ambient *= attenuation; + diffuse *= attenuation; + specular *= attenuation; + + return ambient + diffuse + specular; +} + +void main() +{ + vec3 norm = normalize(Normal); + vec3 viewDir = normalize(viewPos - FragPos); + + // kick it off with the first dirlight + vec3 result = CalcDirLight(dirLights[0], norm, viewDir); + + // then augment with more (off by one) + for(int i = 1; i < dirLightCount; i++) { + result += CalcDirLight(dirLights[i], norm, viewDir); + } + + for(int i = 0; i < pointLightCount; i++) { + result += CalcPointLight(pointLights[i], norm, FragPos, viewDir); + } + + for(int i = 0; i < spotLightCount; i++) { + result += CalcSpotLight(spotLights[i], norm, FragPos, viewDir); + } + + FragColor = vec4(result, 1.0); +} diff --git a/16-scene-refactor/shaders/16-lightsource.frag.glsl b/16-scene-refactor/shaders/16-lightsource.frag.glsl new file mode 100644 index 0000000..30728b2 --- /dev/null +++ b/16-scene-refactor/shaders/16-lightsource.frag.glsl @@ -0,0 +1,9 @@ +#version 330 core +out vec4 FragColor; + +uniform vec3 diffuse; + +void main() +{ + FragColor = vec4(diffuse, 1.0); // set all 4 vector values to 1.0 +} diff --git a/16-scene-refactor/shaders/16-vert.glsl b/16-scene-refactor/shaders/16-vert.glsl new file mode 100644 index 0000000..276f393 --- /dev/null +++ b/16-scene-refactor/shaders/16-vert.glsl @@ -0,0 +1,20 @@ +#version 330 core +layout (location = 0) in vec3 aPos; +layout (location = 1) in vec3 aNormal; +layout (location = 2) in vec2 aTexCoords; + +out vec3 FragPos; +out vec3 Normal; +out vec2 TexCoords; + +uniform mat4 model; +uniform mat4 view; +uniform mat4 projection; + +void main() +{ + gl_Position = projection * view * model * vec4(aPos, 1.0f); + FragPos = vec3(model * vec4(aPos, 1.0)); + Normal = mat3(transpose(inverse(model))) * aNormal; + TexCoords = aTexCoords; +} diff --git a/16-scene-refactor/src/camera.cpp b/16-scene-refactor/src/camera.cpp index b82557e..0d619b8 100644 --- a/16-scene-refactor/src/camera.cpp +++ b/16-scene-refactor/src/camera.cpp @@ -3,27 +3,27 @@ #include 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) { diff --git a/16-scene-refactor/src/camera.hpp b/16-scene-refactor/src/camera.hpp index 0da27be..2603e2d 100644 --- a/16-scene-refactor/src/camera.hpp +++ b/16-scene-refactor/src/camera.hpp @@ -2,11 +2,11 @@ #include 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; diff --git a/16-scene-refactor/src/components.hpp b/16-scene-refactor/src/components.hpp new file mode 100644 index 0000000..859e94a --- /dev/null +++ b/16-scene-refactor/src/components.hpp @@ -0,0 +1,105 @@ +#pragma once + +#include +#include +#include +#include "json.hpp" + +const unsigned int SCR_WIDTH = 800; +const unsigned int SCR_HEIGHT = 600; + +#define MAX_LIGHTS 4 + +namespace components { + template 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 directional; + std::vector positioned; + std::vector 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 materials; + std::map models; + std::vector things; + Camera camera; + Lighting light; + }; + + ENROLL_COMPONENT(Scene, shader, light_shader, materials, models, things, camera, light); +} diff --git a/16-scene-refactor/src/data.hpp b/16-scene-refactor/src/data.hpp deleted file mode 100644 index 7b4bbf6..0000000 --- a/16-scene-refactor/src/data.hpp +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once -#include - -#define CUBE_COUNT 9 diff --git a/16-scene-refactor/src/json.hpp b/16-scene-refactor/src/json.hpp new file mode 100644 index 0000000..02d0eb7 --- /dev/null +++ b/16-scene-refactor/src/json.hpp @@ -0,0 +1,59 @@ +#pragma once +#include +#include +#include +#include +#include + +#define ENROLL_COMPONENT(COMPONENT, ...) \ + NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(COMPONENT, __VA_ARGS__); \ + template <> struct NameOf { \ + static constexpr const char *name = #COMPONENT; \ + }; + +// partial specialization (full specialization works too) +namespace nlohmann { + + template <> + struct adl_serializer { + 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 + struct adl_serializer> { + static void to_json(json& j, const std::optional& opt) { + if (opt == std::nullopt) { + j = nullptr; + } else { + j = *opt; // this will call adl_serializer::to_json which will + // find the free function to_json in T's namespace! + } + } + + static void from_json(const json& j, std::optional& opt) { + if (j.is_null() || j == false) { + opt = std::nullopt; + } else { + opt = std::make_optional(j.template get()); + // same as above, but with adl_serializer::from_json + } + } + }; + + template<> + struct adl_serializer { + 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)}; + } + }; +} diff --git a/16-scene-refactor/src/main.cpp b/16-scene-refactor/src/main.cpp index 323f733..5340edc 100644 --- a/16-scene-refactor/src/main.cpp +++ b/16-scene-refactor/src/main.cpp @@ -1,10 +1,9 @@ #define _USE_MATH_DEFINES #include #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; diff --git a/16-scene-refactor/src/model.hpp b/16-scene-refactor/src/model.hpp index efbfc42..79af923 100644 --- a/16-scene-refactor/src/model.hpp +++ b/16-scene-refactor/src/model.hpp @@ -19,10 +19,10 @@ #include struct Model { - std::map textures_loaded; - std::vector meshes; std::string directory; std::string model_path; + std::map textures_loaded; + std::vector meshes; TextureCounts texture_counts; bool gammaCorrection; diff --git a/16-scene-refactor/src/physics.cpp b/16-scene-refactor/src/physics.cpp deleted file mode 100644 index daa8f87..0000000 --- a/16-scene-refactor/src/physics.cpp +++ /dev/null @@ -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); -} diff --git a/16-scene-refactor/src/physics.hpp b/16-scene-refactor/src/physics.hpp deleted file mode 100644 index dc98a31..0000000 --- a/16-scene-refactor/src/physics.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -#include - -#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); diff --git a/16-scene-refactor/src/scene.cpp b/16-scene-refactor/src/scene.cpp index 5988433..2ccb104 100644 --- a/16-scene-refactor/src/scene.cpp +++ b/16-scene-refactor/src/scene.cpp @@ -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); } } diff --git a/16-scene-refactor/src/scene.hpp b/16-scene-refactor/src/scene.hpp index c92b954..828c75e 100644 --- a/16-scene-refactor/src/scene.hpp +++ b/16-scene-refactor/src/scene.hpp @@ -10,98 +10,55 @@ #include "shader.hpp" #include "model.hpp" #include "camera.hpp" -#include "scene.hpp" +#include "components.hpp" #include -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 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 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 materials; + Camera camera; + components::Lighting light; + std::map models; + std::vector 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(); }; diff --git a/16-scene-refactor/src/shader.cpp b/16-scene-refactor/src/shader.cpp index c49d36e..0937d7d 100644 --- a/16-scene-refactor/src/shader.cpp +++ b/16-scene-refactor/src/shader.cpp @@ -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))); } diff --git a/16-scene-refactor/src/shader.hpp b/16-scene-refactor/src/shader.hpp index 87c99ce..594dd0d 100644 --- a/16-scene-refactor/src/shader.hpp +++ b/16-scene-refactor/src/shader.hpp @@ -7,48 +7,16 @@ #include #include #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 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); }; diff --git a/16-scene-refactor/src/utils.cpp b/16-scene-refactor/src/utils.cpp new file mode 100644 index 0000000..c978e79 --- /dev/null +++ b/16-scene-refactor/src/utils.cpp @@ -0,0 +1,11 @@ +#include +#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(); + } +} diff --git a/16-scene-refactor/src/utils.hpp b/16-scene-refactor/src/utils.hpp new file mode 100644 index 0000000..6bde00d --- /dev/null +++ b/16-scene-refactor/src/utils.hpp @@ -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); +} diff --git a/16-scene-refactor/tests/config.json b/16-scene-refactor/tests/config.json new file mode 100644 index 0000000..47d9449 --- /dev/null +++ b/16-scene-refactor/tests/config.json @@ -0,0 +1,74 @@ +{ + "material_test": { + "ambient": [0.1, 0.1, 0.1], + "shininess": 32.0, + "diffuseMap": 0, + "specularMap": 0 + }, + "lighting_test": { + "directional": [ + { + "position": [0, 0, 0.0], + "direction":[-0.2, -1.0, -0.3], + "ambient":[0.2, 0.2, 0.2], + "diffuse":[0.8, 0.8, 0.8], + "specular":[1.0, 1.0, 1.0], + "constant": 0.0, + "linear": 0.0, + "quadratic": 0.0, + "cut_off": 0.0, + "outer_cut_off": 0.0 + } + ], + "positioned": [ + { + "position": [1.2, 1.0, 2.0], + "direction":[-0.2, -1.0, -0.3], + "ambient":[0.2, 0.2, 0.2], + "diffuse":[0.8, 0.8, 0.8], + "specular":[1.0, 1.0, 1.0], + "constant": 1.0, + "linear": 0.09, + "quadratic": 0.032, + "cut_off": 12.5, + "outer_cut_off": 17.5 + } + ], + "spot": [ + + ], + "camera": { + "position":[2.2, 1.0, 2.0], + "direction":[0.0, 0.0, -1.0], + "ambient":[0.2, 0.2, 0.2], + "diffuse":[0.8, 0.8, 0.8], + "specular":[1.0, 1.0, 1.0], + "constant": 1.0, + "linear": 0.09, + "quadratic": 0.032, + "cut_off": 12.5, + "outer_cut_off": 17.5 + } + }, + + "camera_test": { + "position": [0.0, 0.0, 3.0], + "front": [0.0, 0.0, -1.0], + "up": [0.0, 1.0, 0.0], + "direction": [0.0, 0.0, 0.0], + "movement_speed": 20.0 + }, + "model_test": { + "directory": "assets", + "model_path": "create.obj" + }, + "thing_test": { + "model": "crate", + "position": [0.0, 0.0, 0.0], + "material": "crate" + }, + "shader_test": { + "vertex_path": "shaders/16-vert.glsl", + "frag_path": "shaders/16-frag.glsl" + } +} diff --git a/16-scene-refactor/tests/config_tests.cpp b/16-scene-refactor/tests/config_tests.cpp new file mode 100644 index 0000000..dbb22da --- /dev/null +++ b/16-scene-refactor/tests/config_tests.cpp @@ -0,0 +1,46 @@ +#include +#include "components.hpp" +#include +#include "json.hpp" + +#include +using json = nlohmann::json; + +using namespace fuc2; + +/* + * This also tests Mesh by using Model to load them. + */ +namespace config_tests { + void test_json_components() { + std::ifstream f{"tests/config.json"}; + auto j = json::parse(f); + auto mat = j["material_test"].get(); + + CHECK(mat.ambient.x == 0.1f, "wrong value"); + + auto light = j["lighting_test"].get(); + + auto camera = j["camera_test"].get(); + + auto model = j["model_test"].get(); + + auto thing = j["thing_test"].get(); + } + + + void test_load_json_config() { + std::ifstream f{"config.json"}; + auto j = json::parse(f); + auto scene = j["scene"].get(); + } + + fuc2::Set TESTS{ + .name="config", + .options={ .fail_fast=false }, + .tests={ + TEST(test_json_components), + TEST(test_load_json_config), + } + }; +} diff --git a/16-scene-refactor/tests/main.cpp b/16-scene-refactor/tests/main.cpp index a401b6b..d7f31cb 100644 --- a/16-scene-refactor/tests/main.cpp +++ b/16-scene-refactor/tests/main.cpp @@ -5,6 +5,7 @@ TEST_SET(shader_tests); TEST_SET(model_tests); +TEST_SET(config_tests); using namespace fuc2; @@ -33,7 +34,8 @@ int main(int argc, char* argv[]) { std::vector tests{ shader_tests::TESTS, - model_tests::TESTS + model_tests::TESTS, + config_tests::TESTS, }; return run_tests(tests, argc, argv); diff --git a/16-scene-refactor/tests/meson.build b/16-scene-refactor/tests/meson.build index 877a6d3..f327eb3 100644 --- a/16-scene-refactor/tests/meson.build +++ b/16-scene-refactor/tests/meson.build @@ -2,4 +2,5 @@ fuc2_tests = files( 'main.cpp', 'shader_tests.cpp', 'model_tests.cpp', + 'config_tests.cpp', ) diff --git a/16-scene-refactor/wraps/nlohmann_json.wrap b/16-scene-refactor/wraps/nlohmann_json.wrap new file mode 100644 index 0000000..8c46676 --- /dev/null +++ b/16-scene-refactor/wraps/nlohmann_json.wrap @@ -0,0 +1,11 @@ +[wrap-file] +directory = nlohmann_json-3.11.3 +lead_directory_missing = true +source_url = https://github.com/nlohmann/json/releases/download/v3.11.3/include.zip +source_filename = nlohmann_json-3.11.3.zip +source_hash = a22461d13119ac5c78f205d3df1db13403e58ce1bb1794edc9313677313f4a9d +source_fallback_url = https://github.com/mesonbuild/wrapdb/releases/download/nlohmann_json_3.11.3-1/nlohmann_json-3.11.3.zip +wrapdb_version = 3.11.3-1 + +[provide] +nlohmann_json = nlohmann_json_dep