add texture atlas for gpus without bindless textures

This commit is contained in:
Jay Robson 2024-03-22 01:12:48 +11:00
parent e2b16e0591
commit 2d69bb3c78
35 changed files with 699 additions and 277 deletions

Binary file not shown.

BIN
assets/scene.blend (Stored with Git LFS)

Binary file not shown.

BIN
assets/scene.glb (Stored with Git LFS)

Binary file not shown.

View File

@ -1,26 +0,0 @@
#version 460 core
uniform sampler2D tex;
uniform vec2 direction;
uniform int samples;
in vec2 FragPos;
out vec4 FragColour;
void main()
{
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 offset = vec2(i) * step;
sum += texture(tex, FragPos + offset);
}
FragColour = sum / float(samples);
}

View File

@ -1,21 +0,0 @@
#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);
}

View File

@ -38,18 +38,19 @@ void Arrays::vertex_attrib_pointers()
glVertexAttribPointer(5, 3, GL_FLOAT, true, sizeof(v), ptr_diff(&v.tbn[2], &v)); glVertexAttribPointer(5, 3, GL_FLOAT, true, sizeof(v), ptr_diff(&v.tbn[2], &v));
glEnableVertexAttribArray(5); glEnableVertexAttribArray(5);
glVertexAttribIPointer(6, 1, GL_INT, sizeof(v), ptr_diff(&v.transform_id, &v)); glVertexAttribPointer(6, 3, GL_FLOAT, false, sizeof(v), ptr_diff(&v.material, &v));
glEnableVertexAttribArray(6); glEnableVertexAttribArray(6);
glVertexAttribIPointer(7, 1, GL_UNSIGNED_INT, sizeof(v), ptr_diff(&v.tex_diffuse, &v)); glVertexAttribIPointer(7, 1, GL_INT, sizeof(v), ptr_diff(&v.transform_id, &v));
glEnableVertexAttribArray(7); glEnableVertexAttribArray(7);
glVertexAttribIPointer(8, 1, GL_UNSIGNED_INT, sizeof(v), ptr_diff(&v.tex_normal, &v)); glVertexAttribIPointer(8, 1, GL_UNSIGNED_INT, sizeof(v), ptr_diff(&v.tex_diffuse, &v));
glEnableVertexAttribArray(8); glEnableVertexAttribArray(8);
glVertexAttribPointer(9, 3, GL_FLOAT, false, sizeof(v), ptr_diff(&v.material, &v)); glVertexAttribIPointer(9, 1, GL_UNSIGNED_INT, sizeof(v), ptr_diff(&v.tex_normal, &v));
glEnableVertexAttribArray(9); glEnableVertexAttribArray(9);
} }
std::ostream& Arrays::operator<<(std::ostream& os, const Vertex& v) std::ostream& Arrays::operator<<(std::ostream& os, const Vertex& v)

138
src/graphics/data/atlas.hpp Normal file
View File

@ -0,0 +1,138 @@
#pragma once
#include <vector>
namespace Sim::Graphics::Data
{
template <int N>
struct Atlas
{
struct Pixel
{
uint8_t data[N] = {0};
constexpr Pixel() = default;
constexpr Pixel(const Pixel& p)
{
for(int i = 0; i < N; i++)
{
data[i] = p.data[i];
}
}
constexpr Pixel(const uint8_t* p)
{
for(int i = 0; i < N; i++)
{
data[i] = p[i];
}
}
constexpr bool operator == (const Pixel& p) const
{
for(int i = 0; i < N; i++)
{
if(data[i] != p.data[i])
{
return false;
}
}
return true;
}
constexpr bool operator != (const Pixel& p) const
{
return !(*this == p);
}
constexpr Pixel& operator = (const Pixel& p)
{
for(int i = 0; i < N; i++)
{
data[i] = p.data[i];
}
return *this;
}
constexpr Pixel& operator = (const uint8_t* p)
{
for(int i = 0; i < N; i++)
{
data[i] = p[i];
}
return *this;
}
constexpr uint8_t& operator [] (int i)
{
return data[i];
}
constexpr const uint8_t& operator [] (int i) const
{
return data[i];
}
};
const int width;
const int height;
std::vector<Pixel> data;
Atlas(int width, int height)
: width(width)
, height(height)
, data(width * height)
{
}
Pixel& operator()(int x, int y)
{
return data[y * width + x];
}
const Pixel& operator()(int x, int y) const
{
return data[y * width + x];
}
void draw(const Atlas<N>& src, int x, int y, bool padding = false)
{
for(int i = 0; i < src.height; i++)
{
for(int j = 0; j < src.width; j++)
{
(*this)(x + j, y + i) = src(j, i);
}
}
if(padding)
{
for(int i = 0; i < src.height; i++)
{
(*this)(x - 1, y + i) = src(0, i);
(*this)(x + src.width, y + i) = src(src.width - 1, i);
}
for(int i = 0; i < src.width; i++)
{
(*this)(x + i, y - 1) = src(i, 0);
(*this)(x + i, y + src.height) = src(i, src.height - 1);
}
(*this)(x - 1, y - 1) = src(0, 0);
(*this)(x + src.width, y - 1) = src(src.width - 1, 0);
(*this)(x - 1, y + src.height) = src(0, src.height - 1);
(*this)(x + src.width, y + src.height) = src(src.width - 1, src.height - 1);
}
}
};
};

View File

@ -2,16 +2,18 @@
#pragma once #pragma once
#include <glm/matrix.hpp> #include <glm/matrix.hpp>
#include <string>
namespace Sim::Graphics::Data namespace Sim::Graphics::Data
{ {
struct Camera struct Camera
{ {
glm::vec3 pos; const std::string name;
glm::vec3 look; const glm::vec3 pos;
glm::vec3 up; const glm::vec3 look;
float fov; const glm::vec3 up;
const float fov;
float pitch = 0; float pitch = 0;
float yaw = 0; float yaw = 0;

View File

@ -8,25 +8,27 @@
#include <glm/vec2.hpp> #include <glm/vec2.hpp>
#include <iostream> #include <iostream>
#include <vector> #include <vector>
#include <format>
#include "../shader.hpp"
#include "mesh.hpp" #include "mesh.hpp"
#include "arrays.hpp" #include "arrays.hpp"
#include "material.hpp" #include "material.hpp"
#include "texture.hpp"
#include "font.hpp" #include "font.hpp"
using namespace Sim::Graphics::Data; using namespace Sim::Graphics::Data;
struct Character Font Fonts::BASE;
Font Fonts::MONO;
void Fonts::init()
{ {
uint32_t handle; BASE.init("DroidSans", 64);
float advance; MONO.init("DroidSansMono", 64);
glm::vec2 size; }
glm::vec2 bearing;
};
static Character chars[128]; void Font::init(const std::string& name, int size)
void Font::init()
{ {
FT_Library ft; FT_Library ft;
FT_Face face; FT_Face face;
@ -37,22 +39,15 @@ void Font::init()
return; return;
} }
if(FT_New_Face(ft, "../assets/font/DroidSans.ttf", 0, &face)) if(FT_New_Face(ft, std::format("../assets/font/{}.ttf", name).c_str(), 0, &face))
{ {
std::cout << "Error: failed to load freetype font\n"; std::cout << "Error: failed to load freetype font\n";
return; return;
} }
int size = 256; texel_size = 1.0f / size;
float m = 1.0f / size;
FT_Set_Pixel_Sizes(face, 0, size); FT_Set_Pixel_Sizes(face, 0, size);
GLuint texids[128];
std::vector<glm::vec<4, unsigned char>> pixels;
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
for(int i = 0; i < 128; i++) for(int i = 0; i < 128; i++)
{ {
if(FT_Load_Char(face, (char)i, FT_LOAD_RENDER)) if(FT_Load_Char(face, (char)i, FT_LOAD_RENDER))
@ -65,61 +60,59 @@ void Font::init()
int offx = face->glyph->bitmap_left; int offx = face->glyph->bitmap_left;
int offy = face->glyph->bitmap_top; int offy = face->glyph->bitmap_top;
Character& c = chars[i]; Character& c = characters[i];
c.advance = face->glyph->advance.x * m / 64.0; c.advance = face->glyph->advance.x * texel_size / 64.0;
c.size = {width * m, height * m}; c.size = {width * texel_size, height * texel_size};
c.bearing = {offx * m, offy * m}; c.bearing = {offx * texel_size, offy * texel_size};
if(c.size.x == 0 || c.size.y == 0) if(c.size.x == 0 || c.size.y == 0)
{ {
c.handle = 0;
continue; continue;
} }
pixels.resize(width * height); std::vector<uint8_t> buffer(width * height);
for(int i = 0; i < width * height; i++) for(int i = 0; i < width * height; i++)
{ {
pixels[i] = glm::vec<4, unsigned char>(face->glyph->bitmap.buffer[i]); buffer[i] = face->glyph->bitmap.buffer[i];
} }
glCreateTextures(GL_TEXTURE_2D, 1, &texids[i]); int swizzleMask[] = {GL_RED, GL_RED, GL_RED, GL_RED};
c.handle = Texture::load_mem(buffer.data(), width, height, 1, swizzleMask);
glTextureStorage2D(texids[i], 1, GL_RGBA8, width, height);
glTextureSubImage2D(texids[i], 0, 0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, &pixels[0]);
glTextureParameteri(texids[i], GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTextureParameteri(texids[i], GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTextureParameteri(texids[i], GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTextureParameteri(texids[i], GL_TEXTURE_MAG_FILTER, GL_LINEAR);
c.handle = glGetTextureHandleARB(texids[i]);
glMakeTextureHandleResidentARB(c.handle);
chars[i] = c;
} }
FT_Done_FreeType(ft); FT_Done_FreeType(ft);
} }
Mesh& Mesh::load_text(const char* text, double size) Font::Font()
{ {
std::vector<Arrays::Vertex> vertices; }
std::vector<unsigned int> indices;
Mesh Font::load_text(const std::string& text, float size) const
{
Mesh m;
if(text[0] == '\0')
{
return m;
}
float x = 0, y = size; float x = 0, y = size;
unsigned int at = 0; unsigned int at = 0;
float t0 = 0;
if(text[0] == '\0') float t1 = 1;
/*
if(!Shader::USE_BINDLESS_TEXTURES)
{ {
this->vertices.clear(); t0 += texel_size / 2;
this->indices.clear(); t1 -= texel_size / 2;
return *this; }*/
}
for(unsigned int i = 0; text[i] != '\0'; i++)
for(unsigned int i = 0; i < text.size(); i++)
{ {
char c = text[i]; char c = text[i];
Character ch = chars[c]; const Character& ch = characters[c];
if(c == '\n') if(c == '\n')
{ {
@ -144,30 +137,25 @@ Mesh& Mesh::load_text(const char* text, double size)
float ex = sx + ch.size.x * size; float ex = sx + ch.size.x * size;
float ey = sy + ch.size.y * size; float ey = sy + ch.size.y * size;
vertices.push_back(Arrays::Vertex{.texpos={0, 0}, .pos={sx, sy, 0}, .tex_diffuse=ch.handle, .material={0, 0, 1}}); m.vertices.push_back(Arrays::Vertex{.texpos={t0, t0}, .pos={sx, sy, 0}, .tex_diffuse=ch.handle, .material={0, 0, 1}});
vertices.push_back(Arrays::Vertex{.texpos={0, 1}, .pos={sx, ey, 0}, .tex_diffuse=ch.handle, .material={0, 0, 1}}); m.vertices.push_back(Arrays::Vertex{.texpos={t0, t1}, .pos={sx, ey, 0}, .tex_diffuse=ch.handle, .material={0, 0, 1}});
vertices.push_back(Arrays::Vertex{.texpos={1, 0}, .pos={ex, sy, 0}, .tex_diffuse=ch.handle, .material={0, 0, 1}}); m.vertices.push_back(Arrays::Vertex{.texpos={t1, t0}, .pos={ex, sy, 0}, .tex_diffuse=ch.handle, .material={0, 0, 1}});
vertices.push_back(Arrays::Vertex{.texpos={1, 1}, .pos={ex, ey, 0}, .tex_diffuse=ch.handle, .material={0, 0, 1}}); m.vertices.push_back(Arrays::Vertex{.texpos={t1, t1}, .pos={ex, ey, 0}, .tex_diffuse=ch.handle, .material={0, 0, 1}});
indices.insert(indices.end(), &index[0], &index[6]); m.indices.insert(m.indices.end(), &index[0], &index[6]);
at += 4; at += 4;
x += ch.advance * size; x += ch.advance * size;
} }
this->vertices = std::move(vertices); return m;
this->indices = std::move(indices);
this->transforms.clear();
return *this;
} }
Mesh& Mesh::load_text(const char* text, double size, glm::vec2 align) Mesh Font::load_text(const std::string& text, float size, glm::vec2 align) const
{ {
glm::vec2 max; glm::vec2 max;
Mesh m = load_text(text, size);
load_text(text, size);
for(Arrays::Vertex& v : vertices) for(Arrays::Vertex& v : m.vertices)
{ {
if(v.pos.x > max.x) if(v.pos.x > max.x)
{ {
@ -182,12 +170,12 @@ Mesh& Mesh::load_text(const char* text, double size, glm::vec2 align)
align *= max; align *= max;
for(Arrays::Vertex& v : vertices) for(Arrays::Vertex& v : m.vertices)
{ {
v.pos.x -= align.x; v.pos.x -= align.x;
v.pos.y -= align.y; v.pos.y -= align.y;
} }
return *this; return m;
} }

View File

@ -6,10 +6,56 @@
#include <string> #include <string>
#include <sstream> #include <sstream>
namespace Sim::Graphics::Data::Font #include <glm/vec2.hpp>
namespace Sim::Graphics::Data
{ {
void init(); class Font
{
struct Character
{
uint32_t handle;
glm::vec2 size;
glm::vec2 bearing;
float advance;
};
float texel_size;
Character characters[128];
public:
Font();
void init(const std::string& name, int size);
Mesh load_text(const std::string& text, float size, glm::vec2 align) const;
Mesh load_text(const std::string& text, float size) const;
template <class T>
void load_text(const char* header, T& item, double size)
{
std::stringstream ss;
ss << header << item;
load_text(ss.str(), size);
}
template <class T>
void load_text(const char* header, T& item, double size, glm::vec2 align)
{
std::stringstream ss;
ss << header << item;
load_text(ss.str(), size, align);
}
};
namespace Fonts
{
void init();
extern Font BASE;
extern Font MONO;
}
}; };

View File

View File

@ -28,8 +28,6 @@ struct Mesh
Mesh& set_blank_transform(); Mesh& set_blank_transform();
Mesh& set_normal_id(unsigned int id); Mesh& set_normal_id(unsigned int id);
Mesh& set_diffuse_id(unsigned int id); Mesh& set_diffuse_id(unsigned int id);
Mesh& load_text(const char* text, double size);
Mesh& load_text(const char* text, double size, glm::vec2 align);
Mesh& add(const Mesh& o, glm::mat4 mat = glm::mat4(1), bool bake = false); Mesh& add(const Mesh& o, glm::mat4 mat = glm::mat4(1), bool bake = false);
Mesh to_lines() const; Mesh to_lines() const;
@ -40,14 +38,6 @@ struct Mesh
bool operator==(const Mesh&) const = default; bool operator==(const Mesh&) const = default;
template <class T>
void load_text(const char* header, T& item, double size)
{
std::stringstream ss;
ss << header << item;
load_text(ss.str().c_str(), size);
}
friend std::ostream& operator<<(std::ostream& os, const Mesh& m); friend std::ostream& operator<<(std::ostream& os, const Mesh& m);
}; };

View File

@ -252,7 +252,13 @@ Model::Model(std::string base, std::string filename) : base(base)
glm::vec3 look = glm::normalize(glm::mat3(mat) * glm::vec3(dx, dy, dz)); 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)); 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}); cameras.push_back(Camera {
.name={camera->mName.C_Str()},
.pos=glm::vec3(pos),
.look=look,
.up=up,
.fov=camera->mHorizontalFOV
});
} }
for(int i = 0; i < scene->mNumMaterials; i++) for(int i = 0; i < scene->mNumMaterials; i++)

View File

@ -2,33 +2,199 @@
#include <GL/glew.h> #include <GL/glew.h>
#include <GLFW/glfw3.h> #include <GLFW/glfw3.h>
#include <stb/stb_image.h> #include <stb/stb_image.h>
#include <stb/stb_rect_pack.h>
#include <glm/matrix.hpp>
#include <unordered_map> #include <unordered_map>
#include <iostream> #include <iostream>
#include <cmath>
#include "texture.hpp" #include "texture.hpp"
#include "atlas.hpp"
#include "../shader.hpp"
using namespace Sim::Graphics::Data; using namespace Sim::Graphics::Data;
static std::unordered_map<std::string, uint64_t> loaded; static bool is_done = false;
uint64_t Texture::handle_white; static uint32_t atlas_texid;
uint64_t Texture::handle_normal; static uint32_t atlas_uv_ssbo;
static std::unordered_map<std::string, uint32_t> loaded;
static std::vector<Atlas<4>> texture_atlas_queue;
uint32_t Texture::handle_white;
uint32_t Texture::handle_normal;
void Texture::init() void Texture::init()
{ {
unsigned char pixels_white[] = {255, 255, 255}; glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
unsigned char pixels_normal[] = {128, 128, 255};
handle_white = load_mem(pixels_white, 1, 1, 3); unsigned char pixels_white[] = {255};
handle_normal = load_mem(pixels_normal, 1, 1, 3); unsigned char pixels_normal[] = {128, 255};
int swizzle_white[] = {GL_RED, GL_RED, GL_RED, GL_RED};
int swizzle_normal[] = {GL_RED, GL_RED, GL_GREEN, GL_GREEN};
handle_white = load_mem(pixels_white, 1, 1, 1, swizzle_white);
handle_normal = load_mem(pixels_normal, 1, 1, 2, swizzle_normal);
} }
uint64_t Texture::load_mem(const void* data, int width, int height, int channels) void Texture::generate_atlas()
{ {
// if we are using bindless textures, we don't need to generate an atlas
if(Shader::USE_BINDLESS_TEXTURES)
{
return;
}
int total_area = 0;
int padding = 2;
int offset = 1;
for(const Atlas<4>& atlas : texture_atlas_queue)
{
total_area += (atlas.width + padding) * (atlas.height + padding);
}
int size = std::pow(2, std::ceil(std::log2(std::sqrt(total_area))));
std::vector<stbrp_rect> rects;
std::vector<glm::mat2> uvs;
rects.reserve(texture_atlas_queue.size());
uvs.reserve(texture_atlas_queue.size());
for(int i = 0; i < texture_atlas_queue.size(); i++)
{
const Atlas<4>& atlas = texture_atlas_queue[i];
stbrp_rect rect;
rect.id = i;
rect.w = atlas.width + padding;
rect.h = atlas.height + padding;
rects.push_back(rect);
}
stbrp_context context;
std::vector<stbrp_node> nodes(size);
for(;;)
{
stbrp_init_target(&context, size, size, nodes.data(), nodes.size());
if(stbrp_pack_rects(&context, rects.data(), rects.size()) == 1)
{
break;
}
size *= 2;
nodes.resize(size);
std::cout << "Error: failed to pack textures, trying again with size " << size << "\n";
}
Atlas<4> atlas(size, size);
for(const stbrp_rect& rect : rects)
{
const Atlas<4>& src = texture_atlas_queue[rect.id];
atlas.draw(src, rect.x + offset, rect.y + offset, true);
uvs.emplace_back(glm::mat2(
(rect.x + offset + 0.5f) / size, (rect.y + offset + 0.5f) / size,
(rect.x + offset + src.width - 0.5f) / size, (rect.y + offset + src.height - 0.5f) / size
));
}
std::cout << "Finished stitching " << size << "x" << size << " texture atlas\n";
glGenTextures(1, &atlas_texid);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, atlas_texid);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, size, size, 0, GL_RGBA, GL_UNSIGNED_BYTE, atlas.data.data());
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glGenerateMipmap(GL_TEXTURE_2D);
glActiveTexture(GL_TEXTURE0);
glGenBuffers(1, &atlas_uv_ssbo);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, atlas_uv_ssbo);
glBufferData(GL_SHADER_STORAGE_BUFFER, uvs.size() * sizeof(uvs[0]), uvs.data(), GL_STATIC_DRAW);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 5, atlas_uv_ssbo);
glUniform1i(Shader::MAIN["tex_atlas"], 1);
is_done = true;
}
uint32_t Texture::load_mem(const uint8_t* data, int width, int height, int channels, int* swizzleMask)
{
if(is_done)
{
throw std::runtime_error("Texture loading is done");
}
if(!data) if(!data)
{ {
return 0; return 0;
} }
int swizzleMaskDefault[] = {GL_RED, GL_GREEN, GL_BLUE, GL_ALPHA};
if(!swizzleMask)
{
swizzleMask = swizzleMaskDefault;
switch(channels)
{
case 1:
swizzleMask[1] = GL_ONE;
case 2:
swizzleMask[2] = GL_ONE;
case 3:
swizzleMask[3] = GL_ONE;
}
}
if(!Shader::USE_BINDLESS_TEXTURES)
{
Atlas<4> atlas(width, height);
for(int i = 0; i < width * height; i++)
{
int pixel_pos = i * channels;
Atlas<4>::Pixel pixel;
for(int j = 0; j < 4; j++)
{
switch(swizzleMask[j])
{
case GL_RED:
pixel[j] = data[pixel_pos];
break;
case GL_GREEN:
pixel[j] = data[pixel_pos + 1];
break;
case GL_BLUE:
pixel[j] = data[pixel_pos + 2];
break;
case GL_ALPHA:
pixel[j] = data[pixel_pos + 3];
break;
case GL_ZERO:
pixel[j] = 0;
break;
case GL_ONE:
pixel[j] = 255;
break;
}
}
atlas.data[i] = pixel;
}
texture_atlas_queue.push_back(std::move(atlas));
return texture_atlas_queue.size() - 1;
}
GLenum format; GLenum format;
GLenum format_in; GLenum format_in;
@ -56,22 +222,22 @@ uint64_t Texture::load_mem(const void* data, int width, int height, int channels
unsigned int texid; unsigned int texid;
glCreateTextures(GL_TEXTURE_2D, 1, &texid); glGenTextures(1, &texid);
glTextureStorage2D(texid, 1, format_in, width, height); glBindTexture(GL_TEXTURE_2D, texid);
glTextureSubImage2D(texid, 0, 0, 0, width, height, format, GL_UNSIGNED_BYTE, data); glTexImage2D(GL_TEXTURE_2D, 0, format_in, width, height, 0, format, GL_UNSIGNED_BYTE, data);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTextureParameteriv(texid, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask);
glGenerateMipmap(GL_TEXTURE_2D);
glTextureParameteri(texid, GL_TEXTURE_WRAP_S, GL_REPEAT); uint32_t handle = glGetTextureHandleARB(texid);
glTextureParameteri(texid, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTextureParameteri(texid, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTextureParameteri(texid, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glGenerateTextureMipmap(texid);
uint64_t handle = glGetTextureHandleARB(texid);
glMakeTextureHandleResidentARB(handle); glMakeTextureHandleResidentARB(handle);
return handle; return handle;
} }
uint64_t Texture::load_mem(const unsigned char* filedata, size_t len) uint32_t Texture::load_mem(const uint8_t* filedata, size_t len)
{ {
int width, height, channels; int width, height, channels;
unsigned char* data = stbi_load_from_memory(filedata, len, &width, &height, &channels, 0); unsigned char* data = stbi_load_from_memory(filedata, len, &width, &height, &channels, 0);
@ -80,7 +246,7 @@ uint64_t Texture::load_mem(const unsigned char* filedata, size_t len)
return handle; return handle;
} }
uint64_t Texture::load(std::string path) uint32_t Texture::load(std::string path)
{ {
const auto it = loaded.find(path); const auto it = loaded.find(path);

View File

@ -4,17 +4,19 @@
#include <GL/glew.h> #include <GL/glew.h>
#include <string> #include <string>
#include <glm/vec2.hpp>
namespace Sim::Graphics::Data::Texture namespace Sim::Graphics::Data::Texture
{ {
extern uint64_t handle_white; extern uint32_t handle_white;
extern uint64_t handle_normal; extern uint32_t handle_normal;
void init(); void init();
uint64_t load(std::string path); void generate_atlas();
uint64_t load_mem(const void* data, int width, int height, int channels); uint32_t load(std::string path);
uint64_t load_mem(const unsigned char* data, size_t len); uint32_t load_mem(const uint8_t* data, int width, int height, int channels, int* swizzleMask = nullptr);
uint32_t load_mem(const uint8_t* data, size_t len);
}; };

View File

@ -13,6 +13,7 @@
#include "../camera.hpp" #include "../camera.hpp"
#include "../input/focus.hpp" #include "../input/focus.hpp"
#include "../data/texture.hpp" #include "../data/texture.hpp"
#include "../data/font.hpp"
#include "../../system.hpp" #include "../../system.hpp"
#include "../../util/math.hpp" #include "../../util/math.hpp"
#include "../../util/streams.hpp" #include "../../util/streams.hpp"
@ -49,7 +50,7 @@ public:
if(zoom) if(zoom)
{ {
Data::Camera& active = parent->cameras[parent->camera_at]; 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); active.zoom = Util::Math::clamp(active.zoom - zoom * dt * 0.5f, 0.25, 1);
} }
} }
@ -63,7 +64,7 @@ public:
parent->camera_at = (parent->camera_at + parent->cameras.size() - 1) % parent->cameras.size(); parent->camera_at = (parent->camera_at + parent->cameras.size() - 1) % parent->cameras.size();
break; break;
case GLFW_KEY_KP_2: case GLFW_KEY_KP_2:
rot_pitch -= 1; parent->powered = !parent->powered;
break; break;
case GLFW_KEY_KP_3: case GLFW_KEY_KP_3:
parent->camera_at = (parent->camera_at + 1) % parent->cameras.size(); parent->camera_at = (parent->camera_at + 1) % parent->cameras.size();
@ -72,7 +73,7 @@ public:
rot_yaw += 1; rot_yaw += 1;
break; break;
case GLFW_KEY_KP_5: case GLFW_KEY_KP_5:
parent->powered = !parent->powered; rot_pitch -= 1;
break; break;
case GLFW_KEY_KP_6: case GLFW_KEY_KP_6:
rot_yaw -= 1; rot_yaw -= 1;
@ -93,12 +94,12 @@ public:
{ {
switch(key) switch(key)
{ {
case GLFW_KEY_KP_2:
rot_pitch += 1;
break;
case GLFW_KEY_KP_4: case GLFW_KEY_KP_4:
rot_yaw -= 1; rot_yaw -= 1;
break; break;
case GLFW_KEY_KP_5:
rot_pitch += 1;
break;
case GLFW_KEY_KP_6: case GLFW_KEY_KP_6:
rot_yaw += 1; rot_yaw += 1;
break; break;
@ -150,6 +151,7 @@ CCTV::CCTV(Model& model)
handle = glGetTextureHandleARB(texture); handle = glGetTextureHandleARB(texture);
glMakeTextureHandleResidentARB(handle); glMakeTextureHandleResidentARB(handle);
mat = model.load_matrix("translation_monitor_1");
m_screen.vertices = { m_screen.vertices = {
{.texpos={0, 1}, .pos={0, 0, 0}, .transform_id=0, .tex_diffuse=handle, .material={0, 0, 1}}, {.texpos={0, 1}, .pos={0, 0, 0}, .transform_id=0, .tex_diffuse=handle, .material={0, 0, 1}},
{.texpos={0, 0}, .pos={0, 1, 0}, .transform_id=0, .tex_diffuse=handle, .material={0, 0, 1}}, {.texpos={0, 0}, .pos={0, 1, 0}, .transform_id=0, .tex_diffuse=handle, .material={0, 0, 1}},
@ -157,7 +159,7 @@ CCTV::CCTV(Model& model)
{.texpos={1, 0}, .pos={1, 1, 0}, .transform_id=0, .tex_diffuse=handle, .material={0, 0, 1}}, {.texpos={1, 0}, .pos={1, 1, 0}, .transform_id=0, .tex_diffuse=handle, .material={0, 0, 1}},
}; };
m_screen.indices = {0, 1, 3, 0, 3, 2}; m_screen.indices = {0, 1, 3, 0, 3, 2};
m_screen.transforms = {model.load_matrix("translation_monitor_1")}; m_screen.transforms = {mat};
m_screen.bake_transforms(); m_screen.bake_transforms();
gm_screen.bind(); gm_screen.bind();
@ -194,12 +196,50 @@ CCTV::CCTV(CCTV&& o)
void CCTV::rotate(double dt, float pitch, float yaw) void CCTV::rotate(double dt, float pitch, float yaw)
{ {
Data::Camera& active = cameras[camera_at]; Data::Camera& active = cameras[camera_at];
float m = float(M_PI) * dt * 0.5f / active.zoom; 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.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); active.yaw = Util::Math::clamp(active.yaw + yaw * m, -M_PI / 4, M_PI / 4);
} }
void CCTV::remesh_slow(Data::Mesh& rmesh)
{
if(!powered)
{
return;
}
const Data::Camera& active = cameras[camera_at];
std::stringstream ss;
ss << "- ";
for(int i = 0; i < cameras.size(); i++)
{
if(i == camera_at)
{
ss << "[" << cameras[i].name << "] ";
}
else
{
ss << " " << cameras[i].name << " ";
}
}
ss << "-\n";
rmesh.add(Data::Fonts::MONO.load_text(ss.str(), 0.02, {0.5, 0}), glm::translate(mat, {0.5, 0.95, 0}), true);
char zoom_chars[] = " ";
zoom_chars[(int)std::round(Util::Math::ramp(active.zoom, 1, 0.25, 0, 9))] = '#';
ss.str("");
ss << "Zoom: [" << zoom_chars << "]";
rmesh.add(Data::Fonts::MONO.load_text(ss.str(), 0.02), glm::translate(mat, {0.0125, 0.0125, 0}), true);
}
void CCTV::update(double dt) void CCTV::update(double dt)
{ {
Data::Camera& active = cameras[camera_at]; Data::Camera& active = cameras[camera_at];
@ -207,21 +247,21 @@ void CCTV::update(double dt)
if(m_screen.check_focus()) if(m_screen.check_focus())
Focus::set(std::make_unique<FocusCCTV>(this)); Focus::set(std::make_unique<FocusCCTV>(this));
if(m_buttons[0].check_focus_hold()) 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); active.zoom = Util::Math::clamp(active.zoom - dt * 0.5f, 0.25, 1);
if(m_buttons[1].check_focus_hold()) if(m_buttons[1].check_focus_hold())
rotate(dt, 1, 0); rotate(dt, 1, 0);
if(m_buttons[2].check_focus_hold()) 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); active.zoom = Util::Math::clamp(active.zoom + dt * 0.5f, 0.25, 1);
if(m_buttons[3].check_focus_hold()) if(m_buttons[3].check_focus_hold())
rotate(dt, 0, -1); rotate(dt, 0, -1);
if(m_buttons[4].check_focus()) if(m_buttons[4].check_focus_hold())
powered = !powered; rotate(dt, -1, 0);
if(m_buttons[5].check_focus_hold()) if(m_buttons[5].check_focus_hold())
rotate(dt, 0, 1); rotate(dt, 0, 1);
if(m_buttons[6].check_focus()) if(m_buttons[6].check_focus())
camera_at = (camera_at + cameras.size() - 1) % cameras.size(); camera_at = (camera_at + cameras.size() - 1) % cameras.size();
if(m_buttons[7].check_focus_hold()) if(m_buttons[7].check_focus())
rotate(dt, -1, 0); powered = !powered;
if(m_buttons[8].check_focus()) if(m_buttons[8].check_focus())
camera_at = (camera_at + 1) % cameras.size(); camera_at = (camera_at + 1) % cameras.size();
} }
@ -239,7 +279,7 @@ void CCTV::render_view()
rot = glm::rotate(rot, active.pitch, right); 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 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::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()); glm::vec3 brightness = glm::vec3(System::active->grid.get_light_intensity());
glBindFramebuffer(GL_FRAMEBUFFER, fbo); glBindFramebuffer(GL_FRAMEBUFFER, fbo);

View File

@ -1,6 +1,8 @@
#pragma once #pragma once
#include <glm/matrix.hpp>
#include <vector> #include <vector>
#include <array> #include <array>
@ -27,6 +29,7 @@ class CCTV : public Data::MeshGen
public: public:
glm::mat4 mat;
std::vector<Data::Camera> cameras; std::vector<Data::Camera> cameras;
int camera_at = 0; int camera_at = 0;
bool powered = false; bool powered = false;
@ -37,6 +40,7 @@ public:
~CCTV(); ~CCTV();
void update(double dt) override; void update(double dt) override;
void remesh_slow(Data::Mesh& rmesh) override;
void rotate(double dt, float pitch, float yaw); void rotate(double dt, float pitch, float yaw);
void render_view(); void render_view();
void render_screen(); void render_screen();

View File

@ -8,6 +8,7 @@
#include "../data/texture.hpp" #include "../data/texture.hpp"
#include "../../system.hpp" #include "../../system.hpp"
#include "../../util/streams.hpp" #include "../../util/streams.hpp"
#include "../data/font.hpp"
#include <glm/ext/matrix_transform.hpp> #include <glm/ext/matrix_transform.hpp>
@ -66,7 +67,7 @@ struct CoreMonitor : public Focus::FocusType
sys.reactor.move_cursor(-1); sys.reactor.move_cursor(-1);
break; break;
case GLFW_KEY_KP_5: case GLFW_KEY_KP_5:
sys.reactor.toggle_selected(); sys.reactor.move_cursor(sys.reactor.height);
break; break;
case GLFW_KEY_KP_6: case GLFW_KEY_KP_6:
sys.reactor.move_cursor(1); sys.reactor.move_cursor(1);
@ -75,7 +76,7 @@ struct CoreMonitor : public Focus::FocusType
sys.reactor.reset_rod_speed(); sys.reactor.reset_rod_speed();
break; break;
case GLFW_KEY_KP_2: case GLFW_KEY_KP_2:
sys.reactor.move_cursor(sys.reactor.height); sys.reactor.toggle_selected();
break; break;
default: default:
return; return;
@ -135,9 +136,7 @@ Core::Core(const Model& model)
void Core::remesh_static(Mesh& rmesh) void Core::remesh_static(Mesh& rmesh)
{ {
Data::Mesh mesh; rmesh.add(Data::Fonts::BASE.load_text("Reactor Core", 0.04), mat, true);
mesh.load_text("Reactor Core", 0.04);
rmesh.add(mesh, mat, true);
} }
static Data::Mesh add_dot(glm::mat4 model_mat, glm::vec4 colour) static Data::Mesh add_dot(glm::mat4 model_mat, glm::vec4 colour)
@ -174,13 +173,13 @@ void Core::update(double dt)
if(m_buttons[3].check_focus()) if(m_buttons[3].check_focus())
sys.reactor.move_cursor(-1); sys.reactor.move_cursor(-1);
if(m_buttons[4].check_focus()) if(m_buttons[4].check_focus())
sys.reactor.toggle_selected(); sys.reactor.move_cursor(sys.reactor.height);
if(m_buttons[5].check_focus()) if(m_buttons[5].check_focus())
sys.reactor.move_cursor(1); sys.reactor.move_cursor(1);
if(m_buttons[6].check_focus()) if(m_buttons[6].check_focus())
sys.reactor.reset_rod_speed(); sys.reactor.reset_rod_speed();
if(m_buttons[7].check_focus()) if(m_buttons[7].check_focus())
sys.reactor.move_cursor(sys.reactor.height); sys.reactor.toggle_selected();
} }
void Core::remesh_slow(Mesh& rmesh) void Core::remesh_slow(Mesh& rmesh)

View File

@ -7,6 +7,7 @@
#include "../../coolant/valve.hpp" #include "../../coolant/valve.hpp"
#include "../input/focus.hpp" #include "../input/focus.hpp"
#include "../../util/streams.hpp" #include "../../util/streams.hpp"
#include "../data/font.hpp"
#include <glm/ext/matrix_transform.hpp> #include <glm/ext/matrix_transform.hpp>
#include <iostream> #include <iostream>
@ -67,7 +68,6 @@ PrimaryLoop::PrimaryLoop(const Model& model)
void PrimaryLoop::remesh_static(Mesh& rmesh) void PrimaryLoop::remesh_static(Mesh& rmesh)
{ {
std::stringstream ss; std::stringstream ss;
Data::Mesh mesh;
ss << "Turbine Bypass Valve\n\n"; ss << "Turbine Bypass Valve\n\n";
ss << "Opened\nFlow\nSetpoint\n\n"; ss << "Opened\nFlow\nSetpoint\n\n";
@ -81,8 +81,7 @@ void PrimaryLoop::remesh_static(Mesh& rmesh)
ss << "Pressure\n"; ss << "Pressure\n";
ss << "Level\n"; ss << "Level\n";
mesh.load_text(ss.str().c_str(), 0.04); rmesh.add(Data::Fonts::BASE.load_text(ss.str(), 0.04), mat, true);
rmesh.add(mesh, mat, true);
rmesh.add(g_switch_pump); rmesh.add(g_switch_pump);
rmesh.add(g_switch_bypass); rmesh.add(g_switch_bypass);
@ -108,7 +107,6 @@ void PrimaryLoop::update(double dt)
void PrimaryLoop::remesh_slow(Mesh& rmesh) void PrimaryLoop::remesh_slow(Mesh& rmesh)
{ {
std::stringstream ss; std::stringstream ss;
Sim::Graphics::Data::Mesh mesh;
System& sys = *System::active; System& sys = *System::active;
ss << "\n\n"; ss << "\n\n";
@ -149,7 +147,7 @@ void PrimaryLoop::remesh_slow(Mesh& rmesh)
show_units( ss, sys.loop.condenser.get_pressure() ) << "Pa\n"; show_units( ss, sys.loop.condenser.get_pressure() ) << "Pa\n";
ss << show( sys.loop.condenser.get_level() / 1000 ) << " / " << show( sys.loop.condenser.get_volume() / 1000 ) << " kL\n"; ss << show( sys.loop.condenser.get_level() / 1000 ) << " / " << show( sys.loop.condenser.get_volume() / 1000 ) << " kL\n";
mesh.load_text(ss.str().c_str(), 0.04); Mesh mesh = Data::Fonts::BASE.load_text(ss.str(), 0.04);
rmesh.add(mesh, glm::translate(mat, glm::vec3(0.5, 0, 0))); rmesh.add(mesh, glm::translate(mat, glm::vec3(0.5, 0, 0)));
} }

View File

@ -7,6 +7,7 @@
#include "../../coolant/valve.hpp" #include "../../coolant/valve.hpp"
#include "../input/focus.hpp" #include "../input/focus.hpp"
#include "../../util/streams.hpp" #include "../../util/streams.hpp"
#include "../data/font.hpp"
#include <glm/ext/matrix_transform.hpp> #include <glm/ext/matrix_transform.hpp>
#include <iostream> #include <iostream>
@ -42,7 +43,6 @@ void SecondaryLoop::update(double dt)
void SecondaryLoop::remesh_static(Mesh& rmesh) void SecondaryLoop::remesh_static(Mesh& rmesh)
{ {
std::stringstream ss; std::stringstream ss;
Data::Mesh mesh;
ss << "Cooling Tower\n\n"; ss << "Cooling Tower\n\n";
ss << "Heat\nSteam\nPressure\nLevel\n\n"; ss << "Heat\nSteam\nPressure\nLevel\n\n";
@ -51,8 +51,7 @@ void SecondaryLoop::remesh_static(Mesh& rmesh)
ss << "Freight Pump\n\n"; ss << "Freight Pump\n\n";
ss << "Power\nSpeed\nFlow\n\n"; ss << "Power\nSpeed\nFlow\n\n";
mesh.load_text(ss.str().c_str(), 0.04); rmesh.add(Data::Fonts::BASE.load_text(ss.str(), 0.04), mat, true);
rmesh.add(mesh, mat, true);
rmesh.add(g_switch_2); rmesh.add(g_switch_2);
rmesh.add(g_switch_3); rmesh.add(g_switch_3);
} }
@ -60,7 +59,6 @@ void SecondaryLoop::remesh_static(Mesh& rmesh)
void SecondaryLoop::remesh_slow(Mesh& rmesh) void SecondaryLoop::remesh_slow(Mesh& rmesh)
{ {
std::stringstream ss; std::stringstream ss;
Sim::Graphics::Data::Mesh mesh;
System& sys = *System::active; System& sys = *System::active;
ss << "\n\n"; ss << "\n\n";
@ -77,7 +75,7 @@ void SecondaryLoop::remesh_slow(Mesh& rmesh)
ss << show( sys.freight_pump.get_rpm() ) << " r/min\n"; ss << show( sys.freight_pump.get_rpm() ) << " r/min\n";
show_units( ss, sys.freight_pump.get_flow_mass() ) << "g/s\n"; show_units( ss, sys.freight_pump.get_flow_mass() ) << "g/s\n";
mesh.load_text(ss.str().c_str(), 0.04); Mesh mesh = Data::Fonts::BASE.load_text(ss.str().c_str(), 0.04);
rmesh.add(mesh, glm::translate(mat, glm::vec3(0.5, 0, 0))); rmesh.add(mesh, glm::translate(mat, glm::vec3(0.5, 0, 0)));
} }

View File

@ -8,6 +8,7 @@
#include "../input/focus.hpp" #include "../input/focus.hpp"
#include "../../util/streams.hpp" #include "../../util/streams.hpp"
#include "../../util/math.hpp" #include "../../util/math.hpp"
#include "../data/font.hpp"
#include <glm/ext/matrix_transform.hpp> #include <glm/ext/matrix_transform.hpp>
#include <iostream> #include <iostream>
@ -69,13 +70,11 @@ void Turbine::get_static_transforms(std::vector<glm::mat4>& transforms)
void Turbine::remesh_static(Mesh& rmesh) void Turbine::remesh_static(Mesh& rmesh)
{ {
std::stringstream ss; std::stringstream ss;
Sim::Graphics::Data::Mesh mesh;
ss << "Turbine\n\n"; ss << "Turbine\n\n";
ss << "Heat\nPressure\nSpeed\n\n"; ss << "Heat\nPressure\nSpeed\n\n";
mesh.load_text(ss.str().c_str(), 0.04); rmesh.add(Data::Fonts::BASE.load_text(ss.str(), 0.04), mat, true);
rmesh.add(mesh, mat, true);
rmesh.add(g_dial_phase); rmesh.add(g_dial_phase);
rmesh.add(g_dial_voltage); rmesh.add(g_dial_voltage);
@ -87,7 +86,6 @@ void Turbine::remesh_static(Mesh& rmesh)
void Turbine::remesh_slow(Mesh& rmesh) void Turbine::remesh_slow(Mesh& rmesh)
{ {
std::stringstream ss; std::stringstream ss;
Sim::Graphics::Data::Mesh mesh;
System& sys = *System::active; System& sys = *System::active;
ss << "\n\n"; ss << "\n\n";
@ -95,7 +93,7 @@ void Turbine::remesh_slow(Mesh& rmesh)
ss << show( sys.loop.turbine.get_pressure() / 1000 ) << " kPa\n"; ss << show( sys.loop.turbine.get_pressure() / 1000 ) << " kPa\n";
ss << show( sys.loop.generator.get_rpm() ) << " r/min\n"; ss << show( sys.loop.generator.get_rpm() ) << " r/min\n";
mesh.load_text(ss.str().c_str(), 0.04); Mesh mesh = Data::Fonts::BASE.load_text(ss.str(), 0.04);
rmesh.add(mesh, glm::translate(mat, glm::vec3(0.5, 0, 0))); rmesh.add(mesh, glm::translate(mat, glm::vec3(0.5, 0, 0)));
} }

View File

@ -7,6 +7,7 @@
#include "../../reactor/control/boron_rod.hpp" #include "../../reactor/control/boron_rod.hpp"
#include "../../system.hpp" #include "../../system.hpp"
#include "../../util/streams.hpp" #include "../../util/streams.hpp"
#include "../data/font.hpp"
#include <glm/ext/matrix_transform.hpp> #include <glm/ext/matrix_transform.hpp>
#include <sstream> #include <sstream>
@ -23,7 +24,6 @@ Vessel::Vessel(const Model& model)
void Vessel::remesh_static(Mesh& rmesh) void Vessel::remesh_static(Mesh& rmesh)
{ {
std::stringstream ss; std::stringstream ss;
Sim::Graphics::Data::Mesh mesh;
ss << "Reactor Vessel\n\n"; ss << "Reactor Vessel\n\n";
ss << "Heat\n"; ss << "Heat\n";
@ -37,14 +37,12 @@ void Vessel::remesh_static(Mesh& rmesh)
ss << "Temperature\nMin\nMax\n\n"; ss << "Temperature\nMin\nMax\n\n";
ss << "Control Rods\nMin\nMax\nSpeed\n"; ss << "Control Rods\nMin\nMax\nSpeed\n";
mesh.load_text(ss.str().c_str(), 0.04); rmesh.add(Data::Fonts::BASE.load_text(ss.str(), 0.04), mat, true);
rmesh.add(mesh, mat, true);
} }
void Vessel::remesh_slow(Mesh& rmesh) void Vessel::remesh_slow(Mesh& rmesh)
{ {
std::stringstream ss; std::stringstream ss;
Sim::Graphics::Data::Mesh mesh;
Sim::System& sys = *System::active; Sim::System& sys = *System::active;
double temp_min, temp_max; double temp_min, temp_max;
@ -92,7 +90,7 @@ void Vessel::remesh_slow(Mesh& rmesh)
if(sys.reactor.rod_speed == 0) ss << " (Stopped)"; if(sys.reactor.rod_speed == 0) ss << " (Stopped)";
ss << "\n"; ss << "\n";
mesh.load_text(ss.str().c_str(), 0.04); Mesh mesh = Data::Fonts::BASE.load_text(ss.str(), 0.04);
rmesh.add(mesh, glm::translate(mat, glm::vec3(0.5, 0, 0))); rmesh.add(mesh, glm::translate(mat, glm::vec3(0.5, 0, 0)));
} }

View File

@ -8,6 +8,7 @@
#include <format> #include <format>
#include "shader.hpp" #include "shader.hpp"
#include "shadersource.hpp"
#include "window.hpp" #include "window.hpp"
using namespace Sim::Graphics; using namespace Sim::Graphics;
@ -16,40 +17,54 @@ Shader Shader::MAIN;
Shader Shader::LIGHT; Shader Shader::LIGHT;
Shader* Shader::ACTIVE; Shader* Shader::ACTIVE;
static std::string read_shader(const char* path) bool Shader::USE_BINDLESS_TEXTURES = false;
static std::vector<std::string> shader_compiler_flags;
void Shader::add_define(const std::string& flag)
{
shader_compiler_flags.push_back(flag);
}
void Shader::init()
{
Shader::Source sources_main[] = {
{ShaderSource::MAIN_VSH, "main.vsh", GL_VERTEX_SHADER},
{ShaderSource::MAIN_FSH, "main.fsh", GL_FRAGMENT_SHADER}
};
Shader::Source sources_light[] = {
{ShaderSource::LIGHT_VSH, "light.vsh", GL_VERTEX_SHADER},
{ShaderSource::LIGHT_GSH, "light.gsh", GL_GEOMETRY_SHADER},
{ShaderSource::LIGHT_FSH, "light.fsh", GL_FRAGMENT_SHADER}
};
Shader::MAIN.load(sources_main, "main", 2);
Shader::LIGHT.load(sources_light, "light", 3);
}
std::string apply_shader_compiler_flags(const char* source)
{ {
std::stringstream ss; std::stringstream ss;
std::ifstream file(path, std::ios::binary); ss << "#version 430 core\n";
char buff[1024];
if(!file.is_open()) for(const auto& flag : shader_compiler_flags)
{ {
throw std::runtime_error(std::format("Shader Read Error: {0}", path)); ss << "#define " << flag << "\n";
}
while(!file.eof())
{
file.read(buff, 1024);
ss.write(buff, file.gcount());
} }
ss << source;
return ss.str(); return ss.str();
} }
static std::string read_shader(const char* base, const char* file) Shader::Source::Source(const char* data, const char* name, GLenum type)
{
std::string path = std::string(base) + "/" + std::string(file);
return read_shader(path.c_str());
}
Shader::Source::Source(const char* path, GLenum type)
{ {
int success; int success;
std::string src = read_shader(path); std::string source = apply_shader_compiler_flags(data);
const char* c_src = src.c_str(); data = source.c_str();
id = glCreateShader(type); id = glCreateShader(type);
glShaderSource(id, 1, &c_src, nullptr); glShaderSource(id, 1, &data, nullptr);
glCompileShader(id); glCompileShader(id);
glGetShaderiv(id, GL_COMPILE_STATUS, &success); glGetShaderiv(id, GL_COMPILE_STATUS, &success);
@ -57,7 +72,7 @@ Shader::Source::Source(const char* path, GLenum type)
{ {
char infoLog[512]; char infoLog[512];
glGetShaderInfoLog(id, 512, NULL, infoLog); glGetShaderInfoLog(id, 512, NULL, infoLog);
std::string entry = std::format("Shader Compile Error ({0}): {1}", path, infoLog); std::string entry = std::format("Shader Compile Error ({0}): {1}", name, infoLog);
throw std::runtime_error(entry); throw std::runtime_error(entry);
} }
} }
@ -92,7 +107,7 @@ Shader::~Shader()
} }
} }
void Shader::load(const Source* sources, int count) void Shader::load(const Source* sources, const char* name, int count)
{ {
int success; int success;
prog_id = glCreateProgram(); prog_id = glCreateProgram();
@ -109,7 +124,7 @@ void Shader::load(const Source* sources, int count)
{ {
char infoLog[512]; char infoLog[512];
glGetProgramInfoLog(prog_id, 512, NULL, infoLog); glGetProgramInfoLog(prog_id, 512, NULL, infoLog);
std::string entry = std::format("Shader Link Error: {0}", infoLog); std::string entry = std::format("Shader Link Error {0}: {1}", name, infoLog);
throw std::runtime_error(entry); throw std::runtime_error(entry);
} }
} }

View File

@ -4,6 +4,7 @@
#include <GL/glew.h> #include <GL/glew.h>
#include <unordered_map> #include <unordered_map>
#include <string>
namespace Sim::Graphics namespace Sim::Graphics
{ {
@ -22,7 +23,7 @@ public:
{ {
unsigned int id; unsigned int id;
Source(const char* path, GLenum type); Source(const char* data, const char* name, GLenum type);
Source(const Source& o) = delete; Source(const Source& o) = delete;
Source(Source&& o); Source(Source&& o);
~Source(); ~Source();
@ -33,12 +34,17 @@ public:
static Shader* ACTIVE; static Shader* ACTIVE;
static bool USE_BINDLESS_TEXTURES;
Shader(); Shader();
Shader(const Shader& o) = delete; Shader(const Shader& o) = delete;
Shader(Shader&& o); Shader(Shader&& o);
~Shader(); ~Shader();
void load(const Source* sources, int count); static void add_define(const std::string& flag);
static void init();
void load(const Source* sources, const char* name, int count);
void block_binding(const char* name, unsigned int index); void block_binding(const char* name, unsigned int index);
void use(); void use();

View File

@ -1,5 +1,4 @@
R"GLSL(
#version 460 core
in vec3 frag_pos; in vec3 frag_pos;
@ -11,3 +10,4 @@ void main()
gl_FragDepth = distance / far_plane; gl_FragDepth = distance / far_plane;
} }
)GLSL";

View File

@ -1,5 +1,4 @@
R"GLSL(
#version 460 core
layout (triangles) in; layout (triangles) in;
layout (triangle_strip, max_vertices=18) out; layout (triangle_strip, max_vertices=18) out;
@ -33,3 +32,4 @@ void main()
} }
} }
)GLSL";

View File

@ -1,11 +1,8 @@
R"GLSL(
#version 460 core
#define MAX_LIGHTS 6
layout (location = 1) in vec3 aPos; layout (location = 1) in vec3 aPos;
layout (location = 2) in vec4 aColour; layout (location = 2) in vec4 aColour;
layout (location = 6) in int aTransformIndex; layout (location = 7) in int aTransformIndex;
layout (binding = 3) readonly buffer TransformBuffer layout (binding = 3) readonly buffer TransformBuffer
{ {
@ -31,3 +28,4 @@ void main()
should_ignore = int(aColour.a < 1.f); should_ignore = int(aColour.a < 1.f);
} }
)GLSL";

View File

@ -1,6 +1,8 @@
R"GLSL(
#version 460 core #ifdef USE_BINDLESS_TEXTURES
#extension GL_ARB_bindless_texture : require #extension GL_ARB_bindless_texture : require
#endif
const float PI = 3.141592f; const float PI = 3.141592f;
@ -23,9 +25,40 @@ layout(std140, binding = 2) readonly buffer LightBuffer
Light lights[]; Light lights[];
}; };
#ifdef USE_BINDLESS_TEXTURES
in flat sampler2D frag_tex_diffuse; in flat sampler2D frag_tex_diffuse;
in flat sampler2D frag_tex_normal; in flat sampler2D frag_tex_normal;
#define ReadTexture(tex, uv) texture(tex, uv)
#else
in flat uint frag_tex_diffuse;
in flat uint frag_tex_normal;
uniform sampler2D tex_atlas;
struct AtlasPart
{
vec2 uv_min;
vec2 uv_max;
};
layout(std140, binding = 5) readonly buffer AtlasBuffer
{
AtlasPart atlas[];
};
vec4 ReadTexture(uint tex, vec2 uv)
{
AtlasPart a = atlas[tex];
uv = mod(uv, 1.f) * (a.uv_max - a.uv_min) + a.uv_min;
return texture(tex_atlas, uv);
}
#endif
out vec4 frag_colour; out vec4 frag_colour;
uniform vec3 brightness; uniform vec3 brightness;
@ -107,10 +140,10 @@ vec3 sRGB_To_LinRGB(vec3 c)
void main() void main()
{ {
vec4 albedo = texture2D(frag_tex_diffuse, vin.tex_pos); vec4 albedo = ReadTexture(frag_tex_diffuse, vin.tex_pos);
if(albedo.a == 0.f) discard; if(albedo.a == 0.f) discard;
vec3 tangent = texture2D(frag_tex_normal, vin.tex_pos).rgb * 2.f - 1.f; vec3 tangent = ReadTexture(frag_tex_normal, vin.tex_pos).rgb * 2.f - 1.f;
vec3 albedo_lin = sRGB_To_LinRGB(albedo.rgb) * vin.colour.rgb; vec3 albedo_lin = sRGB_To_LinRGB(albedo.rgb) * vin.colour.rgb;
albedo *= vin.colour; albedo *= vin.colour;
@ -180,3 +213,4 @@ void main()
frag_colour = vec4(light, albedo.a); frag_colour = vec4(light, albedo.a);
} }
)GLSL";

View File

@ -1,6 +1,8 @@
R"GLSL(
#version 460 core #ifdef USE_BINDLESS_TEXTURES
#extension GL_ARB_bindless_texture : require #extension GL_ARB_bindless_texture : require
#endif
layout (location = 0) in vec2 aTexPos; layout (location = 0) in vec2 aTexPos;
layout (location = 1) in vec3 aPos; layout (location = 1) in vec3 aPos;
@ -8,10 +10,26 @@ layout (location = 2) in vec4 aColour;
layout (location = 3) in vec3 aTangent; layout (location = 3) in vec3 aTangent;
layout (location = 4) in vec3 aBitangent; layout (location = 4) in vec3 aBitangent;
layout (location = 5) in vec3 aNormal; layout (location = 5) in vec3 aNormal;
layout (location = 6) in int aTransformIndex; layout (location = 6) in vec3 aMaterial;
layout (location = 7) in sampler2D aTexDiffuse; layout (location = 7) in int aTransformIndex;
layout (location = 8) in sampler2D aTexNormal;
layout (location = 9) in vec3 aMaterial; #ifdef USE_BINDLESS_TEXTURES
layout (location = 8) in sampler2D aTexDiffuse;
layout (location = 9) in sampler2D aTexNormal;
out flat sampler2D frag_tex_diffuse;
out flat sampler2D frag_tex_normal;
#else
layout (location = 8) in uint aTexDiffuse;
layout (location = 9) in uint aTexNormal;
out flat uint frag_tex_diffuse;
out flat uint frag_tex_normal;
#endif
uniform mat4 camera; uniform mat4 camera;
uniform mat4 projection; uniform mat4 projection;
@ -29,9 +47,6 @@ out VS_OUT {
flat vec3 material; flat vec3 material;
} vout; } vout;
out flat sampler2D frag_tex_diffuse;
out flat sampler2D frag_tex_normal;
mat4 load_model_mat(int index) mat4 load_model_mat(int index)
{ {
return index < 0 ? mat4(1.f) : transforms[index]; return index < 0 ? mat4(1.f) : transforms[index];
@ -60,3 +75,4 @@ void main()
gl_Position = mvp * pos; gl_Position = mvp * pos;
} }
)GLSL";

View File

@ -0,0 +1,17 @@
#include "shadersource.hpp"
using namespace Sim::Graphics;
const char* ShaderSource::LIGHT_VSH =
#include "shaders/light.vsh"
const char* ShaderSource::LIGHT_GSH =
#include "shaders/light.gsh"
const char* ShaderSource::LIGHT_FSH =
#include "shaders/light.fsh"
const char* ShaderSource::MAIN_VSH =
#include "shaders/main.vsh"
const char* ShaderSource::MAIN_FSH =
#include "shaders/main.fsh"

View File

@ -0,0 +1,12 @@
#pragma once
namespace Sim::Graphics::ShaderSource
{
extern const char* LIGHT_VSH;
extern const char* LIGHT_GSH;
extern const char* LIGHT_FSH;
extern const char* MAIN_VSH;
extern const char* MAIN_FSH;
};

View File

@ -27,7 +27,6 @@ void Clock::update(double dt)
void Clock::remesh_slow(Mesh& rmesh) void Clock::remesh_slow(Mesh& rmesh)
{ {
Mesh m;
double at = System::active->clock; double at = System::active->clock;
glm::vec2 wsize(Resize::get_size() / 2); glm::vec2 wsize(Resize::get_size() / 2);
std::stringstream ss; std::stringstream ss;
@ -42,7 +41,7 @@ void Clock::remesh_slow(Mesh& rmesh)
ss << std::setfill('0') << std::setw(2) << t_s << "\n"; ss << std::setfill('0') << std::setw(2) << t_s << "\n";
ss << "Day: " << std::floor(at / (3600 * 24)) << "\n"; ss << "Day: " << std::floor(at / (3600 * 24)) << "\n";
m.load_text(ss.str().c_str(), 20); Mesh m = Fonts::BASE.load_text(ss.str(), 20);
rmesh.add(m, glm::translate(glm::mat4(1), glm::vec3(-wsize + glm::vec2(2, 2), 0))); rmesh.add(m, glm::translate(glm::mat4(1), glm::vec3(-wsize + glm::vec2(2, 2), 0)));
} }

View File

@ -105,7 +105,7 @@ void Window::create()
{ {
glfwInit(); glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4); glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 6); glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, true); glfwWindowHint(GLFW_OPENGL_DEBUG_CONTEXT, true);
glfwWindowHint(GLFW_VISIBLE, false); glfwWindowHint(GLFW_VISIBLE, false);
@ -129,17 +129,15 @@ void Window::create()
return; return;
} }
if(!glGetTextureHandleARB || !glMakeTextureHandleResidentARB) if(glGetTextureHandleARB && glMakeTextureHandleResidentARB)
{ {
std::cerr << "Fatal: Bindless textures not supported\n"; Shader::add_define("USE_BINDLESS_TEXTURES");
Shader::USE_BINDLESS_TEXTURES = true;
}
if(!glGetTextureHandleARB) else
std::cerr << " Missing: glGetTextureHandleARB\n"; {
if(!glMakeTextureHandleResidentARB) std::cout << "Warning: Bindless textures are not supported. Using texture atlas instead.\n";
std::cerr << " Missing: glMakeTextureHandleResidentARB\n";
close();
return;
} }
glEnable(GL_MULTISAMPLE); glEnable(GL_MULTISAMPLE);
@ -156,21 +154,9 @@ void Window::create()
Mouse::init(); Mouse::init();
Resize::init(); Resize::init();
Texture::init(); Texture::init();
Font::init(); Fonts::init();
UI::init(); UI::init();
Shader::init();
// 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(); Shader::MAIN.use();
glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindFramebuffer(GL_FRAMEBUFFER, 0);
@ -204,6 +190,8 @@ void Window::create()
equipment.push_back(new Equipment::Generator(model)); equipment.push_back(new Equipment::Generator(model));
equipment.push_back(new Equipment::Pool(model)); equipment.push_back(new Equipment::Pool(model));
Texture::generate_atlas();
remesh_static(); remesh_static();
glfwShowWindow(win); glfwShowWindow(win);

View File

@ -0,0 +1,4 @@
#define STB_RECT_PACK_IMPLEMENTATION
#include <stb/stb_rect_pack.h>

View File

@ -48,5 +48,11 @@ constexpr A mod(A a, auto b)
return v; return v;
} }
template <typename A>
constexpr A ramp(A v, auto imin, auto imax, auto omin, auto omax)
{
return clamp(map(v, imin, imax, omin, omax), omin, omax);
}
}; };