From 4d5076dd90f01bbddb1310f47d286397a1358eba Mon Sep 17 00:00:00 2001 From: alistair Date: Mon, 20 Jun 2022 16:08:34 +1000 Subject: [PATCH] redo collision loading --- data/levels/init.json | 61 +++++++------- src/CMakeLists.txt | 3 + src/collision.cpp | 104 ++++++++++++++++++++++++ src/collision.h | 117 +++++++++++++++++++++++++++ src/debug_flags.h | 6 ++ src/drawing.cpp | 25 +++--- src/drawing.h | 1 + src/entity.h | 7 +- src/jsonloader.cpp | 27 +++++++ src/jsonloader.h | 13 +++ src/level.cpp | 183 +++++++++++++++++++++++++++++++++++------- src/level.h | 6 +- src/main.cpp | 2 +- src/mesh.cpp | 6 ++ src/mesh.h | 3 +- src/player.cpp | 2 + src/shaders.cpp | 4 + src/shaders.h | 3 + 18 files changed, 502 insertions(+), 71 deletions(-) create mode 100644 src/collision.cpp create mode 100644 src/collision.h create mode 100644 src/debug_flags.h create mode 100644 src/jsonloader.cpp create mode 100644 src/jsonloader.h diff --git a/data/levels/init.json b/data/levels/init.json index 29bcdb8..4c6212d 100644 --- a/data/levels/init.json +++ b/data/levels/init.json @@ -1,8 +1,8 @@ { "name": "init", "staticmodels": { - "room": { - "object": "room.obj" + "room2": { + "object": "room2.obj" }, "blob": { "object": "blob.obj" @@ -23,49 +23,56 @@ ["instanced.vert", "fogtest.frag"] ], "skyboxes": [ - "skybox" + "space" ], "lights": [ { "type": "point", - "position": [-2,0,0], + "position": [0,0.2,-3], "direction": [0,0,0], - "ambient": [0.1,0.0,0.2], - "diffuse": [0.0,0.1,0.1], - "specular": [1,1,1], + "ambient": [0.0,0.0,0.0], + "diffuse": [0.7,0.1,0.2], + "specular": [1,0,0], "attenuate": true, - "attenuation": [0.1,0.1,0.1] + "attenuation": [0.1,0.4,0.031] }, { "type": "point", - "position": [1,0,0], + "position": [0,2,0], "direction": [0,0,0], - "ambient": [0.2,0.0,0.02], - "diffuse": [0.1,0.5,0.1], + "ambient": [0.01,0.01,0.01], + "diffuse": [0.1,0.3,0.2], "specular": [1,1,1], "attenuate": true, - "attenuation": [0.1,0.1,0.1] + "attenuation": [0.2,0.4,0.1] } ], - "entities": [ + "physics": [ { - "draw": {"billboardmodel": "blob"}, - "physics": { - "position": [0,0,0], - "orientation": [0,0,0] - } - }, + "position": [0,0,10], + "orientation": [0,0,0], + "collision": [ + { + "AABB": [1,1] + } + ] + }, { - "draw": {"model": "room"}, - "physics": { - "position": [0,0,0], - "orientation": [0,0,0] - } - }, + "position": [1,0,0], + "orientation": [0,0,0], + "collision": [ + { + "Sphere": [1,1] + } + ] + } + ], + "entities": [ { - "draw": {"model": "orb"}, + "id": "room2", + "draw": {"model": "room2"}, "physics": { - "position": [8,2,0], + "position": [0,-0.4,0], "orientation": [0,0,0] } } diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 48eb20c..735f4ef 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -17,6 +17,9 @@ file(GLOB testgamefiles ) add_executable(testgame ${testgamefiles}) + +set_property(TARGET testgame PROPERTY CXX_STANDARD 20) + target_link_libraries(testgame ${GLEW_LIBRARIES}) target_link_libraries(testgame ${OPENGL_LIBRARIES}) target_link_libraries(testgame ${SDL2_LIBRARIES}) diff --git a/src/collision.cpp b/src/collision.cpp new file mode 100644 index 0000000..1514540 --- /dev/null +++ b/src/collision.cpp @@ -0,0 +1,104 @@ + +#include "jsonloader.h" +#include "collision.h" + + +namespace collision { + +// static CollisionInfo Collider::test(PlaneCollider a, SphereCollider b, glm::vec3 pa, glm::vec3 pb); + +// static CollisionInfo Collider::test(SphereCollider a, PlaneCollider b, glm::vec3 pa, glm::vec3 pb); + +// static CollisionInfo Collider::test(AABBCollider a, PlaneCollider b, glm::vec3 pa, glm::vec3 pb); + +// static CollisionInfo Collider::test(PlaneCollider a, AABBCollider b, glm::vec3 pa, glm::vec3 pb); + +// static CollisionInfo Collider::test(AABBCollider a, AABBCollider b, glm::vec3 pa, glm::vec3 pb); + + + CollisionInfo Collider::test(AABBCollider a, AABBCollider b, glm::vec3 pa, glm::vec3 pb) { + CollisionInfo c; + c.intersecting = false; + } + + + CollisionInfo Collider::test(SphereCollider a, SphereCollider b, glm::vec3 pa, glm::vec3 pb) { + CollisionInfo c; + c.intersecting = false; + if (glm::distance(pa, pb) < a.radius + b.radius) { + c.intersecting = true; + glm::vec3 restore = pa - pb; + restore *= (a.radius + b.radius) / (glm::distance(pa, pb)); + c.restore = restore; + } + return c; + } + + + std::optional AABBCollider::from_json(json j) { + // ["AABB": [0.1, 0.1]] + try { + if (!j.contains(AABBCollider::name)) { + return {}; + } + auto r = vec3_fromjson(j[AABBCollider::name]); + if (!r) return {}; + return AABBCollider(*r); + } catch (std::exception &e) { + return {}; + } + } + + std::optional SphereCollider::from_json(json j) { + // ["SphereCollider": {"radius": 0.01}] + try { + if (!j.contains(SphereCollider::name)) { + return {}; + } + return SphereCollider((double)j[SphereCollider::name]["radius"]); + } catch (std::exception &e) { + return {}; + } + } + + Collider::Collider (PlaneCollider p) : type(PLANE) { + type = PLANE; + inner.plane = p; + } + + Collider::Collider (SphereCollider s) { + type = SPHERE; + inner.sphere = s; + } + + Collider::Collider (AABBCollider a) { + inner.aabb = a; + type = AABB; + } + + Collider::Collider (FPlaneCollider f) { + type = FPLANE;; + inner.fplane = f; + } + + + std::optional Collider::from_json(json j) { + if (j.count(PlaneCollider::name)) { + return {}; + //return Collider(*PlaneCollider::from_json(j)); + } else if (j.count(FPlaneCollider::name)) { + return {}; + //return Collider(*FPlaneCollider::from_json(j)); + } else if (j.count(names[AABB])) { + return Collider(*AABBCollider::from_json(j)); + } else if (j.count(names[SPHERE])) { + return Collider(*SphereCollider::from_json(j)); + } else { + return {}; + } + } + + std::string Collider::get_name() { + return names[type]; + } +}; diff --git a/src/collision.h b/src/collision.h new file mode 100644 index 0000000..471581c --- /dev/null +++ b/src/collision.h @@ -0,0 +1,117 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "jsonloader.h" + +namespace collision { + +class CollisionInfo { +public: + bool intersecting; + glm::vec3 restore; +}; + +class PlaneCollider { +public: + static constexpr char * name = "Plane"; + glm::vec3 orientation; + PlaneCollider () {}; + PlaneCollider(glm::vec3 orientation) { + this->orientation = orientation; + } +}; + +class FPlaneCollider { +public: + static constexpr char * name = "FinitePlane"; + glm::vec3 orientation; + glm::vec2 extents; + FPlaneCollider() {} + FPlaneCollider (glm::vec3 orientation, glm::vec2 extents) { + this->orientation = orientation; + this->extents = extents; + } + +}; + +class AABBCollider { +public: + static constexpr char * name = "AABB"; + glm::vec3 size; + AABBCollider(){} + AABBCollider(glm::vec3 size) { + this->size = size; + } + + static std::optional from_json(json j); + +}; + +class SphereCollider { +public: + static constexpr char * name = "Sphere"; + double radius; + SphereCollider(){} + SphereCollider(double radius) { + this->radius = radius; + } + + static std::optional from_json(json j); + +}; + + + +class Collider { +private: + enum Type { + NONE, + PLANE, + FPLANE, + AABB, + SPHERE, + }; + + static constexpr char * names[] = { + "NONE", + "Plane", + "Finite Plane", + "AABB", + "Sphere" + }; + + Type type = NONE; + union Colliders { + PlaneCollider plane; + FPlaneCollider fplane; + AABBCollider aabb; + SphereCollider sphere; + Colliders() {} + } inner; +public: + Collider (PlaneCollider p); + Collider (SphereCollider s); + Collider (AABBCollider a); + Collider (FPlaneCollider f); + + static std::optional from_json(json j); + + static CollisionInfo test(PlaneCollider a, SphereCollider b, glm::vec3 pa, glm::vec3 pb); + static CollisionInfo test(SphereCollider a, PlaneCollider b, glm::vec3 pa, glm::vec3 pb); + static CollisionInfo test(AABBCollider a, PlaneCollider b, glm::vec3 pa, glm::vec3 pb); + static CollisionInfo test(PlaneCollider a, AABBCollider b, glm::vec3 pa, glm::vec3 pb); + static CollisionInfo test(AABBCollider a, AABBCollider b, glm::vec3 pa, glm::vec3 pb); + static CollisionInfo test(SphereCollider a, SphereCollider b, glm::vec3 pa, glm::vec3 pb); + + std::string get_name(); + +}; +}; + + diff --git a/src/debug_flags.h b/src/debug_flags.h new file mode 100644 index 0000000..3bb25d0 --- /dev/null +++ b/src/debug_flags.h @@ -0,0 +1,6 @@ + + + +#pragma once + +// #define SHOW_DRAW_ORDER diff --git a/src/drawing.cpp b/src/drawing.cpp index 0d36f22..e95fc26 100644 --- a/src/drawing.cpp +++ b/src/drawing.cpp @@ -2,6 +2,9 @@ #include "drawing.h" #include "glutil.h" #include "level.h" +#include "debug_flags.h" + + namespace drawing { @@ -145,27 +148,27 @@ void simple_object_draw_system::draw(const glm::mat4 view, const glm::mat4 proj +void setup_shader(Shader *shader, const glm::mat4 view, const glm::mat4 projection, const glm::vec3 position, const glm::mat4 orientation) { + + shader->use(); + shader->setMat4("projection", projection); + shader->setMat4("model", orientation); + shader->setMat4("view", glm::translate(view, position)); + +} void draw_objects(Shader *shader, const std::vector positions, std::shared_ptr m, const glm::mat4 view, const glm::mat4 projection) { shader->use(); - glCheckError(); - int loc = shader->get_uniform("projection"); - glUniformMatrix4fv(loc, 1, GL_FALSE, glm::value_ptr(projection)); - glCheckError(); + shader->setMat4("projection", projection); int i = 0; for (auto obj: positions) { - glCheckError(); - int loc = shader->get_uniform("model"); - glCheckError(); - glUniformMatrix4fv(loc, 1, GL_FALSE, glm::value_ptr(obj.orientation)); - glCheckError(); + shader->setMat4("model", obj.orientation); auto this_view = glm::translate(view, obj.pos); - loc = shader->get_uniform("view"); - glUniformMatrix4fv(loc, 1, GL_FALSE, glm::value_ptr(this_view)); + shader->setMat4("view", this_view); glCheckError(); m->draw(shader); diff --git a/src/drawing.h b/src/drawing.h index 6180f4e..1aa46f9 100644 --- a/src/drawing.h +++ b/src/drawing.h @@ -12,6 +12,7 @@ void draw_objects(Shader *shader, const std::vector positions, std::shared_ptr m, const glm::mat4 view, const glm::mat4 projection) ; +void setup_shader(Shader *shader, const glm::mat4 view, const glm::mat4 projection, const glm::vec3 position, const glm::mat4 orientation); namespace drawing { diff --git a/src/entity.h b/src/entity.h index a68d5ba..7da94ed 100644 --- a/src/entity.h +++ b/src/entity.h @@ -1,6 +1,10 @@ -#include #pragma once +#include +#include +#include +#include "collision.h" +#include enum class entity_type { STATIC_MESH, @@ -21,5 +25,6 @@ struct physics_model { glm::vec3 pos; glm::vec3 velocity; glm::mat4 orientation; + std::vector colliders; }; diff --git a/src/jsonloader.cpp b/src/jsonloader.cpp new file mode 100644 index 0000000..13f39ac --- /dev/null +++ b/src/jsonloader.cpp @@ -0,0 +1,27 @@ + + +#include "jsonloader.h" + +std::optional vec3_fromjson(json j) noexcept { + try { + glm::vec3 c {j[0], j[1], j[2]}; + return c; + } catch (std::exception &e) { + fmt::print("{} {}:{}:: {}", __FUNCTION__, __FILE__, __LINE__, e.what()); + return {}; + } +} + + +std::optional vec2_fromjson(json j) noexcept { + try { + glm::vec2 c {j[0], j[1]}; + return c; + } catch (std::exception &e) { + fmt::print("{} {}:{}:: {}", __FUNCTION__, __FILE__, __LINE__, e.what()); + return {}; + } +} + + + diff --git a/src/jsonloader.h b/src/jsonloader.h new file mode 100644 index 0000000..3f8465d --- /dev/null +++ b/src/jsonloader.h @@ -0,0 +1,13 @@ +#pragma once + +#include +using json = nlohmann::json; +#include +#include +#include +#include +#include + +std::optional vec3_fromjson(json j) noexcept; + +std::optional vec2_fromjson(json j) noexcept; \ No newline at end of file diff --git a/src/level.cpp b/src/level.cpp index 46400e4..27efa02 100644 --- a/src/level.cpp +++ b/src/level.cpp @@ -1,12 +1,51 @@ #include "level.h" +#include "entity.h" #include "player.h" #include - +#include "collision.h" +#include #define DIRECTIONAL_LIGHT 0 #define POINT_LIGHT 1 #define SPOT_LIGHT 2 + +std::optional level::physics_from_json(json p) noexcept { + + if (!(p.count("position") && p.count("orientation"))) { + return {}; + } + try { + // anonymous physics obj + glm::vec3 position {p["position"][0], p["position"][1], p["position"][2]}; + glm::mat4 orientation = glm::mat4(1.0); // identity matrix + orientation = glm::rotate(orientation, (float)p["orientation"][0], glm::vec3(1,0,0)); + orientation = glm::rotate(orientation, (float)p["orientation"][1], glm::vec3(0,1,0)); + orientation = glm::rotate(orientation, (float)p["orientation"][2], glm::vec3(0,0,1)); + + std::optional aabb {}; + + std::vector colliders {}; + + if (p.count("collision")) { + for (json c : p["collision"]) { + std::optional res = collision::Collider::from_json(c); + if (res) { + fmt::print("Added collision shape: {}\n", res->get_name()); + colliders.push_back(*res); + } + } + } + + return physics_model {.pos = position, .orientation = orientation, .colliders=colliders}; + + } catch (std::exception &e) { + return {}; + } + +}; + + auto LIGHT_TYPES = std::unordered_map {{"directional", 0}, {"point", 1}, {"spot", 2}}; @@ -51,6 +90,31 @@ void update_lights(Shader * shader, json &j) { glCheckError(); } + +void level::load_physics(json j) { + for (auto &p: j["physics"]) { + auto id = phys_meshes.size(); + auto id_slug = fmt::format("{}", id); + if (j.count("id")) { + id_slug = j.at("id"); + } + + if (phys_ids.count(id_slug)) { + fmt::print("warn::level::load_physics overwritten physics mesh: '{}'\n", id_slug); + } + + auto model = physics_from_json(p); + if (model) { + phys_meshes.push_back(*model); + phys_ids.insert({id_slug, id}); + } else { + fmt::print("warn::level::load_physics failed to load physics {}\n", id_slug); + } + + } +} + + void level::load_entities(json j) { int i = 0; for (auto &m: j["entities"]) { @@ -59,9 +123,14 @@ void level::load_entities(json j) { bool model = false; bool physics = false; + std::string entity_id = fmt::format("{}", i); + if (m.count("id")) { + entity_id = m.at("id"); + } + if (m.count("draw")) { + model = true; if (m["draw"].count("model")) { - model = true; e.model = model_ids.at(m["draw"].at("model")); e.type = entity_type::STATIC_MESH; } else if (m["draw"].count("billboardmodel")) { @@ -72,27 +141,33 @@ void level::load_entities(json j) { } if (m.count("physics")) { + auto p = m["physics"]; physics = true; - glm::vec3 position {p["position"][0], p["position"][1], p["position"][2]}; - glm::mat4 orientation = glm::mat4(1.0); // identity matrix - orientation = glm::rotate(orientation, (float)p["orientation"][0], glm::vec3(1,0,0)); - orientation = glm::rotate(orientation, (float)p["orientation"][1], glm::vec3(0,1,0)); - orientation = glm::rotate(orientation, (float)p["orientation"][2], glm::vec3(0,0,1)); - physics_model entity_physics {.pos = position, .orientation = orientation}; - int model_id = phys_meshes.size(); - phys_meshes.emplace_back(entity_physics); - e.physics = model_id; + physics_model entity_physics; + if (p.count("id")) { + e.physics = phys_ids[p.at("id")]; + } else { + auto ph = physics_from_json(p); + if (ph) { + entity_physics = *ph; + int model_id = phys_meshes.size(); + phys_meshes.emplace_back(entity_physics); + e.physics = model_id; + + auto physics_id = fmt::format("{}", entity_id); + phys_ids.insert({physics_id, model_id}); + } else { + throw std::exception {}; + } + } } - if (model && physics) { - auto phys = phys_meshes.at(e.physics); - auto model = models.at(e.model); - } else { - fmt::print("warn::level inivisible entity"); + if (! (model && physics)) { + fmt::print("warn::level invisible entity"); } entities.push_back(e); } @@ -109,6 +184,8 @@ level::level(const std::string &json_source) inp >> l; fmt::print("loading level{}", l["name"]); + + if (l == nullptr) { A_ERROR("level | failed to parse json"); } @@ -142,6 +219,7 @@ level::level(const std::string &json_source) } stbi_set_flip_vertically_on_load(false); + load_physics(l); load_entities(l); update_lights(shader, l); @@ -161,30 +239,75 @@ level::level(const std::string &json_source) void level::draw(const glm::mat4 view, const glm::mat4 projection) { - std::multimap order {}; +// std::multimap order {}; - for (const entity &e: entities) { - order.insert({phys_meshes[e.physics].pos.z, e.id}); - } + struct job { + Mesh *m; + physics_model phys; + entity e; + }; + std::multimap order {}; auto camera = player::get_camera(); auto camright = glm::vec3(view[0][0], view[1][0], view[2][0]); auto camup = camera->up; +#ifdef SHOW_DRAW_ORDER + static int prev = 0; + static int ticks = 0; + + int timen = SDL_GetTicks() / 300; + if (timen > ticks) { + ticks = timen; + prev++; + } +#endif + + + for (const entity &e: entities) { + for (int i = 0; i < models[e.model]->meshes.size(); i++) { + Mesh *m = &models[e.model]->meshes[0] + i; + glm::vec3 pos = phys_meshes[e.physics].pos + m->average_position; + pos = camera->pos - pos; + float depth = glm::length(pos); + + order.insert({depth, + job {m, phys_meshes[e.physics], e} + }); + } + } + + + int i = 0; for (auto iter = order.rbegin(); iter != order.rend(); ++iter) { +#ifdef SHOW_DRAW_ORDER + if (i++ >= prev % order.size() + 1) { + break; + } +#endif + auto k = iter->first; - auto v = iter->second; + job v = iter->second; - glm::mat4 draw_view = view; - auto phys = phys_meshes[entities[v].physics]; + auto model = v.phys.orientation; - if (entities[v].type == entity_type::BILLBOARD) { + if (v.e.type == entity_type::BILLBOARD) { + // particle billboarding // http://www.opengl-tutorial.org/intermediate-tutorials/billboards-particles/billboards/ - phys.orientation = glm::mat4(glm::mat3(glm::inverse(view))); - } else { - } + model = glm::mat4(glm::mat3(glm::inverse(view))); + } - draw_objects(shader, {phys}, models[entities[v].model], draw_view, projection); + setup_shader(shader, view, projection, v.phys.pos, model); + v.m->draw(shader); + + // glm::mat4 draw_view = view; + // auto phys = phys_meshes[entities[v].physics]; + + // } + + + +// draw_objects(shader, {phys}, models[entities[v].model], draw_view, projection); } } @@ -208,11 +331,13 @@ void level::update() { return; } - shader->use(); update_lights(shader, l); entities.clear(); + phys_meshes.clear(); + phys_ids.clear(); + load_physics(l); load_entities(l); } diff --git a/src/level.h b/src/level.h index 9256ab6..dd47270 100644 --- a/src/level.h +++ b/src/level.h @@ -18,6 +18,7 @@ using json = nlohmann::json; #include "entity.h" #include "errors.h" #include "drawing.h" +#include "collision.h" #pragma once @@ -31,10 +32,14 @@ static const char * SKYBOX_DIR = "data/objects/cubemaps"; class level { void load_entities(json j); + void load_physics(json j); + std::optional physics_from_json(json) noexcept; + std::optional entity_from_json(json) noexcept; public: int id; std::unordered_map model_ids; + std::unordered_map phys_ids; Shader *shader; std::string json_source_file; @@ -44,7 +49,6 @@ class level { std::vector entities; std::vector sounds; std::vector lights; - // level(); level(const std::string &json); diff --git a/src/main.cpp b/src/main.cpp index a696276..9f9f54d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -122,7 +122,7 @@ class SDLGLGLWindow { gWindow = SDL_CreateWindow( "ponder orb", SDL_WINDOWPOS_UNDEFINED, - SDL_WINDOWPOS_UNDEFINED, screen_width, screen_height, + SDL_WINDOWPOS_UNDEFINED, 800, 600, SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_RESIZABLE); diff --git a/src/mesh.cpp b/src/mesh.cpp index aeacb7f..0777e40 100644 --- a/src/mesh.cpp +++ b/src/mesh.cpp @@ -13,6 +13,12 @@ Mesh::Mesh(std::vector vertices, std::vector indice this->indices = indices; this->textures = textures; + average_position = glm::vec3(0); + for (auto v: vertices) { + average_position += v.position; + } + average_position /= vertices.size(); + setupMesh(); } diff --git a/src/mesh.h b/src/mesh.h index 7f73b38..6676f08 100644 --- a/src/mesh.h +++ b/src/mesh.h @@ -37,6 +37,7 @@ class Mesh { std::vector textures); void draw(Shader *shader); ~Mesh() = default; + glm::vec3 average_position; private: unsigned int VAO, VBO, EBO; void setupMesh(); @@ -50,12 +51,12 @@ class Model { } void draw(Shader *shader); ~Model() = default; - private: Assimp::Importer importer; std::vector meshes; std::string directory; std::vector textures_loaded; + private: unsigned int texture_from_file(const char *fname, std::string directory); void load_model(std::string path); diff --git a/src/player.cpp b/src/player.cpp index d46abb8..10586d8 100644 --- a/src/player.cpp +++ b/src/player.cpp @@ -77,6 +77,8 @@ namespace player { amount *= glm::vec3(1,0,1); } + fmt::print("Player {} {} {}\n", physics.pos.x, physics.pos.y, physics.pos.z); + physics.velocity = amount; diff --git a/src/shaders.cpp b/src/shaders.cpp index 0cf2dcb..8cec3d6 100644 --- a/src/shaders.cpp +++ b/src/shaders.cpp @@ -224,6 +224,10 @@ void Shader::setVec3(const std::string &name, glm::vec3 vec) { glUniform3f(glGetUniformLocation(program, name.c_str()), vec.x, vec.y, vec.z); } +void Shader::setMat4(const std::string &name, glm::mat4 mat) { + int loc = get_uniform(name); + glUniformMatrix4fv(loc, 1, GL_FALSE, glm::value_ptr(mat)); +} GLint Shader::get_attrib(std::string attrib) { diff --git a/src/shaders.h b/src/shaders.h index 45bae02..752fd2a 100644 --- a/src/shaders.h +++ b/src/shaders.h @@ -12,6 +12,8 @@ #include #include +#include +#include class Shader { std::string filename_vertex; @@ -44,6 +46,7 @@ class Shader { void setBool(const std::string &name, bool value); void setInt(const std::string &name, int value); void setVec3(const std::string &name, glm::vec3 vec); + void setMat4(const std::string &name, glm::mat4 mat); GLint get_attrib(std::string attribute_name); GLint get_uniform(std::string uniform_name); };