From c85984fc7ff2b3559a7ff7929682e59ec9c011bc Mon Sep 17 00:00:00 2001 From: Jay Robson Date: Mon, 18 Mar 2024 21:57:06 +1100 Subject: [PATCH] added cameras --- assets/scene.blend | 4 +- assets/scene.glb | 4 +- assets/shader/blur.fsh | 24 +-- assets/shader/blur.vsh | 14 -- assets/shader/main.fsh | 19 +- assets/shader/post_base.vsh | 21 ++ assets/texture/scene/Keys.001.png | Bin 13935 -> 13969 bytes assets/texture/scene/Player.png | Bin 0 -> 105913 bytes src/electric/generator.cpp | 2 +- src/graphics/camera.cpp | 11 +- src/graphics/camera.hpp | 5 +- src/graphics/{mesh => data}/arrays.cpp | 2 +- src/graphics/{mesh => data}/arrays.hpp | 2 +- src/graphics/data/camera.hpp | 22 ++ src/graphics/{mesh => data}/font.cpp | 2 +- src/graphics/{mesh => data}/font.hpp | 2 +- src/graphics/{mesh => data}/gllight.cpp | 12 +- src/graphics/{mesh => data}/gllight.hpp | 3 +- src/graphics/{mesh => data}/glmesh.cpp | 34 ++- src/graphics/{mesh => data}/glmesh.hpp | 7 +- src/graphics/{mesh => data}/light.hpp | 2 +- src/graphics/{mesh => data}/mesh.cpp | 23 +- src/graphics/{mesh => data}/mesh.hpp | 7 +- src/graphics/{mesh => data}/meshgen.hpp | 4 +- src/graphics/{mesh => data}/model.cpp | 34 ++- src/graphics/{mesh => data}/model.hpp | 6 +- src/graphics/{mesh => data}/texture.cpp | 2 +- src/graphics/{mesh => data}/texture.hpp | 2 +- src/graphics/equipment/generator.cpp | 3 +- src/graphics/equipment/generator.hpp | 12 +- src/graphics/equipment/pool.cpp | 1 + src/graphics/equipment/pool.hpp | 12 +- src/graphics/equipment/reactor.cpp | 16 +- src/graphics/equipment/reactor.hpp | 12 +- src/graphics/input/focus.cpp | 22 +- src/graphics/input/focus.hpp | 1 + src/graphics/locations.cpp | 47 ----- src/graphics/locations.hpp | 12 -- src/graphics/monitor/cctv.cpp | 270 ++++++++++++++++++++++++ src/graphics/monitor/cctv.hpp | 47 +++++ src/graphics/monitor/core.cpp | 34 +-- src/graphics/monitor/core.hpp | 22 +- src/graphics/monitor/primary_loop.cpp | 8 +- src/graphics/monitor/primary_loop.hpp | 32 +-- src/graphics/monitor/secondary_loop.cpp | 9 +- src/graphics/monitor/secondary_loop.hpp | 30 +-- src/graphics/monitor/turbine.cpp | 8 +- src/graphics/monitor/turbine.hpp | 30 +-- src/graphics/monitor/vessel.cpp | 8 +- src/graphics/monitor/vessel.hpp | 12 +- src/graphics/resize.cpp | 1 + src/graphics/shader.cpp | 89 ++++---- src/graphics/shader.hpp | 17 +- src/graphics/ui.cpp | 23 +- src/graphics/widget/clock.cpp | 7 +- src/graphics/widget/clock.hpp | 4 +- src/graphics/window.cpp | 121 +++++++---- src/graphics/window.hpp | 1 + src/main.cpp | 1 - src/util/math.hpp | 17 +- 60 files changed, 811 insertions(+), 388 deletions(-) delete mode 100644 assets/shader/blur.vsh create mode 100644 assets/shader/post_base.vsh create mode 100644 assets/texture/scene/Player.png rename src/graphics/{mesh => data}/arrays.cpp (97%) rename src/graphics/{mesh => data}/arrays.hpp (92%) create mode 100644 src/graphics/data/camera.hpp rename src/graphics/{mesh => data}/font.cpp (99%) rename src/graphics/{mesh => data}/font.hpp (72%) rename src/graphics/{mesh => data}/gllight.cpp (88%) rename src/graphics/{mesh => data}/gllight.hpp (84%) rename src/graphics/{mesh => data}/glmesh.cpp (73%) rename src/graphics/{mesh => data}/glmesh.hpp (74%) rename src/graphics/{mesh => data}/light.hpp (91%) rename src/graphics/{mesh => data}/mesh.cpp (92%) rename src/graphics/{mesh => data}/mesh.hpp (89%) rename src/graphics/{mesh => data}/meshgen.hpp (83%) rename src/graphics/{mesh => data}/model.cpp (88%) rename src/graphics/{mesh => data}/model.hpp (83%) rename src/graphics/{mesh => data}/texture.cpp (98%) rename src/graphics/{mesh => data}/texture.hpp (87%) delete mode 100644 src/graphics/locations.cpp delete mode 100644 src/graphics/locations.hpp create mode 100644 src/graphics/monitor/cctv.cpp create mode 100644 src/graphics/monitor/cctv.hpp diff --git a/assets/scene.blend b/assets/scene.blend index 1364395..e7c4615 100644 --- a/assets/scene.blend +++ b/assets/scene.blend @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:bb00bcd432a1380d925bec21896b8fc5d03f5cf828c7081e56e7813383b7bb70 -size 15915245 +oid sha256:b85c7237e37ff2b9ba6b0d3407f27989c1173b3e4c0eee5776fe52effd26ad69 +size 16317033 diff --git a/assets/scene.glb b/assets/scene.glb index 0e3e0c9..e41dd76 100644 --- a/assets/scene.glb +++ b/assets/scene.glb @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c75a3989a2bc1edcead17eb35e26d800b66f43d53f21a9cccde5ae7830c040ef -size 1876068 +oid sha256:588b2c0d24f195a86b22f5d45aec1008a8b8c35164797c4d74f1631de926d8d7 +size 1932828 diff --git a/assets/shader/blur.fsh b/assets/shader/blur.fsh index 14d3baf..b44da61 100644 --- a/assets/shader/blur.fsh +++ b/assets/shader/blur.fsh @@ -1,26 +1,26 @@ #version 460 core -uniform int samples; uniform sampler2D tex; +uniform vec2 direction; +uniform int samples; -in vec2 texPos; - +in vec2 FragPos; out vec4 FragColour; void main() { - vec2 texel_size = 1.f / vec2(textureSize(tex, 0)); - float samples_n = pow(samples * 2 + 1, 2); - vec4 colour; - - for(int x = -samples; x <= samples; x++) - for(int y = -samples; y <= samples; y++) + int radius = (samples - 1) / 2; + ivec2 size = textureSize(tex, 0); + vec2 step = direction / size; + vec4 sum = vec4(0.f); + + for(int i = -radius; i <= radius; i++) { - vec2 off = texel_size * vec2(x, y); - colour += texture2D(tex, texPos + off); + vec2 offset = vec2(i) * step; + sum += texture(tex, FragPos + offset); } - FragColour = vec4((colour / samples_n).rgb, 1); + FragColour = sum / float(samples); } diff --git a/assets/shader/blur.vsh b/assets/shader/blur.vsh deleted file mode 100644 index eea7bfb..0000000 --- a/assets/shader/blur.vsh +++ /dev/null @@ -1,14 +0,0 @@ - -#version 460 core - -layout (location = 1) in vec2 aTexPos; -layout (location = 2) in vec4 aPos; - -out vec2 texPos; - -void main() -{ - texPos = aTexPos; - gl_Position = aPos; -} - diff --git a/assets/shader/main.fsh b/assets/shader/main.fsh index 272e354..086b51d 100644 --- a/assets/shader/main.fsh +++ b/assets/shader/main.fsh @@ -122,23 +122,26 @@ void main() vec3 N = normalize(vin.normal); vec3 V = normalize(camera_pos - vin.pos.xyz); - vec3 F0 = vec3(0.04f); - F0 = mix(F0, albedo_lin, metalness); - + vec3 F0 = mix(vec3(0.04f), albedo_lin, metalness); + vec3 ambient = vec3(vin.ambient) * albedo_lin * brightness; vec3 Lo = vec3(0.f); + for(int i = 0; i < lights_count; i++) { Light l = lights[i]; - - float light_m; - vec3 L = normalize(l.pos.xyz - vin.pos); float d = length(vin.pos - l.pos.xyz); + vec3 L = (l.pos.xyz - vin.pos) / d; + float light_m; if(shadows_enabled) { float max_d = texture(shadow_maps[i], -L).r * far_plane; light_m = Ramp(d - max_d, 0.f, 2.5e-2f, 1.f, 0.f); - if(light_m <= 0.f) continue; + + if(light_m <= 0.f) + { + continue; + } } else @@ -162,14 +165,12 @@ void main() vec3 numerator = NDF * G * F; float denominator = 4.f * max(dot(N, V), 0.f) * max(dot(N, L), 0.f) + 1e-4f; vec3 specular = numerator / denominator; - // add to outgoing radiance Lo float NdotL = max(dot(N, L), 0.f); Lo += (kD * albedo_lin / PI + specular) * radiance * NdotL * light_m; } - vec3 ambient = vec3(vin.ambient) * albedo_lin * brightness; vec3 light = LinRGB_To_sRGB(ambient + Lo); light = mix(light, albedo.rgb, luminance); diff --git a/assets/shader/post_base.vsh b/assets/shader/post_base.vsh new file mode 100644 index 0000000..6891ada --- /dev/null +++ b/assets/shader/post_base.vsh @@ -0,0 +1,21 @@ + +#version 460 core + +const vec2 QuadVertices[6] = vec2[6]( + vec2(-1.0, 1.0), + vec2(-1.0, -1.0), + vec2( 1.0, -1.0), + vec2(-1.0, 1.0), + vec2( 1.0, -1.0), + vec2( 1.0, 1.0) +); + +out vec2 FragPos; + +void main() +{ + vec2 vertex = QuadVertices[gl_VertexID]; + FragPos = vertex * 0.5f + 0.5f; + gl_Position = vec4(vertex, 0.f, 1.f); +} + diff --git a/assets/texture/scene/Keys.001.png b/assets/texture/scene/Keys.001.png index 4fc0f99a9537fbe0bb058c4d65246f80d9a02d22..70b1f8632fcf3ce56f60d92c7452ac7ebc767c3a 100644 GIT binary patch delta 778 zcmaE#Gck993gefFs#|=Ftqd&`49%=e%&km}7+4q>Ca{A@1_L8w1ICFEHX{==1H%HS zxS@do;{s*|MxYuH27y`4Df~cQ^JGRwiFyZ_qw$x!lysZQ1PV_C%zd*+sc$Qn>k5Ia z)-YKgy}wOryq;a%iyuT4d`RB->cOKmtD9FVaMU$eSX*fB^632U&83@UWAX0z?&|*C z@3osGTsQ<xBP$oaF5yVqSqQDYEY!JfQ@(4Yxe)m_v`OQ9bM_EuQ`XMDo*BkGS7>< ztWmtjf7+GJxyC6_!?x~A-~MYXY4)D^C0lL?8f@T=i77Wp<==6Kv28xj%_*^(9>E%7 zjo*!bEweEw;r_C|a_`pfg})e5R<8cSTTsB!GhL(l?(@5~clZ9SUUv6BztrKP=IXN? z>I@9L6`3IsB@w=xXD~T3sTdoYo0=LKnCV)YnkMR+n46~PT9_Le>l&FErWhNUrkEHf znoR!7BCld%Xpokcl$NNQmTYXMYm#DMqHAGbYN4B)nq+8Vl#*g$Y-T*!fmK~4#WFE5 zG1W3zH_0^3QrE=P+*CI)In7u%CB;0=GC9RO)x^wXayP3JpP_-diGhWgk)frjk%i&p z+pO(epfnZ&PGggE*i?|Yv)Fo75|fRLQcVnubxn;^l66fC&5eL=O-<5GF*8oHOai*v zFvWDTH@gRyu`bM*&2!lcIqVL)S}g*mKDH!pcNcIvWcca4yB^5mEbxddW?ao-HzzZMoHa69xn#oW^;`?Xe;BkWJd&6+W99kIB&m5{Oa1SLlpD@_diQ(!S8dkG zjCPF{2TKks-BXx%b4$s&-$w+)rinZ%e&pG#=ke!6>CLC+=TzJO+88IL7=Jz@n6&W9 z(IWB9n-&Tk?_AuqDsW%D{qs$SD!VsFt=;_1_9ypY*4)&YueZ2OJft*D!KtCgN^8X- zr=GH25n)Ly|EeGEGW$LBX2ysb6zMEr6P@&!{eScQ&8FR#gY$wcI9cLzekaRtyk%P3 zmRm1>yX-X!hYfRh)baRirp@!0m|I1C;gGn&rnk;|)*kklcMOO9ccpmrS%@rnsxQ{~ z-T2os8-oh&FKc%eXU~89h4I9tHJ8i}98@$2T=b^2zx;dI`M3Ayefv|#uwj1De#OSQ zZ#HjW@@7&oG)y&1wJ}dylxk_BYhYwzo?>X3XqjZ3Iys3|UBx2RG||}D&`j6T)Yw$l z#3;p5*TTdkSvM`kG&RvAB_+wkBxUkiRwq6KGcyZAQv)MIb7OO3(n+qpn#DFmFB zCik(aAanPy^{SYfrzWK&8ky>vCM6~T-D;Mmn`n|`rkiSFV4P-=YG`6>C60Cf`; AKmY&$ diff --git a/assets/texture/scene/Player.png b/assets/texture/scene/Player.png new file mode 100644 index 0000000000000000000000000000000000000000..7b0ee6ccf7aaa79ac9275ba420d83aae67a49288 GIT binary patch literal 105913 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

mmKNnFCx7UmGJp0^E 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 double energy_friction = get_rpm() / 60 * dt * friction; double work = Util::Math::j_to_ms2(energy_input - energy_friction, mass); - phase = std::fmod(phase + Util::Math::map( get_rpm(), 0, 3600, 0, 120 * M_PI ) * dt, 2 * M_PI); + phase = std::fmod(phase + Util::Math::map( get_rpm(), 0, 3600, 0, 120 * M_PI ) * dt, 4 * M_PI); // do energy transfer stuff here if(breaker_closed) diff --git a/src/graphics/camera.cpp b/src/graphics/camera.cpp index 4e9a168..22d2bef 100644 --- a/src/graphics/camera.cpp +++ b/src/graphics/camera.cpp @@ -3,7 +3,7 @@ #include #include "camera.hpp" -#include "mesh/mesh.hpp" +#include "data/mesh.hpp" #include "input/keyboard.hpp" #include "../util/math.hpp" @@ -19,7 +19,7 @@ static bool on_ground = false; static double yaw = 0, pitch = 0; static glm::vec<3, double> pos(0, 0, 2); static glm::vec<3, double> velocity(0); -static Mesh collision_scene; +static Data::Mesh collision_scene; static glm::mat4 camera_mat; Json::Value Camera::serialize() @@ -79,7 +79,12 @@ glm::vec<3, double> Camera::get_pos() return pos; } -void Camera::init(const Model& model) +glm::vec<3, double> Camera::get_pos_base() +{ + return pos - glm::vec<3, double>(0, 0, 1.5); +} + +void Camera::init(const Data::Model& model) { collision_scene = model.load("collision"); } diff --git a/src/graphics/camera.hpp b/src/graphics/camera.hpp index 8da9d6f..bfeec35 100644 --- a/src/graphics/camera.hpp +++ b/src/graphics/camera.hpp @@ -5,13 +5,14 @@ #include #include "../system.hpp" -#include "mesh/model.hpp" +#include "data/model.hpp" namespace Sim::Graphics::Camera { glm::vec<3, double> get_normal(); glm::vec<3, double> get_pos(); +glm::vec<3, double> get_pos_base(); glm::mat4 get_matrix(); double get_pitch(); double get_yaw(); @@ -19,7 +20,7 @@ double get_yaw(); Json::Value serialize(); void load(const Json::Value& node); -void init(const Model& model); +void init(const Data::Model& model); void rotate(double pitch, double yaw); void move(double x, double y, double z); void update(double dt); diff --git a/src/graphics/mesh/arrays.cpp b/src/graphics/data/arrays.cpp similarity index 97% rename from src/graphics/mesh/arrays.cpp rename to src/graphics/data/arrays.cpp index 87a6d87..aaaba0c 100644 --- a/src/graphics/mesh/arrays.cpp +++ b/src/graphics/data/arrays.cpp @@ -9,7 +9,7 @@ #include "arrays.hpp" #include "font.hpp" -using namespace Sim::Graphics; +using namespace Sim::Graphics::Data; static void* ptr_diff(void* a, void* b) { diff --git a/src/graphics/mesh/arrays.hpp b/src/graphics/data/arrays.hpp similarity index 92% rename from src/graphics/mesh/arrays.hpp rename to src/graphics/data/arrays.hpp index 8377346..734ad33 100644 --- a/src/graphics/mesh/arrays.hpp +++ b/src/graphics/data/arrays.hpp @@ -4,7 +4,7 @@ #include #include -namespace Sim::Graphics::Arrays +namespace Sim::Graphics::Data::Arrays { struct Vertex diff --git a/src/graphics/data/camera.hpp b/src/graphics/data/camera.hpp new file mode 100644 index 0000000..c1e4fce --- /dev/null +++ b/src/graphics/data/camera.hpp @@ -0,0 +1,22 @@ + +#pragma once + +#include + +namespace Sim::Graphics::Data +{ + +struct Camera +{ + glm::vec3 pos; + glm::vec3 look; + glm::vec3 up; + float fov; + + float pitch = 0; + float yaw = 0; + float zoom = 1; +}; + +}; + diff --git a/src/graphics/mesh/font.cpp b/src/graphics/data/font.cpp similarity index 99% rename from src/graphics/mesh/font.cpp rename to src/graphics/data/font.cpp index 5ad81e3..d3f9a68 100644 --- a/src/graphics/mesh/font.cpp +++ b/src/graphics/data/font.cpp @@ -13,7 +13,7 @@ #include "arrays.hpp" #include "font.hpp" -using namespace Sim::Graphics; +using namespace Sim::Graphics::Data; struct Character { diff --git a/src/graphics/mesh/font.hpp b/src/graphics/data/font.hpp similarity index 72% rename from src/graphics/mesh/font.hpp rename to src/graphics/data/font.hpp index 40df8ab..552e9a1 100644 --- a/src/graphics/mesh/font.hpp +++ b/src/graphics/data/font.hpp @@ -6,7 +6,7 @@ #include #include -namespace Sim::Graphics::Font +namespace Sim::Graphics::Data::Font { void init(); diff --git a/src/graphics/mesh/gllight.cpp b/src/graphics/data/gllight.cpp similarity index 88% rename from src/graphics/mesh/gllight.cpp rename to src/graphics/data/gllight.cpp index 38eeae3..5def4a8 100644 --- a/src/graphics/mesh/gllight.cpp +++ b/src/graphics/data/gllight.cpp @@ -10,7 +10,7 @@ #include #include -using namespace Sim::Graphics; +using namespace Sim::Graphics::Data; static glm::mat4 shadow_mats[6]; @@ -64,12 +64,20 @@ void GLLight::render() glm::mat4 camera_mat = glm::translate(glm::mat4(1), -light.pos); glUniformMatrix4fv(Shader::LIGHT["camera"], 1, false, &camera_mat[0][0]); glBindFramebuffer(GL_FRAMEBUFFER, fbo); - glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); glViewport(0, 0, size, size); Window::render_scene(); } +void GLLight::render_player() +{ + glm::mat4 camera_mat = glm::translate(glm::mat4(1), -light.pos); + glUniformMatrix4fv(Shader::LIGHT["camera"], 1, false, &camera_mat[0][0]); + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glViewport(0, 0, size, size); + Window::render_player(); +} + void GLLight::init() { glm::mat4 shadow_proj = glm::perspective(M_PI * 0.5f, 1.0f, 0.01f, 100.f); diff --git a/src/graphics/mesh/gllight.hpp b/src/graphics/data/gllight.hpp similarity index 84% rename from src/graphics/mesh/gllight.hpp rename to src/graphics/data/gllight.hpp index e14e202..44c4c25 100644 --- a/src/graphics/mesh/gllight.hpp +++ b/src/graphics/data/gllight.hpp @@ -3,7 +3,7 @@ #include "light.hpp" -namespace Sim::Graphics +namespace Sim::Graphics::Data { struct GLLight @@ -22,6 +22,7 @@ struct GLLight static void init(); void render(); + void render_player(); }; }; diff --git a/src/graphics/mesh/glmesh.cpp b/src/graphics/data/glmesh.cpp similarity index 73% rename from src/graphics/mesh/glmesh.cpp rename to src/graphics/data/glmesh.cpp index e313219..1725b14 100644 --- a/src/graphics/mesh/glmesh.cpp +++ b/src/graphics/data/glmesh.cpp @@ -8,7 +8,7 @@ #include "../camera.hpp" #include "../../util/streams.hpp" -using namespace Sim::Graphics; +using namespace Sim::Graphics::Data; constexpr static void init(GLMesh* m) { @@ -52,20 +52,25 @@ GLMesh::~GLMesh() if(vao) glDeleteVertexArrays(1, &vao); } -void GLMesh::bind(bool bind_ssbo) +void GLMesh::bind() { init(this); glBindVertexArray(vao); - - if(ssbo_size > 0 && bind_ssbo) - { - glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo); - glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, ssbo); - } } -void GLMesh::set(const Mesh& m, int mode, bool send_ssbo) +void GLMesh::bind_ssbo() +{ + if(!ssbo) + { + glGenBuffers(1, &ssbo); + } + + glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo); + glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, ssbo); +} + +void GLMesh::set(const Mesh& m, int mode) { glBindBuffer(GL_ARRAY_BUFFER, vbo); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo); @@ -74,17 +79,6 @@ void GLMesh::set(const Mesh& m, int mode, bool send_ssbo) this->size = m.indices.size(); this->ssbo_size = m.transforms.size(); - - if(ssbo_size > 0 && send_ssbo) - { - if(ssbo == 0) - { - glGenBuffers(1, &ssbo); - } - - glBindBuffer(GL_SHADER_STORAGE_BUFFER, ssbo); - glBufferData(GL_SHADER_STORAGE_BUFFER, m.transforms.size() * sizeof(m.transforms[0]), &m.transforms[0], mode); - } } void GLMesh::render() diff --git a/src/graphics/mesh/glmesh.hpp b/src/graphics/data/glmesh.hpp similarity index 74% rename from src/graphics/mesh/glmesh.hpp rename to src/graphics/data/glmesh.hpp index 5769819..2f5f08d 100644 --- a/src/graphics/mesh/glmesh.hpp +++ b/src/graphics/data/glmesh.hpp @@ -8,7 +8,7 @@ #include -namespace Sim::Graphics +namespace Sim::Graphics::Data { struct GLMesh @@ -23,8 +23,9 @@ struct GLMesh GLMesh(const GLMesh& o) = delete; ~GLMesh(); - void bind(bool bind_ssbo = true); - void set(const Mesh& m, int mode, bool send_ssbo = true); + void bind(); + void bind_ssbo(); + void set(const Mesh& m, int mode); void render(int type); void render(); }; diff --git a/src/graphics/mesh/light.hpp b/src/graphics/data/light.hpp similarity index 91% rename from src/graphics/mesh/light.hpp rename to src/graphics/data/light.hpp index e61c0db..b6852f5 100644 --- a/src/graphics/mesh/light.hpp +++ b/src/graphics/data/light.hpp @@ -3,7 +3,7 @@ #include -namespace Sim::Graphics +namespace Sim::Graphics::Data { struct Light diff --git a/src/graphics/mesh/mesh.cpp b/src/graphics/data/mesh.cpp similarity index 92% rename from src/graphics/mesh/mesh.cpp rename to src/graphics/data/mesh.cpp index f532171..e503629 100644 --- a/src/graphics/mesh/mesh.cpp +++ b/src/graphics/data/mesh.cpp @@ -9,7 +9,7 @@ #include -using namespace Sim::Graphics; +using namespace Sim::Graphics::Data; Mesh::Mesh() { @@ -178,15 +178,28 @@ bool ray_intersects_triangle(vec3 ray_origin, bool Mesh::check_focus(double len) const { + if(!Focus::is_triggered()) + { + return false; + } + auto near = Focus::get_trigger_near(); auto far = Focus::get_trigger_far(); - return Focus::is_triggered() && check_intersect(near, glm::normalize(far - near) * len); + return check_intersect(near, glm::normalize(far - near) * len); } -bool Mesh::check_focus() const +bool Mesh::check_focus_hold(double len) { - return check_focus(2.5); + if(!Focus::is_triggered() && (!focus || Focus::is_triggered_release())) + { + return focus = false; + } + + auto near = Focus::get_trigger_near(); + auto far = Focus::get_trigger_far(); + + return focus = check_intersect(near, glm::normalize(far - near) * len); } bool Mesh::check_intersect(vec3 pos, vec3 path) const @@ -329,7 +342,7 @@ Mesh Mesh::to_lines() const return m; } -std::ostream& Sim::Graphics::operator<<(std::ostream& os, const Mesh& m) +std::ostream& Sim::Graphics::Data::operator<<(std::ostream& os, const Mesh& m) { os << "Mesh(\n"; os << " Vertices(\n"; diff --git a/src/graphics/mesh/mesh.hpp b/src/graphics/data/mesh.hpp similarity index 89% rename from src/graphics/mesh/mesh.hpp rename to src/graphics/data/mesh.hpp index 1cd7081..69ba390 100644 --- a/src/graphics/mesh/mesh.hpp +++ b/src/graphics/data/mesh.hpp @@ -12,7 +12,7 @@ #include "arrays.hpp" #include "light.hpp" -namespace Sim::Graphics +namespace Sim::Graphics::Data { struct Mesh @@ -20,6 +20,7 @@ struct Mesh std::vector vertices; std::vector indices; std::vector transforms; + bool focus = false; Mesh(); @@ -31,8 +32,8 @@ struct Mesh Mesh& add(const Mesh& o, glm::mat4 mat = glm::mat4(1), bool bake = false); Mesh to_lines() const; - bool check_focus() const; - bool check_focus(double len) const; + bool check_focus(double len = 2.5) const; + bool check_focus_hold(double len = 2.5); bool check_intersect(glm::vec<3, double> pos, glm::vec<3, double> path) const; glm::vec<3, double> calc_intersect(glm::vec<3, double> pos, glm::vec<3, double> path) const; diff --git a/src/graphics/mesh/meshgen.hpp b/src/graphics/data/meshgen.hpp similarity index 83% rename from src/graphics/mesh/meshgen.hpp rename to src/graphics/data/meshgen.hpp index bdf9ac5..9a3d018 100644 --- a/src/graphics/mesh/meshgen.hpp +++ b/src/graphics/data/meshgen.hpp @@ -1,9 +1,9 @@ #pragma once -#include "../mesh/model.hpp" +#include "model.hpp" -namespace Sim::Graphics +namespace Sim::Graphics::Data { class MeshGen diff --git a/src/graphics/mesh/model.cpp b/src/graphics/data/model.cpp similarity index 88% rename from src/graphics/mesh/model.cpp rename to src/graphics/data/model.cpp index aa4f237..ec00973 100644 --- a/src/graphics/mesh/model.cpp +++ b/src/graphics/data/model.cpp @@ -14,7 +14,7 @@ #include "model.hpp" #include "../../util/streams.hpp" -using namespace Sim::Graphics; +using namespace Sim::Graphics::Data; struct ProcState { @@ -176,10 +176,10 @@ static void proc_mesh(ProcState& state, aiMesh* mesh, const aiScene* scene) glm::mat4 convert_mat(aiMatrix4x4 m) { return { - m.a1, m.a2, m.a3, m.a4, - m.b1, m.b2, m.b3, m.b4, - m.c1, m.c2, m.c3, m.c4, - m.d1, m.d2, m.d3, m.d4 + m.a1, m.b1, m.c1, m.d1, + m.a2, m.b2, m.c2, m.d2, + m.a3, m.b3, m.c3, m.d3, + m.a4, m.b4, m.c4, m.d4 }; } @@ -213,7 +213,7 @@ static void proc_node(ProcState& state, glm::mat4 mat, aiNode* node, const aiSce if(node->mNumMeshes > 0) { - state.transforms.push_back(glm::transpose(mat)); + state.transforms.push_back(mat); } } @@ -258,7 +258,7 @@ glm::mat4 get_transforms(const aiNode* node) return mat; } -glm::mat4 Model::get_matrix(const char* name) const +glm::mat4 Model::load_matrix(const char* name) const { return get_transforms(scene->mRootNode->FindNode(name)); } @@ -280,18 +280,34 @@ Model::Model(std::string base, std::string filename) : base(base) for(int i = 0; i < scene->mNumLights; i++) { aiLight* light = scene->mLights[i]; - glm::mat4 mat = get_matrix(light->mName.C_Str()); + glm::mat4 mat = load_matrix(light->mName.C_Str()); auto [x, y, z] = light->mPosition; auto [r, g, b] = light->mColorDiffuse; - glm::vec4 pos = glm::vec4(x, y, z, 1) * mat; + glm::vec4 pos = mat * glm::vec4(x, y, z, 1); lights.push_back({ glm::vec3(pos), {r, g, b}, }); } + + for(int i = 0; i < scene->mNumCameras; i++) + { + aiCamera* camera = scene->mCameras[i]; + glm::mat4 mat = load_matrix(camera->mName.C_Str()); + + auto [x, y, z] = camera->mPosition; + auto [dx, dy, dz] = camera->mLookAt; + auto [ux, uy, uz] = camera->mUp; + + glm::vec4 pos = mat * glm::vec4(x, y, z, 1); + glm::vec3 look = glm::normalize(glm::mat3(mat) * glm::vec3(dx, dy, dz)); + glm::vec3 up = glm::normalize(glm::mat3(mat) * glm::vec3(ux, uy, uz)); + + cameras.push_back({.pos=glm::vec3(pos), .look=look, .up=up, .fov=camera->mHorizontalFOV}); + } } Mesh Model::load(const char* name, glm::mat4 mat) const diff --git a/src/graphics/mesh/model.hpp b/src/graphics/data/model.hpp similarity index 83% rename from src/graphics/mesh/model.hpp rename to src/graphics/data/model.hpp index 7c8323d..e849f75 100644 --- a/src/graphics/mesh/model.hpp +++ b/src/graphics/data/model.hpp @@ -3,6 +3,7 @@ #include "mesh.hpp" #include "light.hpp" +#include "camera.hpp" #include #include @@ -14,7 +15,7 @@ #include #include -namespace Sim::Graphics +namespace Sim::Graphics::Data { class Model @@ -26,6 +27,7 @@ class Model public: std::vector textures; + std::vector cameras; std::vector lights; Model(std::string base, std::string filename); @@ -35,7 +37,7 @@ public: Mesh load_root(glm::mat4 mat) const; Mesh load(const char* name) const; Mesh load(const char* name, glm::mat4 mat) const; - glm::mat4 get_matrix(const char* name) const; + glm::mat4 load_matrix(const char* name) const; }; }; diff --git a/src/graphics/mesh/texture.cpp b/src/graphics/data/texture.cpp similarity index 98% rename from src/graphics/mesh/texture.cpp rename to src/graphics/data/texture.cpp index 7871b68..0e6266c 100644 --- a/src/graphics/mesh/texture.cpp +++ b/src/graphics/data/texture.cpp @@ -8,7 +8,7 @@ #include "texture.hpp" -using namespace Sim::Graphics; +using namespace Sim::Graphics::Data; static std::unordered_map loaded; unsigned int Texture::handle_white; diff --git a/src/graphics/mesh/texture.hpp b/src/graphics/data/texture.hpp similarity index 87% rename from src/graphics/mesh/texture.hpp rename to src/graphics/data/texture.hpp index 686a917..223a135 100644 --- a/src/graphics/mesh/texture.hpp +++ b/src/graphics/data/texture.hpp @@ -3,7 +3,7 @@ #include -namespace Sim::Graphics::Texture +namespace Sim::Graphics::Data::Texture { extern unsigned int handle_white; diff --git a/src/graphics/equipment/generator.cpp b/src/graphics/equipment/generator.cpp index c847cac..b6dfdf6 100644 --- a/src/graphics/equipment/generator.cpp +++ b/src/graphics/equipment/generator.cpp @@ -5,6 +5,7 @@ #include using namespace Sim::Graphics::Equipment; +using namespace Sim::Graphics::Data; Generator::Generator(const Model& model) { @@ -19,7 +20,7 @@ void Generator::remesh_static(Mesh& rmesh) void Generator::get_static_transforms(std::vector& transforms) { Sim::System& sys = *Sim::System::active; - glm::mat4 rot = glm::rotate(glm::mat4(1), (float)sys.loop.generator.get_phase(), glm::vec3(0, 0, 1)); + glm::mat4 rot = glm::rotate(glm::mat4(1), (float)sys.loop.generator.get_phase() * 0.5f, glm::vec3(0, 0, 1)); transforms.push_back(rot); } diff --git a/src/graphics/equipment/generator.hpp b/src/graphics/equipment/generator.hpp index 28a8b8f..3dc735e 100644 --- a/src/graphics/equipment/generator.hpp +++ b/src/graphics/equipment/generator.hpp @@ -1,20 +1,20 @@ #pragma once -#include "../mesh/meshgen.hpp" +#include "../data/meshgen.hpp" namespace Sim::Graphics::Equipment { -class Generator : public MeshGen +class Generator : public Data::MeshGen { - Mesh g_rotor; + Data::Mesh g_rotor; public: - Generator(const Model& model); - virtual void get_static_transforms(std::vector& transforms); - virtual void remesh_static(Mesh& rmesh); + Generator(const Data::Model& model); + void get_static_transforms(std::vector& transforms) override; + void remesh_static(Data::Mesh& rmesh) override; }; }; diff --git a/src/graphics/equipment/pool.cpp b/src/graphics/equipment/pool.cpp index d97c255..d522cd1 100644 --- a/src/graphics/equipment/pool.cpp +++ b/src/graphics/equipment/pool.cpp @@ -5,6 +5,7 @@ #include using namespace Sim::Graphics::Equipment; +using namespace Sim::Graphics::Data; Pool::Pool(const Model& model) { diff --git a/src/graphics/equipment/pool.hpp b/src/graphics/equipment/pool.hpp index c918dd8..0f534ed 100644 --- a/src/graphics/equipment/pool.hpp +++ b/src/graphics/equipment/pool.hpp @@ -1,20 +1,20 @@ #pragma once -#include "../mesh/meshgen.hpp" +#include "../data/meshgen.hpp" namespace Sim::Graphics::Equipment { -class Pool : public MeshGen +class Pool : public Data::MeshGen { - Mesh g_pool; + Data::Mesh g_pool; public: - Pool(const Model& model); - virtual void get_static_transforms(std::vector& transforms); - virtual void remesh_static(Mesh& rmesh); + Pool(const Data::Model& model); + void get_static_transforms(std::vector& transforms) override; + void remesh_static(Data::Mesh& rmesh) override; }; }; diff --git a/src/graphics/equipment/reactor.cpp b/src/graphics/equipment/reactor.cpp index 8db143d..c040180 100644 --- a/src/graphics/equipment/reactor.cpp +++ b/src/graphics/equipment/reactor.cpp @@ -8,6 +8,7 @@ #include using namespace Sim::Graphics::Equipment; +using namespace Sim::Graphics::Data; Reactor::Reactor(const Model& model) { @@ -22,6 +23,8 @@ void Reactor::remesh_static(Mesh& rmesh) double t_sx = -(sys.reactor.width - 1) * t_step / 2.0; double t_sy = -(sys.reactor.height - 1) * t_step / 2.0; + glm::mat4 mat_scale = glm::scale(glm::mat4(1), glm::vec3(t_step / 0.4)); + for(int i = 0; i < sys.reactor.size; i++) { int x = i % sys.reactor.width; @@ -39,12 +42,21 @@ void Reactor::remesh_static(Mesh& rmesh) if(r->get_colour()[3] != 0) { Mesh m1, m2; - m1.add(g_control_rod_base, glm::translate(glm::mat4(1), glm::vec3(ox, oy, 0))); - m2.add(g_control_rod_lift, glm::translate(glm::mat4(1), glm::vec3(ox, oy, 0))); + glm::mat4 m = glm::translate(glm::mat4(1), glm::vec3(ox, oy, 0)) * mat_scale; + m1.add(g_control_rod_base, m); + m2.add(g_control_rod_lift, m); m1.bake_transforms(); m2.bake_transforms(); m2.set_blank_transform(); + for(int i = 0; i < m2.vertices.size(); i++) + { + if(g_control_rod_lift.vertices[i].pos.z == 0) + { + m2.vertices[i].transform_id = -1; + } + } + rmesh.add(m1); rmesh.add(m2); } diff --git a/src/graphics/equipment/reactor.hpp b/src/graphics/equipment/reactor.hpp index c76ddad..d6420b0 100644 --- a/src/graphics/equipment/reactor.hpp +++ b/src/graphics/equipment/reactor.hpp @@ -1,20 +1,20 @@ #pragma once -#include "../mesh/meshgen.hpp" +#include "../data/meshgen.hpp" namespace Sim::Graphics::Equipment { -class Reactor : public MeshGen +class Reactor : public Data::MeshGen { - Mesh g_control_rod_lift, g_control_rod_base; + Data::Mesh g_control_rod_lift, g_control_rod_base; public: - Reactor(const Model& model); - virtual void get_static_transforms(std::vector& transforms); - virtual void remesh_static(Mesh& rmesh); + Reactor(const Data::Model& model); + void get_static_transforms(std::vector& transforms) override; + void remesh_static(Data::Mesh& rmesh) override; }; }; diff --git a/src/graphics/input/focus.cpp b/src/graphics/input/focus.cpp index 627452d..31cc256 100644 --- a/src/graphics/input/focus.cpp +++ b/src/graphics/input/focus.cpp @@ -24,6 +24,7 @@ static std::unique_ptr state = nullptr; static bool mouse_visible = false; static bool mouse_locked = false; static bool triggered = false; +static bool triggered_release = false; void Focus::on_keypress(int key, int sc, int action, int mods) { @@ -53,8 +54,10 @@ void Focus::on_mouse_button(int button, int action, int mods) state->on_mouse_button(button, action, mods); } - if(button == GLFW_MOUSE_BUTTON_LEFT && action == GLFW_PRESS) + if(button == GLFW_MOUSE_BUTTON_LEFT && (action == GLFW_RELEASE || action == GLFW_PRESS)) { + bool t = false; + if(is_mouse_locked() && mouse_visible) { double mx, my; @@ -66,14 +69,21 @@ void Focus::on_mouse_button(int button, int action, int mods) trigger_near = glm::unProject(glm::vec3(mouse, -1), Camera::get_matrix(), Window::projection_matrix, viewport); trigger_far = glm::unProject(glm::vec3(mouse, 1), Camera::get_matrix(), Window::projection_matrix, viewport); - triggered = true; + t = true; } else if(!mouse_visible) { trigger_near = Camera::get_pos(); trigger_far = trigger_near + Camera::get_normal(); - triggered = true; + t = true; + } + + if(t) + { + t = (action == GLFW_PRESS); + triggered_release = !t; + triggered = t; } } } @@ -107,6 +117,7 @@ glm::vec<3, double> Focus::get_trigger_far() void Focus::update(double dt) { triggered = false; + triggered_release = false; bool c = is_mouse_locked(); @@ -194,3 +205,8 @@ bool Focus::is_triggered() return triggered; } +bool Focus::is_triggered_release() +{ + return triggered_release; +} + diff --git a/src/graphics/input/focus.hpp b/src/graphics/input/focus.hpp index a78f9aa..d016ab5 100644 --- a/src/graphics/input/focus.hpp +++ b/src/graphics/input/focus.hpp @@ -24,6 +24,7 @@ struct FocusType bool is_focused(); void clear_focus(); bool is_triggered(); +bool is_triggered_release(); bool is_mouse_locked(); void clear_mouse_locked(); glm::vec<3, double> get_trigger_near(); diff --git a/src/graphics/locations.cpp b/src/graphics/locations.cpp deleted file mode 100644 index ad9ebe6..0000000 --- a/src/graphics/locations.cpp +++ /dev/null @@ -1,47 +0,0 @@ - -#include "locations.hpp" -#include - -using namespace Sim::Graphics; - -const glm::mat4 Locations::monitors[7] = { - ( - glm::translate(glm::mat4(1), glm::vec3(-2.9475, -1.7778 + 0.05, 3 - 0.05)) * - glm::rotate(glm::mat4(1), glm::radians(-90), glm::vec3(1, 0, 0)) * - glm::rotate(glm::mat4(1), glm::radians(-90), glm::vec3(0, 1, 0)) * - glm::scale(glm::mat4(1), glm::vec3(1.9, 1.9, 1.9)) - ), - ( - glm::translate(glm::mat4(1), glm::vec3(-1.5 + 0.05, 3.9475, 3 - 0.05)) * - glm::rotate(glm::mat4(1), glm::radians(-90), glm::vec3(1, 0, 0)) * - glm::scale(glm::mat4(1), glm::vec3(1.9, 1.9, 1.9)) - ), - ( - glm::translate(glm::mat4(1), glm::vec3(1 + 0.05, 3.9475, 3 - 0.05)) * - glm::rotate(glm::mat4(1), glm::radians(-90), glm::vec3(1, 0, 0)) * - glm::scale(glm::mat4(1), glm::vec3(1.9, 1.9, 1.9)) - ), - ( - glm::translate(glm::mat4(1), glm::vec3(3.5 + 0.05, 3.9475, 3 - 0.05)) * - glm::rotate(glm::mat4(1), glm::radians(-90), glm::vec3(1, 0, 0)) * - glm::scale(glm::mat4(1), glm::vec3(1.9, 1.9, 1.9)) - ), - ( - glm::translate(glm::mat4(1), glm::vec3(6 + 0.05, 3.9475, 3 - 0.05)) * - glm::rotate(glm::mat4(1), glm::radians(-90), glm::vec3(1, 0, 0)) * - glm::scale(glm::mat4(1), glm::vec3(1.9, 1.9, 1.9)) - ), - ( - glm::translate(glm::mat4(1), glm::vec3(8.9475, 7.0/3.0 - 0.05, 3 - 0.05)) * - glm::rotate(glm::mat4(1), glm::radians(-90), glm::vec3(1, 0, 0)) * - glm::rotate(glm::mat4(1), glm::radians(90), glm::vec3(0, 1, 0)) * - glm::scale(glm::mat4(1), glm::vec3(1.9, 1.9, 1.9)) - ), - ( - glm::translate(glm::mat4(1), glm::vec3(8.9475, -1.0/3.0 - 0.05, 3 - 0.05)) * - glm::rotate(glm::mat4(1), glm::radians(-90), glm::vec3(1, 0, 0)) * - glm::rotate(glm::mat4(1), glm::radians(90), glm::vec3(0, 1, 0)) * - glm::scale(glm::mat4(1), glm::vec3(1.9, 1.9, 1.9)) - ) -}; - diff --git a/src/graphics/locations.hpp b/src/graphics/locations.hpp deleted file mode 100644 index 471a29f..0000000 --- a/src/graphics/locations.hpp +++ /dev/null @@ -1,12 +0,0 @@ - -#pragma once - -#include - -namespace Sim::Graphics::Locations -{ - -extern const glm::mat4 monitors[7]; - -}; - diff --git a/src/graphics/monitor/cctv.cpp b/src/graphics/monitor/cctv.cpp new file mode 100644 index 0000000..0884820 --- /dev/null +++ b/src/graphics/monitor/cctv.cpp @@ -0,0 +1,270 @@ + +#include +#include + +#include +#include + +#include + +#include "cctv.hpp" +#include "../shader.hpp" +#include "../window.hpp" +#include "../camera.hpp" +#include "../input/focus.hpp" +#include "../../system.hpp" +#include "../../util/math.hpp" +#include "../../util/streams.hpp" + +#define HEIGHT 512 + +using namespace Sim::Graphics::Monitor; +using namespace Sim::Graphics::Data; +using namespace Sim::Graphics; +using namespace Sim; + +class FocusCCTV : public Focus::FocusType +{ + CCTV* parent; + + int zoom = 0; + int rot_pitch = 0; + int rot_yaw = 0; + +public: + + FocusCCTV(CCTV* parent) + { + this->parent = parent; + } + + virtual void update(double dt) + { + if(rot_pitch || rot_yaw) + { + parent->rotate(dt, rot_pitch, -rot_yaw); + } + + if(zoom) + { + Data::Camera& active = parent->cameras[parent->camera_at]; + active.zoom = Util::Math::clamp(1.f / (1.f / active.zoom - zoom * dt * 0.5f), 1.f, 4.f); + } + } + + virtual void on_keypress(int key, int sc, int action, int mods) + { + if(action == GLFW_PRESS) + { + switch(key) + { + case GLFW_KEY_KP_1: + parent->camera_at = (parent->camera_at + parent->cameras.size() - 1) % parent->cameras.size(); + break; + case GLFW_KEY_KP_2: + rot_pitch -= 1; + break; + case GLFW_KEY_KP_3: + parent->camera_at = (parent->camera_at + 1) % parent->cameras.size(); + break; + case GLFW_KEY_KP_4: + rot_yaw += 1; + break; + case GLFW_KEY_KP_5: + parent->powered = !parent->powered; + break; + case GLFW_KEY_KP_6: + rot_yaw -= 1; + break; + case GLFW_KEY_KP_7: + zoom += 1; + break; + case GLFW_KEY_KP_8: + rot_pitch += 1; + break; + case GLFW_KEY_KP_9: + zoom -= 1; + break; + } + } + + else if(action == GLFW_RELEASE) + { + switch(key) + { + case GLFW_KEY_KP_2: + rot_pitch += 1; + break; + case GLFW_KEY_KP_4: + rot_yaw -= 1; + break; + case GLFW_KEY_KP_6: + rot_yaw += 1; + break; + case GLFW_KEY_KP_7: + zoom -= 1; + break; + case GLFW_KEY_KP_8: + rot_pitch -= 1; + break; + case GLFW_KEY_KP_9: + zoom += 1; + break; + } + } + } +}; + +CCTV::CCTV(Model& model) + : height(HEIGHT) + , width(HEIGHT * 16 / 9) + , cameras(model.cameras) +{ + m_buttons[0] = model.load("click_cctv_numpad_1"); + m_buttons[1] = model.load("click_cctv_numpad_2"); + m_buttons[2] = model.load("click_cctv_numpad_3"); + m_buttons[3] = model.load("click_cctv_numpad_4"); + m_buttons[4] = model.load("click_cctv_numpad_5"); + m_buttons[5] = model.load("click_cctv_numpad_6"); + m_buttons[6] = model.load("click_cctv_numpad_7"); + m_buttons[7] = model.load("click_cctv_numpad_8"); + m_buttons[8] = model.load("click_cctv_numpad_9"); + + glGenFramebuffers(1, &fbo); + glGenTextures(1, &texture); + glGenRenderbuffers(1, &rbo_depth); + + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glBindRenderbuffer(GL_RENDERBUFFER, rbo_depth); + glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT, width, height); + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbo_depth); + glBindTexture(GL_TEXTURE_2D, texture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, nullptr); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0); + + handle = glGetTextureHandleARB(texture); + glMakeTextureHandleResidentARB(handle); + + m_screen.vertices = { + {.texid=handle, .texpos={0, 1}, .pos={0, 0, 0}, .normal={0, 0, 1}, .material={0, 0, 1}, .transform_id=0}, + {.texid=handle, .texpos={0, 0}, .pos={0, 1, 0}, .normal={0, 0, 1}, .material={0, 0, 1}, .transform_id=0}, + {.texid=handle, .texpos={1, 1}, .pos={1, 0, 0}, .normal={0, 0, 1}, .material={0, 0, 1}, .transform_id=0}, + {.texid=handle, .texpos={1, 0}, .pos={1, 1, 0}, .normal={0, 0, 1}, .material={0, 0, 1}, .transform_id=0}, + }; + m_screen.indices = {0, 1, 3, 0, 3, 2}; + m_screen.transforms = {model.load_matrix("translation_monitor_1")}; + m_screen.bake_transforms(); + + gm_screen.bind(); + gm_screen.set(m_screen, GL_STATIC_DRAW); +} + +CCTV::~CCTV() +{ + if(fbo) glDeleteFramebuffers(1, &fbo); + if(texture) glDeleteTextures(1, &texture); + if(rbo_depth) glDeleteRenderbuffers(1, &rbo_depth); +} + +CCTV::CCTV(CCTV&& o) + : width(o.width) + , height(o.height) + , cameras(std::move(o.cameras)) + , gm_screen(std::move(o.gm_screen)) + , m_screen(std::move(o.m_screen)) + , m_buttons(std::move(o.m_buttons)) + , powered(o.powered) +{ + fbo = o.fbo; + texture = o.texture; + rbo_depth = o.rbo_depth; + handle = o.handle; + + o.fbo = 0; + o.texture = 0; + o.rbo_depth = 0; + o.handle = 0; +} + +void CCTV::rotate(double dt, float pitch, float yaw) +{ + Data::Camera& active = cameras[camera_at]; + float m = float(M_PI) * dt * 0.5f / active.zoom; + + active.pitch = Util::Math::clamp(active.pitch + pitch * m, -M_PI / 4, M_PI / 4); + active.yaw = Util::Math::clamp(active.yaw + yaw * m, -M_PI / 4, M_PI / 4); +} + +void CCTV::update(double dt) +{ + Data::Camera& active = cameras[camera_at]; + + if(m_screen.check_focus()) + Focus::set(std::make_unique(this)); + if(m_buttons[0].check_focus_hold()) + active.zoom = Util::Math::clamp(1.f / (1.f / active.zoom - dt * 0.5f), 1.f, 4.f); + if(m_buttons[1].check_focus_hold()) + rotate(dt, 1, 0); + if(m_buttons[2].check_focus_hold()) + active.zoom = Util::Math::clamp(1.f / (1.f / active.zoom + dt * 0.5f), 1.f, 4.f); + if(m_buttons[3].check_focus_hold()) + rotate(dt, 0, -1); + if(m_buttons[4].check_focus()) + powered = !powered; + if(m_buttons[5].check_focus_hold()) + rotate(dt, 0, 1); + if(m_buttons[6].check_focus()) + camera_at = (camera_at + cameras.size() - 1) % cameras.size(); + if(m_buttons[7].check_focus_hold()) + rotate(dt, -1, 0); + if(m_buttons[8].check_focus()) + camera_at = (camera_at + 1) % cameras.size(); +} + +void CCTV::render_view() +{ + if(!powered) + return; + + Data::Camera& active = cameras[camera_at]; + + glm::mat4 rot = glm::mat4(1); + glm::vec3 right = glm::normalize(glm::cross(active.look, active.up)); + rot = glm::rotate(rot, -active.yaw, active.up); + rot = glm::rotate(rot, active.pitch, right); + + glm::mat4 view = glm::lookAt(active.pos, active.pos + glm::mat3(rot) * active.look, active.up); + glm::mat4 proj = glm::perspective(active.fov / active.zoom, (float)width / height, 0.1f, 100.0f); + glm::vec3 brightness = glm::vec3(System::active->grid.get_light_intensity()); + + glBindFramebuffer(GL_FRAMEBUFFER, fbo); + glViewport(0, 0, width, height); + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); + glFrontFace(GL_CCW); + + glUniformMatrix4fv(Shader::MAIN["projection"], 1, false, &proj[0][0]); + glUniformMatrix4fv(Shader::MAIN["camera"], 1, false, &view[0][0]); + glUniform3fv(Shader::MAIN["camera_pos"], 1, &active.pos[0]); + + Window::render_scene(); + Window::render_player(); +} + +void CCTV::render_screen() +{ + if(!powered) + return; + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glBlendFunc(GL_SRC_COLOR, GL_ONE); + + gm_screen.bind(); + gm_screen.render(); + + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); +} + diff --git a/src/graphics/monitor/cctv.hpp b/src/graphics/monitor/cctv.hpp new file mode 100644 index 0000000..c39bde4 --- /dev/null +++ b/src/graphics/monitor/cctv.hpp @@ -0,0 +1,47 @@ + +#pragma once + +#include +#include + +#include "../data/meshgen.hpp" +#include "../data/camera.hpp" +#include "../data/glmesh.hpp" + +namespace Sim::Graphics::Monitor +{ + +class CCTV : public Data::MeshGen +{ + Data::GLMesh gm_screen; + Data::Mesh m_screen; + std::array m_buttons; + + unsigned int fbo; + unsigned int texture; + unsigned int rbo_depth; + unsigned int handle; + + const int width; + const int height; + + +public: + + std::vector cameras; + int camera_at = 0; + bool powered = false; + + CCTV(Data::Model& model); + CCTV(const CCTV&) = delete; + CCTV(CCTV&&); + ~CCTV(); + + void update(double dt) override; + void rotate(double dt, float pitch, float yaw); + void render_view(); + void render_screen(); +}; + +}; + diff --git a/src/graphics/monitor/core.cpp b/src/graphics/monitor/core.cpp index 9431f4e..e4de59f 100644 --- a/src/graphics/monitor/core.cpp +++ b/src/graphics/monitor/core.cpp @@ -3,10 +3,9 @@ #include #include "core.hpp" -#include "../locations.hpp" #include "../input/focus.hpp" -#include "../mesh/arrays.hpp" -#include "../mesh/texture.hpp" +#include "../data/arrays.hpp" +#include "../data/texture.hpp" #include "../../system.hpp" #include "../../util/streams.hpp" @@ -18,6 +17,7 @@ using namespace Sim; using namespace Sim::Graphics; using namespace Sim::Graphics::Monitor; +using namespace Sim::Graphics::Data; using namespace Sim::Util::Streams; static void set_all(bool state) @@ -118,16 +118,16 @@ struct CoreJoystick : public Focus::FocusType Core::Core(const Model& model) { - mat = Locations::monitors[2]; - m_buttons[0] = model.load("click_numpad_1"); - m_buttons[1] = model.load("click_numpad_2"); - m_buttons[2] = model.load("click_numpad_3"); - m_buttons[3] = model.load("click_numpad_4"); - m_buttons[4] = model.load("click_numpad_5"); - m_buttons[5] = model.load("click_numpad_6"); - m_buttons[6] = model.load("click_numpad_7"); - m_buttons[7] = model.load("click_numpad_8"); - m_buttons[8] = model.load("click_numpad_9"); + mat = model.load_matrix("translation_monitor_3"); + m_buttons[0] = model.load("click_reactor_numpad_1"); + m_buttons[1] = model.load("click_reactor_numpad_2"); + m_buttons[2] = model.load("click_reactor_numpad_3"); + m_buttons[3] = model.load("click_reactor_numpad_4"); + m_buttons[4] = model.load("click_reactor_numpad_5"); + m_buttons[5] = model.load("click_reactor_numpad_6"); + m_buttons[6] = model.load("click_reactor_numpad_7"); + m_buttons[7] = model.load("click_reactor_numpad_8"); + m_buttons[8] = model.load("click_reactor_numpad_9"); m_joystick = model.load("click_reactor_joystick"); m_monitor = model.load("translation_monitor_3"); m_scram = model.load("click_scram"); @@ -135,14 +135,14 @@ Core::Core(const Model& model) void Core::remesh_static(Mesh& rmesh) { - Mesh mesh; + Data::Mesh mesh; mesh.load_text("Reactor Core", 0.04); rmesh.add(mesh, mat, true); } -static Mesh add_dot(glm::mat4 model_mat, glm::vec4 colour) +static Data::Mesh add_dot(glm::mat4 model_mat, glm::vec4 colour) { - Mesh mesh; + Data::Mesh mesh; mesh.indices = {0, 1, 3, 0, 3, 2}; mesh.vertices = { @@ -186,7 +186,7 @@ void Core::update(double dt) void Core::remesh_slow(Mesh& rmesh) { Sim::System& sys = *System::active; - Sim::Graphics::Mesh mesh; + Sim::Graphics::Data::Mesh mesh; double step = sys.reactor.cell_width / sys.vessel.diameter * 0.8; double sx = 0.5 - (sys.reactor.width - 1) * step / 2.0; diff --git a/src/graphics/monitor/core.hpp b/src/graphics/monitor/core.hpp index 308e7b6..18b6d57 100644 --- a/src/graphics/monitor/core.hpp +++ b/src/graphics/monitor/core.hpp @@ -1,27 +1,27 @@ #pragma once -#include "../mesh/model.hpp" -#include "../mesh/meshgen.hpp" +#include "../data/model.hpp" +#include "../data/meshgen.hpp" namespace Sim::Graphics::Monitor { -class Core : public MeshGen +class Core : public Data::MeshGen { glm::mat4 mat; - Mesh m_monitor; - Mesh m_buttons[9]; - Mesh m_joystick; - Mesh m_scram; + Data::Mesh m_monitor; + Data::Mesh m_buttons[9]; + Data::Mesh m_joystick; + Data::Mesh m_scram; public: - Core(const Model& model); - virtual void update(double dt); - virtual void remesh_static(Mesh& rmesh); - virtual void remesh_slow(Mesh& rmesh); + Core(const Data::Model& model); + void update(double dt) override; + void remesh_static(Data::Mesh& rmesh) override; + void remesh_slow(Data::Mesh& rmesh) override; }; }; diff --git a/src/graphics/monitor/primary_loop.cpp b/src/graphics/monitor/primary_loop.cpp index 82c9d27..a3f820c 100644 --- a/src/graphics/monitor/primary_loop.cpp +++ b/src/graphics/monitor/primary_loop.cpp @@ -3,7 +3,6 @@ #include #include "primary_loop.hpp" -#include "../locations.hpp" #include "../../system.hpp" #include "../../coolant/valve.hpp" #include "../input/focus.hpp" @@ -14,6 +13,7 @@ using namespace Sim::Graphics; using namespace Sim::Graphics::Monitor; +using namespace Sim::Graphics::Data; using namespace Sim::Util::Streams; struct ValveJoystick : public Focus::FocusType @@ -51,7 +51,7 @@ struct ValveJoystick : public Focus::FocusType PrimaryLoop::PrimaryLoop(const Model& model) { - mat = Locations::monitors[3]; + mat = model.load_matrix("translation_monitor_4"); g_switch_pump = model.load("visual_pump_switch_1"); g_switch_bypass = model.load("visual_bypass_switch"); @@ -67,7 +67,7 @@ PrimaryLoop::PrimaryLoop(const Model& model) void PrimaryLoop::remesh_static(Mesh& rmesh) { std::stringstream ss; - Mesh mesh; + Data::Mesh mesh; ss << "Turbine Bypass Valve\n\n"; ss << "Opened\nFlow\nSetpoint\n\n"; @@ -108,7 +108,7 @@ void PrimaryLoop::update(double dt) void PrimaryLoop::remesh_slow(Mesh& rmesh) { std::stringstream ss; - Sim::Graphics::Mesh mesh; + Sim::Graphics::Data::Mesh mesh; System& sys = *System::active; ss << "\n\n"; diff --git a/src/graphics/monitor/primary_loop.hpp b/src/graphics/monitor/primary_loop.hpp index 382b9e2..493acd6 100644 --- a/src/graphics/monitor/primary_loop.hpp +++ b/src/graphics/monitor/primary_loop.hpp @@ -1,34 +1,34 @@ #pragma once -#include "../mesh/model.hpp" -#include "../mesh/meshgen.hpp" +#include "../data/model.hpp" +#include "../data/meshgen.hpp" namespace Sim::Graphics::Monitor { -class PrimaryLoop : public MeshGen +class PrimaryLoop : public Data::MeshGen { glm::mat4 mat; - Mesh m_joystick_turbine_bypass; - Mesh m_joystick_turbine_inlet; + Data::Mesh m_joystick_turbine_bypass; + Data::Mesh m_joystick_turbine_inlet; - Mesh g_switch_pump; - Mesh g_switch_bypass; - Mesh g_switch_inlet; + Data::Mesh g_switch_pump; + Data::Mesh g_switch_bypass; + Data::Mesh g_switch_inlet; - Mesh m_switch_pump; - Mesh m_switch_bypass; - Mesh m_switch_inlet; + Data::Mesh m_switch_pump; + Data::Mesh m_switch_bypass; + Data::Mesh m_switch_inlet; public: - PrimaryLoop(const Model& model); - virtual void update(double dt); - virtual void get_static_transforms(std::vector& transforms); - virtual void remesh_static(Mesh& rmesh); - virtual void remesh_slow(Mesh& rmesh); + PrimaryLoop(const Data::Model& model); + void update(double dt) override; + void get_static_transforms(std::vector& transforms) override; + void remesh_static(Data::Mesh& rmesh) override; + void remesh_slow(Data::Mesh& rmesh) override; }; }; diff --git a/src/graphics/monitor/secondary_loop.cpp b/src/graphics/monitor/secondary_loop.cpp index 1fda1db..230f916 100644 --- a/src/graphics/monitor/secondary_loop.cpp +++ b/src/graphics/monitor/secondary_loop.cpp @@ -3,7 +3,6 @@ #include #include "secondary_loop.hpp" -#include "../locations.hpp" #include "../../system.hpp" #include "../../coolant/valve.hpp" #include "../input/focus.hpp" @@ -14,12 +13,12 @@ using namespace Sim::Graphics; using namespace Sim::Graphics::Monitor; +using namespace Sim::Graphics::Data; using namespace Sim::Util::Streams; - SecondaryLoop::SecondaryLoop(const Model& model) { - mat = Locations::monitors[5]; + mat = model.load_matrix("translation_monitor_6"); g_switch_2 = model.load("visual_pump_switch_2"); g_switch_3 = model.load("visual_pump_switch_3"); @@ -43,7 +42,7 @@ void SecondaryLoop::update(double dt) void SecondaryLoop::remesh_static(Mesh& rmesh) { std::stringstream ss; - Mesh mesh; + Data::Mesh mesh; ss << "Cooling Tower\n\n"; ss << "Heat\nSteam\nPressure\nLevel\n\n"; @@ -61,7 +60,7 @@ void SecondaryLoop::remesh_static(Mesh& rmesh) void SecondaryLoop::remesh_slow(Mesh& rmesh) { std::stringstream ss; - Sim::Graphics::Mesh mesh; + Sim::Graphics::Data::Mesh mesh; System& sys = *System::active; ss << "\n\n"; diff --git a/src/graphics/monitor/secondary_loop.hpp b/src/graphics/monitor/secondary_loop.hpp index 9048036..28c035d 100644 --- a/src/graphics/monitor/secondary_loop.hpp +++ b/src/graphics/monitor/secondary_loop.hpp @@ -1,31 +1,31 @@ #pragma once -#include "../mesh/model.hpp" -#include "../mesh/meshgen.hpp" +#include "../data/model.hpp" +#include "../data/meshgen.hpp" namespace Sim::Graphics::Monitor { -class SecondaryLoop : public MeshGen +class SecondaryLoop : public Data::MeshGen { glm::mat4 mat; - Mesh g_switch_2; - Mesh g_switch_3; - - Mesh m_joystick_turbine_bypass; - Mesh m_joystick_turbine_inlet; - Mesh m_switch_2; - Mesh m_switch_3; + Data::Mesh g_switch_2; + Data::Mesh g_switch_3; + + Data::Mesh m_joystick_turbine_bypass; + Data::Mesh m_joystick_turbine_inlet; + Data::Mesh m_switch_2; + Data::Mesh m_switch_3; public: - SecondaryLoop(const Model& model); - virtual void update(double dt); - virtual void get_static_transforms(std::vector& transforms); - virtual void remesh_static(Mesh& rmesh); - virtual void remesh_slow(Mesh& rmesh); + SecondaryLoop(const Data::Model& model); + void update(double dt) override; + void get_static_transforms(std::vector& transforms) override; + void remesh_static(Data::Mesh& rmesh) override; + void remesh_slow(Data::Mesh& rmesh) override; }; }; diff --git a/src/graphics/monitor/turbine.cpp b/src/graphics/monitor/turbine.cpp index dd307b0..e87f465 100644 --- a/src/graphics/monitor/turbine.cpp +++ b/src/graphics/monitor/turbine.cpp @@ -3,7 +3,6 @@ #include #include "turbine.hpp" -#include "../locations.hpp" #include "../../system.hpp" #include "../../coolant/valve.hpp" #include "../input/focus.hpp" @@ -15,11 +14,12 @@ using namespace Sim::Graphics; using namespace Sim::Graphics::Monitor; +using namespace Sim::Graphics::Data; using namespace Sim::Util::Streams; Turbine::Turbine(const Model& model) { - mat = Locations::monitors[4]; + mat = model.load_matrix("translation_monitor_5"); g_dial_phase = model.load("visual_dial_phase"); g_dial_voltage = model.load("visual_dial_voltage"); @@ -69,7 +69,7 @@ void Turbine::get_static_transforms(std::vector& transforms) void Turbine::remesh_static(Mesh& rmesh) { std::stringstream ss; - Sim::Graphics::Mesh mesh; + Sim::Graphics::Data::Mesh mesh; ss << "Turbine\n\n"; ss << "Heat\nPressure\nSpeed\n\n"; @@ -87,7 +87,7 @@ void Turbine::remesh_static(Mesh& rmesh) void Turbine::remesh_slow(Mesh& rmesh) { std::stringstream ss; - Sim::Graphics::Mesh mesh; + Sim::Graphics::Data::Mesh mesh; System& sys = *System::active; ss << "\n\n"; diff --git a/src/graphics/monitor/turbine.hpp b/src/graphics/monitor/turbine.hpp index 5a7a21a..73ab166 100644 --- a/src/graphics/monitor/turbine.hpp +++ b/src/graphics/monitor/turbine.hpp @@ -1,31 +1,31 @@ #pragma once -#include "../mesh/model.hpp" -#include "../mesh/glmesh.hpp" -#include "../mesh/meshgen.hpp" +#include "../data/model.hpp" +#include "../data/glmesh.hpp" +#include "../data/meshgen.hpp" namespace Sim::Graphics::Monitor { -class Turbine : public MeshGen +class Turbine : public Data::MeshGen { glm::mat4 mat; - Mesh g_dial_phase; - Mesh g_dial_voltage; - Mesh g_dial_power; - Mesh g_dial_frequency; - Mesh g_switch_breaker; - Mesh m_switch_breaker; + Data::Mesh g_dial_phase; + Data::Mesh g_dial_voltage; + Data::Mesh g_dial_power; + Data::Mesh g_dial_frequency; + Data::Mesh g_switch_breaker; + Data::Mesh m_switch_breaker; public: - Turbine(const Model& model); - virtual void update(double dt); - virtual void get_static_transforms(std::vector& transforms); - virtual void remesh_static(Mesh& rmesh); - virtual void remesh_slow(Mesh& rmesh); + Turbine(const Data::Model& model); + void update(double dt) override; + void get_static_transforms(std::vector& transforms) override; + void remesh_static(Data::Mesh& rmesh) override; + void remesh_slow(Data::Mesh& rmesh) override; }; }; diff --git a/src/graphics/monitor/vessel.cpp b/src/graphics/monitor/vessel.cpp index fa5ce9c..f288d70 100644 --- a/src/graphics/monitor/vessel.cpp +++ b/src/graphics/monitor/vessel.cpp @@ -5,7 +5,6 @@ #include "vessel.hpp" #include "../../reactor/rod.hpp" #include "../../reactor/control/boron_rod.hpp" -#include "../locations.hpp" #include "../../system.hpp" #include "../../util/streams.hpp" @@ -13,17 +12,18 @@ #include using namespace Sim::Graphics::Monitor; +using namespace Sim::Graphics::Data; using namespace Sim::Util::Streams; Vessel::Vessel(const Model& model) { - mat = Locations::monitors[1]; + mat = model.load_matrix("translation_monitor_2"); } void Vessel::remesh_static(Mesh& rmesh) { std::stringstream ss; - Sim::Graphics::Mesh mesh; + Sim::Graphics::Data::Mesh mesh; ss << "Reactor Vessel\n\n"; ss << "Heat\n"; @@ -44,7 +44,7 @@ void Vessel::remesh_static(Mesh& rmesh) void Vessel::remesh_slow(Mesh& rmesh) { std::stringstream ss; - Sim::Graphics::Mesh mesh; + Sim::Graphics::Data::Mesh mesh; Sim::System& sys = *System::active; double temp_min, temp_max; diff --git a/src/graphics/monitor/vessel.hpp b/src/graphics/monitor/vessel.hpp index b986a32..4991a1b 100644 --- a/src/graphics/monitor/vessel.hpp +++ b/src/graphics/monitor/vessel.hpp @@ -1,21 +1,21 @@ #pragma once -#include "../mesh/model.hpp" -#include "../mesh/meshgen.hpp" +#include "../data/model.hpp" +#include "../data/meshgen.hpp" namespace Sim::Graphics::Monitor { -class Vessel : public MeshGen +class Vessel : public Data::MeshGen { glm::mat4 mat; public: - Vessel(const Model& model); - virtual void remesh_static(Mesh& rmesh); - virtual void remesh_slow(Mesh& rmesh); + Vessel(const Data::Model& model); + void remesh_static(Data::Mesh& rmesh) override; + void remesh_slow(Data::Mesh& rmesh) override; }; }; diff --git a/src/graphics/resize.cpp b/src/graphics/resize.cpp index 888a794..db2ade3 100644 --- a/src/graphics/resize.cpp +++ b/src/graphics/resize.cpp @@ -7,6 +7,7 @@ using namespace Sim::Graphics; using namespace Sim::Graphics::Resize; +using namespace Sim::Graphics::Window; static bool is_fullscreen = false; diff --git a/src/graphics/shader.cpp b/src/graphics/shader.cpp index c79a7b8..cd29c22 100644 --- a/src/graphics/shader.cpp +++ b/src/graphics/shader.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include "shader.hpp" #include "window.hpp" @@ -12,26 +13,20 @@ using namespace Sim::Graphics; Shader Shader::MAIN; -Shader Shader::BLUR; Shader Shader::LIGHT; Shader* Shader::ACTIVE; -static int load_shader(const char* src, int type) -{ - int id = glCreateShader(type); - - glShaderSource(id, 1, &src, nullptr); - glCompileShader(id); - - return id; -} - static std::string read_shader(const char* path) { std::stringstream ss; std::ifstream file(path, std::ios::binary); char buff[1024]; + if(!file.is_open()) + { + throw std::runtime_error(std::format("Shader Read Error: {0}", path)); + } + while(!file.eof()) { file.read(buff, 1024); @@ -47,6 +42,37 @@ static std::string read_shader(const char* base, const char* file) return read_shader(path.c_str()); } +Shader::Source::Source(const char* path, GLenum type) +{ + int success; + std::string src = read_shader(path); + const char* c_src = src.c_str(); + + id = glCreateShader(type); + glShaderSource(id, 1, &c_src, nullptr); + glCompileShader(id); + glGetShaderiv(id, GL_COMPILE_STATUS, &success); + + if(!success) + { + char infoLog[512]; + glGetShaderInfoLog(id, 512, NULL, infoLog); + std::string entry = std::format("Shader Compile Error ({0}): {1}", path, infoLog); + throw std::runtime_error(entry); + } +} + +Shader::Source::Source(Source&& o) +{ + id = o.id; + o.id = 0; +} + +Shader::Source::~Source() +{ + if(id) glDeleteShader(id); +} + Shader::Shader() { @@ -66,30 +92,15 @@ Shader::~Shader() } } -void Shader::load(const char* path, const char* file_vsh, const char* file_fsh) +void Shader::load(const Source* sources, int count) { - load(path, file_vsh, nullptr, file_fsh); -} - -void Shader::load(const char* path, const char* file_vsh, const char* file_gsh, const char* file_fsh) -{ - std::string shader_vsh = file_vsh ? read_shader(path, file_vsh) : ""; - std::string shader_gsh = file_gsh ? read_shader(path, file_gsh) : ""; - std::string shader_fsh = file_fsh ? read_shader(path, file_fsh) : ""; - int success; - int vsh_id = file_vsh ? load_shader(shader_vsh.c_str(), GL_VERTEX_SHADER) : 0; - int gsh_id = file_gsh ? load_shader(shader_gsh.c_str(), GL_GEOMETRY_SHADER) : 0; - int fsh_id = file_fsh ? load_shader(shader_fsh.c_str(), GL_FRAGMENT_SHADER) : 0; - prog_id = glCreateProgram(); - if(file_vsh) - glAttachShader(prog_id, vsh_id); - if(file_gsh) - glAttachShader(prog_id, gsh_id); - if(file_fsh) - glAttachShader(prog_id, fsh_id); + for(int i = 0; i < count; i++) + { + glAttachShader(prog_id, sources[i].id); + } glLinkProgram(prog_id); glGetProgramiv(prog_id, GL_LINK_STATUS, &success); @@ -98,21 +109,9 @@ void Shader::load(const char* path, const char* file_vsh, const char* file_gsh, { char infoLog[512]; glGetProgramInfoLog(prog_id, 512, NULL, infoLog); - std::cout << "Shader Link Error (" << path << "," << file_vsh << "," << file_fsh << "): " << infoLog << std::endl; - Window::close(); - return; + std::string entry = std::format("Shader Link Error: {0}", infoLog); + throw std::runtime_error(entry); } - - glUseProgram(prog_id); - - if(file_vsh) - glDeleteShader(vsh_id); - if(file_gsh) - glDeleteShader(gsh_id); - if(file_fsh) - glDeleteShader(fsh_id); - - ACTIVE = this; } void Shader::use() diff --git a/src/graphics/shader.hpp b/src/graphics/shader.hpp index 06bd72b..2846a92 100644 --- a/src/graphics/shader.hpp +++ b/src/graphics/shader.hpp @@ -1,11 +1,14 @@ #pragma once +#include + #include namespace Sim::Graphics { + class Shader { unsigned int prog_id = 0; @@ -14,9 +17,18 @@ class Shader std::unordered_map uniform_block_indices; public: + + struct Source + { + unsigned int id; + + Source(const char* path, GLenum type); + Source(const Source& o) = delete; + Source(Source&& o); + ~Source(); + }; static Shader MAIN; - static Shader BLUR; static Shader LIGHT; static Shader* ACTIVE; @@ -26,8 +38,7 @@ public: Shader(Shader&& o); ~Shader(); - void load(const char* path, const char* file_vsh, const char* file_gsh, const char* file_fsh); - void load(const char* path, const char* file_vsh, const char* file_fsh); + void load(const Source* sources, int count); void block_binding(const char* name, unsigned int index); void use(); diff --git a/src/graphics/ui.cpp b/src/graphics/ui.cpp index 06959ca..66f80d2 100644 --- a/src/graphics/ui.cpp +++ b/src/graphics/ui.cpp @@ -6,11 +6,11 @@ #include -#include "mesh/mesh.hpp" -#include "mesh/glmesh.hpp" -#include "mesh/arrays.hpp" -#include "mesh/font.hpp" -#include "mesh/texture.hpp" +#include "data/mesh.hpp" +#include "data/glmesh.hpp" +#include "data/arrays.hpp" +#include "data/font.hpp" +#include "data/texture.hpp" #include "resize.hpp" #include "shader.hpp" @@ -18,18 +18,17 @@ using namespace Sim::Graphics; -static GLMesh gm_ui; -static GLMesh gm_dynamic_slow[2]; - +static Data::GLMesh gm_ui; +static Data::GLMesh gm_dynamic_slow[2]; static Widget::Clock w_clock; static int gm_dynamic_slow_at = 0; void UI::init() { - Mesh m; + Data::Mesh m; - unsigned int handle = Texture::handle_white; + unsigned int handle = Data::Texture::handle_white; m.indices = {0, 1, 3, 0, 3, 2}; m.vertices = { {.texid=handle, .texpos={0, 0}, .pos={-1, -1, 0}, .normal={0, 0, -1}, .colour={1, 1, 1, 1}, .material={0, 0, 1}}, @@ -51,10 +50,12 @@ void UI::update(double dt) void UI::update_slow() { - Mesh mesh; + Data::Mesh mesh; w_clock.remesh_slow(mesh); + mesh.bake_transforms(); + gm_dynamic_slow[gm_dynamic_slow_at].bind(); gm_dynamic_slow[gm_dynamic_slow_at].set(mesh, GL_DYNAMIC_DRAW); gm_dynamic_slow_at = (gm_dynamic_slow_at + 1) % 2; diff --git a/src/graphics/widget/clock.cpp b/src/graphics/widget/clock.cpp index 142c964..a7273ce 100644 --- a/src/graphics/widget/clock.cpp +++ b/src/graphics/widget/clock.cpp @@ -10,14 +10,15 @@ #include #include -#include "../mesh/arrays.hpp" -#include "../mesh/font.hpp" -#include "../mesh/arrays.hpp" +#include "../data/arrays.hpp" +#include "../data/font.hpp" +#include "../data/arrays.hpp" #include "../resize.hpp" #include "../../system.hpp" #include "../../util/streams.hpp" using namespace Sim::Graphics::Widget; +using namespace Sim::Graphics::Data; void Clock::update(double dt) { diff --git a/src/graphics/widget/clock.hpp b/src/graphics/widget/clock.hpp index 3ee3bd7..d0b6dd4 100644 --- a/src/graphics/widget/clock.hpp +++ b/src/graphics/widget/clock.hpp @@ -1,7 +1,7 @@ #pragma once -#include "../mesh/glmesh.hpp" +#include "../data/glmesh.hpp" namespace Sim::Graphics::Widget { @@ -11,7 +11,7 @@ struct Clock double dt; void update(double dt); - void remesh_slow(Mesh& rmesh); + void remesh_slow(Data::Mesh& rmesh); }; }; diff --git a/src/graphics/window.cpp b/src/graphics/window.cpp index d4ee3f8..daec62e 100644 --- a/src/graphics/window.cpp +++ b/src/graphics/window.cpp @@ -10,8 +10,8 @@ #include #include -#include "mesh/mesh.hpp" -#include "mesh/arrays.hpp" +#include "data/mesh.hpp" +#include "data/arrays.hpp" #include "input/keyboard.hpp" #include "input/mouse.hpp" #include "input/focus.hpp" @@ -19,17 +19,17 @@ #include "resize.hpp" #include "window.hpp" #include "shader.hpp" -#include "mesh/font.hpp" -#include "locations.hpp" +#include "data/font.hpp" #include "monitor/vessel.hpp" #include "monitor/core.hpp" #include "monitor/primary_loop.hpp" #include "monitor/secondary_loop.hpp" #include "monitor/turbine.hpp" -#include "mesh/texture.hpp" -#include "mesh/model.hpp" -#include "mesh/gllight.hpp" -#include "mesh/meshgen.hpp" +#include "monitor/cctv.hpp" +#include "data/texture.hpp" +#include "data/model.hpp" +#include "data/gllight.hpp" +#include "data/meshgen.hpp" #include "equipment/reactor.hpp" #include "equipment/generator.hpp" #include "equipment/pool.hpp" @@ -38,6 +38,7 @@ #include "ui.hpp" using namespace Sim::Graphics; +using namespace Sim::Graphics::Data; constexpr int SSBO_TRANSFORMS_LEN = 2; @@ -55,11 +56,14 @@ static Mesh g_scene; static std::vector g_scene_transforms; static GLMesh gm_scene; +static GLMesh gm_player; static GLMesh gm_dynamic_slow[2]; static std::vector lights; -static std::vector> monitors; -static std::vector> equipment; +static std::vector monitors; +static std::vector equipment; + +static Monitor::CCTV* monitor_cctv; glm::mat4 Window::projection_matrix; @@ -90,21 +94,15 @@ void remesh_static() equipment->remesh_static(mesh); } - gm_scene.bind(false); - gm_scene.set(mesh, GL_STATIC_DRAW, false); + gm_scene.bind(); + gm_scene.set(mesh, GL_STATIC_DRAW); g_scene_transforms = std::move(mesh.transforms); - std::cout << "Total triangles: " << mesh.indices.size() / 3 << "\n"; + std::cout << "Total triangle count: " << mesh.indices.size() / 3 << "\n"; } void render_shadow_map() { - Shader::LIGHT.use(); - - for(auto& light : lights) - { - light.render(); - } } void Window::create() @@ -165,21 +163,36 @@ void Window::create() Font::init(); UI::init(); - Shader::MAIN.load("../assets/shader", "main.vsh", "main.fsh"); + // load all the shaders + Shader::Source sources_main[] = { + {"../assets/shader/main.vsh", GL_VERTEX_SHADER}, + {"../assets/shader/main.fsh", GL_FRAGMENT_SHADER}, + }; + Shader::Source sources_light[] = { + {"../assets/shader/light.vsh", GL_VERTEX_SHADER}, + {"../assets/shader/light.gsh", GL_GEOMETRY_SHADER}, + {"../assets/shader/light.fsh", GL_FRAGMENT_SHADER}, + }; + Shader::MAIN.load(sources_main, 2); + Shader::LIGHT.load(sources_light, 3); + + Shader::MAIN.use(); glBindFramebuffer(GL_FRAMEBUFFER, 0); Sim::System& sys = *System::active; - Mesh m_transparent; Model model("../assets", "scene.glb"); + Mesh m_player = model.load("visual_player"); + + gm_player.bind(); + gm_player.bind_ssbo(); + gm_player.set(m_player, GL_STATIC_DRAW); g_scene.add(model.load("cr")); g_scene.add(model.load("cb")); g_scene.add(model.load("hw")); g_scene.bake_transforms(); - std::cout << "Static scene triangles: " << g_scene.indices.size() / 3 << "\n"; - Camera::init(model); // send all the light data @@ -190,21 +203,23 @@ void Window::create() glUniform1i(Shader::MAIN["lights_count"], model.lights.size()); - monitors.push_back(std::make_unique(model)); - monitors.push_back(std::make_unique(model)); - monitors.push_back(std::make_unique(model)); - monitors.push_back(std::make_unique(model)); - monitors.push_back(std::make_unique(model)); - equipment.push_back(std::make_unique(model)); - equipment.push_back(std::make_unique(model)); - equipment.push_back(std::make_unique(model)); + monitors.push_back(new Monitor::Core(model)); + monitors.push_back(new Monitor::Vessel(model)); + monitors.push_back(new Monitor::PrimaryLoop(model)); + monitors.push_back(new Monitor::SecondaryLoop(model)); + monitors.push_back(new Monitor::Turbine(model)); + monitors.push_back(monitor_cctv = new Monitor::CCTV(model)); + + equipment.push_back(new Equipment::Reactor(model)); + equipment.push_back(new Equipment::Generator(model)); + equipment.push_back(new Equipment::Pool(model)); remesh_static(); glfwShowWindow(win); // setup lighting and prerender shadows - Shader::LIGHT.load("../assets/shader", "light.vsh", "light.gsh", "light.fsh"); + Shader::LIGHT.use(); glUniform1f(Shader::LIGHT["far_plane"], 100.0f); GLLight::init(); @@ -322,9 +337,16 @@ void Window::update(double dt) } } +void Window::render_player() +{ + gm_player.bind(); + gm_player.bind_ssbo(); + gm_player.render(); +} + void Window::render_scene() { - gm_scene.bind(false); + gm_scene.bind(); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 3, ssbo_transforms[ssbo_transforms_at]); gm_scene.render(); @@ -336,32 +358,49 @@ void Window::render_scene() void Window::render() { - render_shadow_map(); + Shader::LIGHT.use(); + for(auto& light : lights) + { + light.render(); + } glm::vec<2, int> size = Resize::get_size(); glm::vec3 camera_pos = Camera::get_pos(); glm::mat4 mat_camera = Camera::get_matrix(); Shader::MAIN.use(); - glBindFramebuffer(GL_FRAMEBUFFER, 0); - glViewport(0, 0, size.x, size.y); - + glm::vec3 brightness = glm::vec3(System::active->grid.get_light_intensity()); glm::mat4 mat_projection = glm::perspective(glm::radians(90.0f), Resize::get_aspect(), 0.01f, 100.f); - glUniformMatrix4fv(Shader::MAIN["projection"], 1, false, &mat_projection[0][0]); + glUniform3fv(Shader::MAIN["brightness"], 1, &brightness[0]); + + glm::mat4 mat_player = glm::mat4(1); + mat_player = glm::translate(mat_player, glm::vec3(Camera::get_pos_base())); + mat_player = glm::rotate(mat_player, (float)glm::radians(90 - Camera::get_yaw()), glm::vec3(0, 0, 1)); + + glBindBuffer(GL_SHADER_STORAGE_BUFFER, gm_player.ssbo); + glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(glm::mat4), &mat_player, GL_STREAM_DRAW); + + monitor_cctv->render_view(); + glUniformMatrix4fv(Shader::MAIN["camera"], 1, false, &mat_camera[0][0]); glUniform3fv(Shader::MAIN["camera_pos"], 1, &camera_pos[0]); - glUniform3fv(Shader::MAIN["brightness"], 1, &brightness[0]); + glUniformMatrix4fv(Shader::MAIN["projection"], 1, false, &mat_projection[0][0]); projection_matrix = mat_projection; + + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + glViewport(0, 0, size.x, size.y); glFrontFace(GL_CCW); - glClearColor(0, 0, 0, 1.0f); - glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); render_scene(); + monitor_cctv->render_screen(); brightness = glm::vec3(1); + glBindFramebuffer(GL_FRAMEBUFFER, 0); glUniform3fv(Shader::MAIN["brightness"], 1, &brightness[0]); + glClear(GL_DEPTH_BUFFER_BIT); UI::render(); Focus::render_ui(); diff --git a/src/graphics/window.hpp b/src/graphics/window.hpp index 16b3eb5..0baf471 100644 --- a/src/graphics/window.hpp +++ b/src/graphics/window.hpp @@ -17,6 +17,7 @@ void reload(); void update(double dt); void render(); void render_scene(); +void render_player(); void destroy(); void close(); diff --git a/src/main.cpp b/src/main.cpp index 6298681..af2e703 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -10,7 +10,6 @@ #include "coolant/valve.hpp" #include "coolant/pump.hpp" -#include "graphics/mesh/mesh.hpp" #include "graphics/input/focus.hpp" #include "graphics/window.hpp" diff --git a/src/util/math.hpp b/src/util/math.hpp index 527d4f3..0b30f01 100644 --- a/src/util/math.hpp +++ b/src/util/math.hpp @@ -19,13 +19,24 @@ constexpr double j_to_ms2(double j, double mass) return m*std::sqrt(m * j / (mass * 0.001)); } -constexpr float map(float v, float imin, float imax, float omin, float omax) +template +constexpr A map(A v, auto imin, auto imax, auto omin, auto omax) { return (v - imin) * (omax - omin) / (imax - imin) + omin; } -template -constexpr A mod(A a, B b) +template +constexpr A clamp(A v, auto min, auto max) +{ + if(v < min) + return min; + if(v > max) + return max; + return v; +} + +template +constexpr A mod(A a, auto b) { A v = std::fmod(a, b);