Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions src/common/application.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -136,8 +136,7 @@ void our::Application::configureOpenGL() {
// This is necessary for some platforms
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);

// Make window size fixed (User can't resize it)
glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);

// Set Number of sample used in MSAA (0 = Disabled)
glfwWindowHint(GLFW_SAMPLES, 0);
Expand Down
41 changes: 27 additions & 14 deletions src/common/systems/forward-renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,24 @@

namespace our {

void ForwardRenderer::resizePostprocess(glm::ivec2 size) {
if (!postprocessMaterial) return;

glBindFramebuffer(GL_FRAMEBUFFER, postprocessFrameBuffer);

colorTarget->bind();
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, size.x, size.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTarget->getOpenGLName(), 0);

depthTarget->bind();
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, size.x, size.y, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_INT,
nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTarget->getOpenGLName(), 0);

Texture2D::unbind();
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

void ForwardRenderer::initialize(glm::ivec2 windowSize, const nlohmann::json& config) {
// First, we store the window size for later use
this->windowSize = windowSize;
Expand Down Expand Up @@ -54,21 +72,8 @@ namespace our {
// Then we check if there is a postprocessing shader in the configuration
if (config.contains("postprocess")) {
glGenFramebuffers(1, &postprocessFrameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, postprocessFrameBuffer);
colorTarget = new Texture2D();
colorTarget->bind();
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, windowSize.x, windowSize.y, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, colorTarget->getOpenGLName(),
0);
colorTarget->unbind();

depthTarget = new Texture2D();
depthTarget->bind();
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, windowSize.x, windowSize.y, 0, GL_DEPTH_COMPONENT,
GL_UNSIGNED_INT, nullptr);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthTarget->getOpenGLName(), 0);
depthTarget->unbind();
glBindFramebuffer(GL_FRAMEBUFFER, 0);

// Create a vertex array to use for drawing the texture
glGenVertexArrays(1, &postProcessVertexArray);
Expand All @@ -94,6 +99,8 @@ namespace our {
// The default options are fine but we don't need to interact with the depth buffer
// so it is more performant to disable the depth mask
postprocessMaterial->pipelineState.depthMask = false;

resizePostprocess(windowSize);
}
}

Expand Down Expand Up @@ -125,7 +132,13 @@ namespace our {
}
}

void ForwardRenderer::render(World* world) {
void ForwardRenderer::render(World* world, glm::ivec2 windowSize) {
if (windowSize.x <= 0 || windowSize.y <= 0) return;
if (this->windowSize != windowSize) {
this->windowSize = windowSize;
resizePostprocess(windowSize);
}

// First of all, we search for a camera and for all the mesh renderers
CameraComponent* camera = nullptr;
opaqueCommands.clear();
Expand Down
4 changes: 3 additions & 1 deletion src/common/systems/forward-renderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,16 @@ namespace our {
TexturedMaterial* postprocessMaterial;
std::vector<LightRenderData> sceneLights;

void resizePostprocess(glm::ivec2 size);

public:
// Initialize the renderer including the sky and the Postprocessing objects.
// windowSize is the width & height of the window (in pixels).
void initialize(glm::ivec2 windowSize, const nlohmann::json& config);
// Clean up the renderer
void destroy();
// This function should be called every frame to draw the given world
void render(World* world);
void render(World* world, glm::ivec2 windowSize);
};

} // namespace our
78 changes: 59 additions & 19 deletions src/states/menu-state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,17 @@
#include <audio/audio-buffer.hpp>
#include <audio/audio-utils.hpp>
#include <functional>
#include <glm/common.hpp>
#include <material/material.hpp>
#include <mesh/mesh.hpp>
#include <shader/shader.hpp>
#include <texture/texture-utils.hpp>
#include <texture/texture2d.hpp>

// This struct is used to store the location and size of a button and the code it should execute when clicked
struct Button {
struct PixelRect {
// The position (of the top-left corner) of the button and its size in pixels
glm::vec2 position, size;
// The function that should be excuted when the button is clicked. It takes no arguments and returns nothing.
std::function<void()> action;

// This function returns true if the given vector v is inside the button. Otherwise, false is returned.
// This is used to check if the mouse is hovering over the button.
Expand All @@ -33,6 +32,19 @@ struct Button {
}
};

// Button using normalized coordinates.
struct Button {
glm::vec2 normalizedPosition, normalizedSize;
std::function<void()> action;

PixelRect getPixelRect(const PixelRect& menuRect) const {
return {
menuRect.position + normalizedPosition * menuRect.size,
normalizedSize * menuRect.size,
};
}
};

// This state shows how to use some of the abstractions we created to make a menu.
class Menustate : public our::State {
// A meterial holding the menu shader and the menu texture to draw
Expand All @@ -46,6 +58,31 @@ class Menustate : public our::State {
// An array of the button that we can interact with
std::array<Button, 2> buttons;

static PixelRect getMenuRect(glm::ivec2 framebufferSize) {
glm::vec2 viewportSize(framebufferSize);
constexpr float menuAspectRatio = 16.0f / 9.0f;

glm::vec2 contentSize = viewportSize;
if (viewportSize.x / viewportSize.y > menuAspectRatio) {
contentSize.x = viewportSize.y * menuAspectRatio;
} else {
contentSize.y = viewportSize.x / menuAspectRatio;
}

return {(viewportSize - contentSize) * 0.5f, contentSize};
}

glm::vec2 getMousePositionInFramebuffer(glm::ivec2 framebufferSize) {
glm::vec2 mousePosition = getApp()->getMouse().getMousePosition();
glm::ivec2 windowSize = getApp()->getWindowSize();
if (windowSize.x <= 0 || windowSize.y <= 0) return mousePosition;

return {
mousePosition.x * framebufferSize.x / static_cast<float>(windowSize.x),
mousePosition.y * framebufferSize.y / static_cast<float>(windowSize.y),
};
}

our::AudioBuffer* menuMusic = nullptr;
our::AudioBuffer* menuSelectSound = nullptr;

Expand Down Expand Up @@ -112,12 +149,12 @@ class Menustate : public our::State {
// - The argument list () which is the arguments that the lambda should receive when it is called.
// We leave it empty since button actions receive no input.
// - The body {} which contains the code to be executed.
buttons[0].position = {830.0f, 607.0f};
buttons[0].size = {400.0f, 33.0f};
buttons[0].normalizedPosition = {0.6484375f, 0.8430556f};
buttons[0].normalizedSize = {0.3125f, 0.0458333f};
buttons[0].action = [this]() { this->getApp()->changeState("play"); };

buttons[1].position = {830.0f, 644.0f};
buttons[1].size = {400.0f, 33.0f};
buttons[1].normalizedPosition = {0.6484375f, 0.8944444f};
buttons[1].normalizedSize = {0.3125f, 0.0458333f};
Comment thread
AhmedSobhy01 marked this conversation as resolved.
buttons[1].action = [this]() { this->getApp()->close(); };

// TODO: Maybe load them in application.cpp or serialize them from the config json
Expand All @@ -138,47 +175,49 @@ class Menustate : public our::State {
getApp()->close();
}

// Get a reference to the mouse object and get the current mouse position
glm::ivec2 size = getApp()->getFrameBufferSize();
PixelRect menuRect = getMenuRect(size);

// Get a reference to the mouse object and convert the current mouse position to framebuffer coordinates.
auto& mouse = getApp()->getMouse();
glm::vec2 mousePosition = mouse.getMousePosition();
glm::vec2 mousePosition = getMousePositionInFramebuffer(size);

// If the mouse left-button is just pressed, check if the mouse was inside
// any menu button. If it was inside a menu button, run the action of the button.
if (mouse.justPressed(0)) {
for (auto& button : buttons) {
if (button.isInside(mousePosition)) button.action();
if (button.getPixelRect(menuRect).isInside(mousePosition)) button.action();
}
}

// Get the framebuffer size to set the viewport and the create the projection matrix.
glm::ivec2 size = getApp()->getFrameBufferSize();
// Make sure the viewport covers the whole size of the framebuffer.
glViewport(0, 0, size.x, size.y);

// Clear to black so any space around the aspect-preserved menu image stays black
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

// The view matrix is an identity (there is no camera that moves around).
// The projection matrix applys an orthographic projection whose size is the framebuffer size in pixels
// so that the we can define our object locations and sizes in pixels.
// Note that the top is at 0.0 and the bottom is at the framebuffer height. This allows us to consider the
// top-left corner of the window to be the origin which makes dealing with the mouse input easier.
glm::mat4 VP = glm::ortho(0.0f, (float)size.x, (float)size.y, 0.0f, 1.0f, -1.0f);
// The local to world (model) matrix of the background which is just a scaling matrix to make the menu cover the
// whole window. Note that we defind the scale in pixels.
glm::mat4 M = glm::scale(glm::mat4(1.0f), glm::vec3(size.x, size.y, 1.0f));
// The local to world (model) matrix of the background draws the menu inside an aspect-preserved content area.
glm::mat4 M = menuRect.getLocalToWorld();

// First, we apply the fading effect.
time += (float)deltaTime;
menuMaterial->tint = glm::vec4(glm::smoothstep(0.00f, 2.00f, time));
// Then we render the menu background
// Notice that I don't clear the screen first, since I assume that the menu rectangle will draw over the whole
// window anyway.
menuMaterial->setup();
menuMaterial->shader->set("transform", VP * M);
rectangle->draw();

// Determine which button (if any) the mouse is currently over
int currentlyHovered = -1;
for (int i = 0; i < (int)buttons.size(); i++) {
if (buttons[i].isInside(mousePosition)) {
if (buttons[i].getPixelRect(menuRect).isInside(mousePosition)) {
currentlyHovered = i;
break;
}
Expand All @@ -192,8 +231,9 @@ class Menustate : public our::State {

// Draw the highlight rectangle over the hovered button
if (currentlyHovered != -1) {
PixelRect hoveredRect = buttons[currentlyHovered].getPixelRect(menuRect);
highlightMaterial->setup();
highlightMaterial->shader->set("transform", VP * buttons[currentlyHovered].getLocalToWorld());
highlightMaterial->shader->set("transform", VP * hoveredRect.getLocalToWorld());
rectangle->draw();
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/states/play-state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ class Playstate : public our::State {
collisionSystem.update(&world);

// Rendering
renderer.render(&world);
renderer.render(&world, getApp()->getFrameBufferSize());
enemyHealthBars.render(&world, getApp(), uiRenderer, activeCamera);

#ifdef COLLISION_DEBUG_DRAW
Expand Down
3 changes: 2 additions & 1 deletion src/states/renderer-test-state.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,11 @@ class RendererTestState : public our::State {

void onDraw(double deltaTime) override {
// We simply call the renderer's "render" function and it should do all the rendering work
renderer.render(&world);
renderer.render(&world, getApp()->getFrameBufferSize());
}

void onDestroy() override {
renderer.destroy();
world.clear();
our::clearAllAssets();
}
Expand Down
Loading