Compare commits

...

5 Commits

Author SHA1 Message Date
2aed98d688 Some refactoring and better usage of text 2021-05-04 12:22:23 +02:00
10138d5fd8 Add draft for text rendering 2021-05-04 12:07:56 +02:00
307d000253 Rename String::get_length 2021-05-04 11:44:54 +02:00
482116018f Add Parallax Demo + some bugfixes
The initialization order in the Application was wrong, so some code has been moved from run to the constructor.
2021-05-03 22:23:15 +02:00
3dacb0c095 Add ability for client to override fixed and dynamic update 2021-05-03 21:33:38 +02:00
14 changed files with 1067 additions and 734 deletions

View File

@ -10,12 +10,16 @@ def add_strict_compile_flags(env):
env.Append(CCFLAGS=["-Wall", "-Wextra", "-pedantic", "-std=c++17"])
def add_debug_compile_flags(env):
env.Append(CCFLAGS=["-g"])
# Create the environment and create a Compilation Database for use in VSCodium
env = Environment(tools=['default', 'compilation_db'])
env.CompilationDatabase()
env.Append(CPPPATH=['cpp/', 'include/'])
env.Append(LIBS=['glfw', 'dl'])
env.Append(LIBS=['glfw', 'dl', 'freetype'])
add_third_party_includes(env)
add_strict_compile_flags(env)
@ -45,9 +49,11 @@ testEnv.Append(LINKFLAGS=[
add_third_party_includes(testEnv)
add_strict_compile_flags(testEnv)
add_debug_compile_flags(testEnv)
# Build the test programs
catch_cpp = "test/catch_amalgamated.cpp"
testEnv.Program('test/bin/vector-test.out', [catch_cpp, 'test/vector/vector-test.cpp'])
testEnv.Program('test/bin/test-app.out', Glob('test/test-app/*.cpp'))
testEnv.Program('test/bin/parallax-demo.out', Glob('test/parallax-demo/*.cpp'))

View File

@ -6,12 +6,16 @@
namespace Gedeng {
void Application::run() {
Application::Application(unsigned long ms_per_update, unsigned int window_size_x, unsigned int window_size_y,
String window_name)
: MS_PER_UPDATE(ms_per_update), WINDOW_SIZE_X(window_size_x), WINDOW_SIZE_Y(window_size_y),
WINDOW_NAME(window_name) {
// Setup Rendering
// FIXME: Make these parameters variable, maybe move this to a different function
RenderBackend::initialize_window(1920, 1080, String("Application"));
RenderBackend::initialize_window(WINDOW_SIZE_X, WINDOW_SIZE_Y, WINDOW_NAME);
Input::initialize(RenderBackend::get_window());
}
void Application::run() {
unsigned long previous_time_ms = Time::get_time_ms();
unsigned long time_until_fixed_update_ms = 0.0;
@ -26,13 +30,13 @@ void Application::run() {
// Update fixed time step
while (time_until_fixed_update_ms >= MS_PER_UPDATE) {
// Fixed Update
GG_CORE_INFO("Fixed Update");
fixed_update(static_cast<double>(MS_PER_UPDATE) / 1000.0);
time_until_fixed_update_ms -= MS_PER_UPDATE;
}
// Variable update
GG_CORE_INFO("Variable Update");
dynamic_update(static_cast<double>(elapsed_time_ms) / 1000.0);
RenderBackend::render();
}
}

View File

@ -15,6 +15,11 @@ void RenderBackend::initialize_window(unsigned int width, unsigned int height, S
window = glfwCreateWindow(width, height, title, NULL, NULL);
glfwMakeContextCurrent(window);
gladLoadGLLoader((GLADloadproc)glfwGetProcAddress);
glEnable(GL_DEPTH_TEST);
glEnable(GL_CULL_FACE);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
}
bool RenderBackend::is_window_created() {

View File

@ -5,13 +5,11 @@ namespace Gedeng {
String::String() : String("") {
}
String::String(const char *stringText)
: length(strlen(stringText)), text(new char[length + sizeof(char)]) {
String::String(const char *stringText) : length(strlen(stringText)), text(new char[length + sizeof(char)]) {
memcpy(text, stringText, length + sizeof(char));
}
String::String(const String &other)
: length(other.getLength()), text(new char[length + sizeof(char)]) {
String::String(const String &other) : length(other.get_length()), text(new char[length + sizeof(char)]) {
memcpy(text, other.c_str(), length + sizeof(char));
}
@ -116,7 +114,7 @@ const char *String::c_str() const {
return text;
}
size_t String::getLength() const {
size_t String::get_length() const {
return length;
}

132
cpp/TextLabel.cpp Normal file
View File

@ -0,0 +1,132 @@
// Must be the first include
#include <glad/glad.h>
// Other includes
#include <GLFW/glfw3.h>
#include <glm/ext/matrix_clip_space.hpp>
#include <glm/gtc/type_ptr.hpp>
#include "Gedeng/Logger.h"
#include "Gedeng/TextLabel.h"
namespace Gedeng {
TextLabel::TextLabel(const String &text) : text(text), shader(Shader("Shader/text.vs", "Shader/text.fs")) {
FT_Library ft;
if (FT_Init_FreeType(&ft)) {
GG_CORE_ERROR("Couldn't create font library!");
return;
}
FT_Face face;
if (FT_New_Face(ft, "Resources/Fonts/Inter.ttf", 0, &face)) {
GG_CORE_ERROR("Couldn't load font from path!");
return;
}
// Set size
FT_Set_Pixel_Sizes(face, 0, 12);
// Load first 128 ASCII Characters
glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // disable byte-alignment restriction
for (unsigned char c = 0; c < 128; c++) {
// load character glyph
if (FT_Load_Char(face, c, FT_LOAD_RENDER)) {
GG_CORE_ERROR("Couldn't load character glyph!");
continue;
}
// generate texture
unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, face->glyph->bitmap.width, face->glyph->bitmap.rows, 0, GL_RED,
GL_UNSIGNED_BYTE, face->glyph->bitmap.buffer);
// set texture options
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);
// now store character for later use
Character character = {texture, glm::ivec2(face->glyph->bitmap.width, face->glyph->bitmap.rows),
glm::ivec2(face->glyph->bitmap_left, face->glyph->bitmap_top),
static_cast<unsigned int>(face->glyph->advance.x)};
characters.insert(std::pair<char, Character>(c, character));
}
glBindTexture(GL_TEXTURE_2D, 0);
// destroy FreeType once we're finished
FT_Done_Face(face);
FT_Done_FreeType(ft);
// configure VAO/VBO for texture quads
// -----------------------------------
glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo);
glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 6 * 4, NULL, GL_DYNAMIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 4 * sizeof(float), 0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindVertexArray(0);
valid = true;
}
void TextLabel::set_text(const String &text) {
this->text = text;
}
void TextLabel::render_text(float x, float y, float scale, Vector3 color) {
// activate corresponding render state
shader.use();
glUniform3f(glGetUniformLocation(shader.ID, "textColor"), color.x, color.y, color.z);
// FIXME: We need the screen size here... (or UI module size?)
glm::mat4 projection = glm::ortho(0.0f, static_cast<float>(900), 0.0f, static_cast<float>(600));
glUniformMatrix4fv(glGetUniformLocation(shader.ID, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
glActiveTexture(GL_TEXTURE0);
glBindVertexArray(vao);
// iterate through all characters
for (size_t i = 0; i < text.get_length(); i++) {
Character ch = characters[text[i]];
float xpos = x + ch.bearing.x * scale;
float ypos = y - (ch.size.y - ch.bearing.y) * scale;
float w = ch.size.x * scale;
float h = ch.size.y * scale;
// update VBO for each character
float vertices[6][4] = {
{xpos, ypos + h, 0.0f, 0.0f}, {xpos, ypos, 0.0f, 1.0f}, {xpos + w, ypos, 1.0f, 1.0f},
{xpos, ypos + h, 0.0f, 0.0f}, {xpos + w, ypos, 1.0f, 1.0f}, {xpos + w, ypos + h, 1.0f, 0.0f}};
// render glyph texture over quad
glBindTexture(GL_TEXTURE_2D, ch.texture_id);
// update content of VBO memory
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferSubData(GL_ARRAY_BUFFER, 0, sizeof(vertices),
vertices); // be sure to use glBufferSubData and not glBufferData
glBindBuffer(GL_ARRAY_BUFFER, 0);
// render quad
glDrawArrays(GL_TRIANGLES, 0, 6);
// now advance cursors for next glyph (note that advance is number of 1/64 pixels)
x += (ch.advance >> 6) * scale; // bitshift by 6 to get value in pixels (2^6 = 64 (divide amount of 1/64th
// pixels by 64 to get amount of pixels))
}
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_2D, 0);
}
bool TextLabel::is_valid() {
return valid;
}
} // namespace Gedeng

1413
cpp/vendor/glad.c vendored

File diff suppressed because it is too large Load Diff

View File

@ -1,18 +1,34 @@
#pragma once
#include "String.h"
namespace Gedeng {
class Application {
public:
Application() = default;
Application(unsigned long ms_per_update, unsigned int window_size_x, unsigned int window_size_y,
String window_name);
// Virtual since this class will be inherited by user-created applications
virtual ~Application() = default;
// Game Loop
void run();
// Primarily for gameplay and physics
// To be overridden by client applications
virtual void fixed_update(double delta) = 0;
// Primarily for rendering
// To be overridden by client applications
virtual void dynamic_update(double delta) = 0;
private:
const unsigned long MS_PER_UPDATE = 20;
// TODO: These will probably become a separate Settings struct
const unsigned long MS_PER_UPDATE;
const unsigned int WINDOW_SIZE_X;
const unsigned int WINDOW_SIZE_Y;
const String WINDOW_NAME;
};
// To be defined in client applications

View File

@ -17,7 +17,8 @@ class Input {
glfwSetKeyCallback(window, key_callback);
}
// FIXME: Ignore warnings produced by these unused variables -- they're required for the callback to work
// FIXME: Ignore warnings produced by these unused variables -- they're required for the callback to work:
// https://gcc.gnu.org/onlinedocs/gcc/Diagnostic-Pragmas.html
static void key_callback(GLFWwindow *window, int key, int scancode, int action, int mods) {
if (key == GLFW_KEY_ESCAPE && action == GLFW_PRESS) glfwSetWindowShouldClose(window, GLFW_TRUE);

View File

@ -2,7 +2,11 @@
// Adapted from LearnOpenGL
// Must be the first include
#include <glad/glad.h>
// Other includes
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <fstream>
@ -152,21 +156,17 @@ class Shader {
if (!success) {
glGetShaderInfoLog(shader, 1024, NULL, infoLog);
std::cout << "ERROR::SHADER_COMPILATION_ERROR of type: " << type << "\n"
<< infoLog
<< "\n -- --------------------------------------------------- -- "
<< std::endl;
<< infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
}
} else {
glGetProgramiv(shader, GL_LINK_STATUS, &success);
if (!success) {
glGetProgramInfoLog(shader, 1024, NULL, infoLog);
std::cout << "ERROR::PROGRAM_LINKING_ERROR of type: " << type << "\n"
<< infoLog
<< "\n -- --------------------------------------------------- -- "
<< std::endl;
<< infoLog << "\n -- --------------------------------------------------- -- " << std::endl;
}
}
}
};
}
} // namespace Gedeng

View File

@ -48,7 +48,7 @@ class String {
const char *c_str() const;
/// Return the length of the String (without the terminator).
size_t getLength() const;
size_t get_length() const;
private:
size_t length;

View File

@ -0,0 +1,41 @@
#pragma once
#include <ft2build.h>
#include <glm/glm.hpp>
#include <map>
#include FT_FREETYPE_H
#include "Gedeng/Shader.h"
#include "Gedeng/String.h"
#include "Gedeng/Vector3.h"
namespace Gedeng {
class TextLabel {
public:
TextLabel(const String &text = String(""));
void set_text(const String &text);
void render_text(float x, float y, float scale, Vector3 color);
bool is_valid();
private:
struct Character {
unsigned int texture_id; // ID handle of the glyph texture
glm::ivec2 size; // Size of glyph
glm::ivec2 bearing; // Offset from baseline to left/top of glyph
unsigned int advance; // Offset to advance to next glyph
};
std::map<char, Character> characters;
unsigned int vao, vbo;
String text;
bool valid;
Shader shader;
};
} // namespace Gedeng

View File

@ -2,6 +2,14 @@
namespace Gedeng {
class Vector3 {};
class Vector3 {
public:
Vector3(float x, float y, float z) : x(x), y(y), z(z) {
}
float x;
float y;
float z;
};
} // namespace Gedeng

112
test/parallax-demo/main.cpp Normal file
View File

@ -0,0 +1,112 @@
#include "Gedeng/Logger.h"
#include "Gedeng/TextLabel.h"
#include "Gedeng/Vector3.h"
#define GEDENG_MAIN
#include <Gedeng.h>
class ParallaxApp : public Gedeng::Application {
public:
ParallaxApp(unsigned long ms_per_update, unsigned int window_size_x, unsigned int window_size_y,
Gedeng::String window_name)
: Application(ms_per_update, window_size_x, window_size_y, window_name), number_of_steps(10.0),
number_of_refinement_steps(10.0), bump_depth(0.1),
render_shader(Gedeng::Shader("Shader/bump.vs", "Shader/bump.fs")),
camera(Gedeng::Camera(90, 1920, 1080, 0.1, 1000.0)),
albedo("Resources/Textures/PavingStones/PavingStones070_2K_Color.jpg", Gedeng::Texture::Settings()),
bump("Resources/Textures/PavingStones/PavingStones070_2K_Displacement.jpg", Gedeng::Texture::Settings()),
normal("Resources/Textures/PavingStones/PavingStones070_2K_Normal.jpg", Gedeng::Texture::Settings()) {
// Move and rotate the camera so we see the quad well
camera.translate(glm::vec3(0.0, -1.0, 1.0));
camera.rotate(30, glm::vec3(1.0, 0.0, 0.0));
}
~ParallaxApp() = default;
void fixed_update(double delta) override {
// Settings for bump mapping
if (Gedeng::Input::is_key_down(GLFW_KEY_Q)) {
number_of_steps += delta * 5.0;
}
if (Gedeng::Input::is_key_down(GLFW_KEY_W)) {
number_of_steps -= delta * 5.0;
}
if (Gedeng::Input::is_key_down(GLFW_KEY_A)) {
number_of_refinement_steps += delta * 5.0;
}
if (Gedeng::Input::is_key_down(GLFW_KEY_S)) {
number_of_refinement_steps -= delta * 5.0;
}
if (Gedeng::Input::is_key_down(GLFW_KEY_Z)) {
bump_depth += delta * 0.1;
}
if (Gedeng::Input::is_key_down(GLFW_KEY_X)) {
bump_depth -= delta * 0.1;
}
Gedeng::String text;
text += std::to_string(number_of_steps).c_str();
text += " steps, ";
text += std::to_string(number_of_refinement_steps).c_str();
text += " refinement steps, ";
text += std::to_string(bump_depth).c_str();
text += " bump depth";
debug_text.set_text(text);
}
void dynamic_update(double delta) override {
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
render_shader.use();
// Camera
render_shader.setMat4("projection", camera.get_projection());
render_shader.setMat4("view", camera.get_view());
render_shader.setVec3("viewPos", camera.get_translation());
// Lighting
render_shader.setVec3("lightPos", glm::vec3(0.0, 1.0, 5.0));
render_shader.setFloat("number_of_steps", glm::max(0.0f, number_of_steps));
render_shader.setFloat("number_of_refinement_steps", glm::max(0.0f, number_of_refinement_steps));
render_shader.setFloat("bump_depth", glm::max(0.0f, bump_depth));
// Textures
albedo.bind_to(0);
normal.bind_to(1);
bump.bind_to(2);
// Quad which is rendered onto
quad_mesh.rotate(delta * 25.0f, glm::normalize(glm::vec3(0.0, 0.0, 1.0)));
quad_mesh.render(render_shader);
debug_text.render_text(25.0f, 25.0f, 1.0f, Gedeng::Vector3(1.0, 1.0, 0.0));
}
private:
float number_of_steps;
float number_of_refinement_steps;
float bump_depth;
Gedeng::Shader render_shader;
Gedeng::VertexBuffer vertex_rectangle;
Gedeng::Camera camera;
Gedeng::Texture albedo;
Gedeng::Texture bump;
Gedeng::Texture normal;
Gedeng::QuadMesh quad_mesh;
Gedeng::TextLabel debug_text;
};
Gedeng::Application *Gedeng::create_application() {
GG_CLIENT_INFO("Creating Application");
return new ParallaxApp(20, 900, 600, String("Parallax Demo"));
}

View File

@ -4,12 +4,23 @@
class TestApp : public Gedeng::Application {
public:
TestApp() = default;
TestApp(unsigned long ms_per_update, unsigned int window_size_x, unsigned int window_size_y,
Gedeng::String window_name)
: Application(ms_per_update, window_size_x, window_size_y, window_name) {
}
~TestApp() = default;
void fixed_update(double delta) override {
GG_CLIENT_INFO("Fixed update");
}
void dynamic_update(double delta) override {
GG_CLIENT_INFO("Dynamic update");
}
};
Gedeng::Application *Gedeng::create_application() {
GG_CLIENT_INFO("Creating Application");
return new TestApp();
return new TestApp(20, 900, 600, String("Test App"));
}