mirror of
https://github.com/TomHarte/CLK.git
synced 2026-03-13 19:16:40 +00:00
236 lines
7.0 KiB
C++
236 lines
7.0 KiB
C++
//
|
|
// TextureTarget.cpp
|
|
// Clock Signal
|
|
//
|
|
// Created by Thomas Harte on 07/02/2016.
|
|
// Copyright 2016 Thomas Harte. All rights reserved.
|
|
//
|
|
|
|
#include "TextureTarget.hpp"
|
|
|
|
#include <algorithm>
|
|
#include <stdexcept>
|
|
|
|
using namespace Outputs::Display::OpenGL;
|
|
|
|
TextureTarget::TextureTarget(
|
|
const API api,
|
|
const GLsizei width,
|
|
const GLsizei height,
|
|
const GLenum texture_unit,
|
|
const GLint mag_filter,
|
|
const bool has_stencil_buffer
|
|
) :
|
|
api_(api),
|
|
width_(width),
|
|
height_(height),
|
|
texture_unit_(texture_unit)
|
|
{
|
|
// Generate and bind a frame buffer.
|
|
test_gl([&]{ glGenFramebuffers(1, &framebuffer_); });
|
|
test_gl([&]{ glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_); });
|
|
|
|
// Generate a texture and bind it to the nominated texture unit.
|
|
test_gl([&]{ glGenTextures(1, &texture_); });
|
|
bind_texture();
|
|
|
|
// Set dimensions and set the user-supplied magnification filter.
|
|
test_gl([&]{
|
|
glTexImage2D(
|
|
GL_TEXTURE_2D,
|
|
0,
|
|
GL_RGBA,
|
|
GLsizei(width_),
|
|
GLsizei(height_),
|
|
0,
|
|
GL_RGBA,
|
|
GL_UNSIGNED_BYTE,
|
|
nullptr
|
|
);
|
|
});
|
|
test_gl([&]{ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter); });
|
|
test_gl([&]{ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); });
|
|
|
|
// Set the texture as colour attachment 0 on the frame buffer.
|
|
test_gl([&]{ glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture_, 0); });
|
|
|
|
// Also add a stencil buffer if requested.
|
|
if(has_stencil_buffer) {
|
|
test_gl([&]{ glGenRenderbuffers(1, &renderbuffer_); });
|
|
test_gl([&]{ glBindRenderbuffer(GL_RENDERBUFFER, renderbuffer_); });
|
|
test_gl([&]{ glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, width_, height_); });
|
|
test_gl([&]{
|
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, renderbuffer_);
|
|
});
|
|
}
|
|
|
|
// Check for successful construction.
|
|
const auto framebuffer_status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
|
|
if(framebuffer_status != GL_FRAMEBUFFER_COMPLETE) {
|
|
switch(framebuffer_status) {
|
|
case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT:
|
|
throw std::runtime_error("GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT");
|
|
case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER:
|
|
throw std::runtime_error("GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER");
|
|
case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER:
|
|
throw std::runtime_error("GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER");
|
|
case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT:
|
|
throw std::runtime_error("GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT");
|
|
case GL_FRAMEBUFFER_UNSUPPORTED:
|
|
throw std::runtime_error("GL_FRAMEBUFFER_UNSUPPORTED");
|
|
default:
|
|
throw std::runtime_error("Framebuffer status incomplete: " + std::to_string(framebuffer_status));
|
|
|
|
case 0:
|
|
test_gl_error();
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Clear the framebuffer.
|
|
test_gl([&]{ glClear(GL_COLOR_BUFFER_BIT); });
|
|
}
|
|
|
|
TextureTarget::~TextureTarget() {
|
|
glDeleteFramebuffers(1, &framebuffer_);
|
|
glDeleteTextures(1, &texture_);
|
|
glDeleteRenderbuffers(1, &renderbuffer_);
|
|
glDeleteVertexArrays(1, &drawing_vertex_array_);
|
|
glDeleteBuffers(1, &drawing_array_buffer_);
|
|
}
|
|
|
|
TextureTarget::TextureTarget(TextureTarget &&rhs) {
|
|
*this = std::move(rhs);
|
|
}
|
|
|
|
TextureTarget &TextureTarget::operator =(TextureTarget &&rhs) {
|
|
api_ = rhs.api_;
|
|
std::swap(framebuffer_, rhs.framebuffer_);
|
|
std::swap(texture_, rhs.texture_);
|
|
std::swap(renderbuffer_, rhs.renderbuffer_);
|
|
std::swap(width_, rhs.width_);
|
|
std::swap(height_, rhs.height_);
|
|
std::swap(texture_unit_, rhs.texture_unit_);
|
|
|
|
// Other fields in the TextureTarget relate to the `draw` functionality below, which I intend to
|
|
// eliminate. Therefore just let them be.
|
|
|
|
return *this;
|
|
}
|
|
|
|
void TextureTarget::bind_framebuffer() {
|
|
test_gl([&]{ glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_); });
|
|
test_gl([&]{ glViewport(0, 0, width_, height_); });
|
|
}
|
|
|
|
void TextureTarget::bind_texture() const {
|
|
test_gl([&]{ glActiveTexture(texture_unit_); });
|
|
test_gl([&]{ glBindTexture(GL_TEXTURE_2D, texture_); });
|
|
}
|
|
|
|
// TODO: eliminate below, plus relevant local storage.
|
|
void TextureTarget::draw(const float aspect_ratio, const float colour_threshold) const {
|
|
if(!pixel_shader_) {
|
|
const char *const vertex_shader =
|
|
R"glsl(
|
|
in vec2 texCoord;
|
|
in vec2 position;
|
|
|
|
out vec2 texCoordVarying;
|
|
|
|
void main(void) {
|
|
texCoordVarying = texCoord;
|
|
gl_Position = vec4(position, 0.0, 1.0);
|
|
}
|
|
)glsl";
|
|
const char *const fragment_shader =
|
|
R"glsl(
|
|
in vec2 texCoordVarying;
|
|
|
|
uniform sampler2D texID;
|
|
uniform float threshold;
|
|
|
|
out vec4 fragColour;
|
|
|
|
void main(void) {
|
|
fragColour = clamp(texture(texID, texCoordVarying), threshold, 1.0);
|
|
}
|
|
)glsl";
|
|
pixel_shader_ = std::make_unique<Shader>(api_, vertex_shader, fragment_shader);
|
|
pixel_shader_->bind();
|
|
|
|
test_gl([&]{ glGenVertexArrays(1, &drawing_vertex_array_); });
|
|
test_gl([&]{ glGenBuffers(1, &drawing_array_buffer_); });
|
|
|
|
test_gl([&]{ glBindVertexArray(drawing_vertex_array_); });
|
|
test_gl([&]{ glBindBuffer(GL_ARRAY_BUFFER, drawing_array_buffer_); });
|
|
|
|
const GLint position_attribute = pixel_shader_->get_attrib_location("position");
|
|
const GLint tex_coord_attribute = pixel_shader_->get_attrib_location("texCoord");
|
|
|
|
test_gl([&]{ glEnableVertexAttribArray(GLuint(position_attribute)); });
|
|
test_gl([&]{ glEnableVertexAttribArray(GLuint(tex_coord_attribute)); });
|
|
|
|
const GLsizei vertex_stride = 4 * sizeof(GLfloat);
|
|
test_gl([&]{
|
|
glVertexAttribPointer(
|
|
GLuint(position_attribute),
|
|
2,
|
|
GL_FLOAT,
|
|
GL_FALSE,
|
|
vertex_stride,
|
|
(void *)0
|
|
);
|
|
});
|
|
test_gl([&]{
|
|
glVertexAttribPointer(
|
|
GLuint(tex_coord_attribute),
|
|
2,
|
|
GL_FLOAT,
|
|
GL_FALSE,
|
|
vertex_stride,
|
|
(void *)(2 * sizeof(GLfloat))
|
|
);
|
|
});
|
|
|
|
const GLint texIDUniform = pixel_shader_->get_uniform_location("texID");
|
|
test_gl([&]{ glUniform1i(texIDUniform, GLint(texture_unit_ - GL_TEXTURE0)); });
|
|
|
|
threshold_uniform_ = pixel_shader_->get_uniform_location("threshold");
|
|
}
|
|
|
|
if(set_aspect_ratio_ != aspect_ratio) {
|
|
set_aspect_ratio_ = aspect_ratio;
|
|
float buffer[4*4];
|
|
|
|
// establish texture coordinates
|
|
buffer[2] = 0.0f;
|
|
buffer[3] = 0.0f;
|
|
buffer[6] = 0.0f;
|
|
buffer[7] = 1.0f;//float(height_) / float(expanded_height_);
|
|
buffer[10] = 1.0f;//float(width_) / float(expanded_width_);
|
|
buffer[11] = 0.0f;
|
|
buffer[14] = buffer[10];
|
|
buffer[15] = buffer[7];
|
|
|
|
// determine positions; rule is to keep the same height and centre
|
|
const float internal_aspect_ratio = float(width_) / float(height_);
|
|
const float aspect_ratio_ratio = internal_aspect_ratio / aspect_ratio;
|
|
|
|
buffer[0] = -aspect_ratio_ratio; buffer[1] = -1.0f;
|
|
buffer[4] = -aspect_ratio_ratio; buffer[5] = 1.0f;
|
|
buffer[8] = aspect_ratio_ratio; buffer[9] = -1.0f;
|
|
buffer[12] = aspect_ratio_ratio; buffer[13] = 1.0f;
|
|
|
|
// upload buffer
|
|
test_gl([&]{ glBindBuffer(GL_ARRAY_BUFFER, drawing_array_buffer_); });
|
|
test_gl([&]{ glBufferData(GL_ARRAY_BUFFER, sizeof(buffer), buffer, GL_STATIC_DRAW); });
|
|
}
|
|
|
|
pixel_shader_->bind();
|
|
test_gl([&]{ glUniform1f(threshold_uniform_, colour_threshold); });
|
|
|
|
test_gl([&]{ glBindVertexArray(drawing_vertex_array_); });
|
|
test_gl([&]{ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); });
|
|
}
|