271 lines
7.0 KiB
C++
271 lines
7.0 KiB
C++
|
|
#include <GL/glew.h>
|
|
#include <GLFW/glfw3.h>
|
|
|
|
#include <glm/ext/matrix_transform.hpp>
|
|
#include <glm/ext/matrix_clip_space.hpp>
|
|
|
|
#include <cmath>
|
|
|
|
#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<FocusCCTV>(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);
|
|
}
|
|
|