From d2cbd0075f3d3cd18fed3a97147d9cdeade9dbb1 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Mon, 20 Jun 2016 21:59:10 -0700 Subject: [PATCH 01/27] Temporary fix for crash [DO NOT SHIP][TMP] --- include/Events.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/Events.hpp b/include/Events.hpp index 78da23278..a73eee9bc 100644 --- a/include/Events.hpp +++ b/include/Events.hpp @@ -87,9 +87,9 @@ class DelegateBase { static_assert(sizeof(Deleter) == sizeof(ptrdiff_t), "Integral pointer conversion is flawed"); ~DelegateBase() { - if (m_flagRequiresDeletion != 0) { - ((Deleter)m_deleters[m_deleter])(m_caller); - } + // if (m_flagRequiresDeletion != 0) { + // ((Deleter)m_deleters[m_deleter])(m_caller); + // } } Caller m_caller; From 9c712a44f6e6c0e0de52b9d7ea44df8d75c18a9a Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Sat, 25 Jun 2016 17:44:07 -0700 Subject: [PATCH 02/27] First pass new graphics API for vorb. Sans camera. --- UnitTests/VorbGraphics.cpp | 15 ++ UnitTests/VorbUI.cpp | 35 ++-- Vorb.vcxproj | 9 ++ Vorb.vcxproj.filters | 27 ++++ include/graphics/Camera.h | 42 +++++ include/graphics/DepthState.h | 2 +- include/graphics/PostProcess.h | 104 ++++++++++++ include/graphics/Renderer.h | 126 +++++++++++++++ include/graphics/Scene.h | 83 ++++++++++ include/graphics/ShaderCommon.inl | 50 ++++++ include/ui/IGameScreen.h | 62 ++++--- include/ui/MainGame.h | 54 ++++--- src/graphics/Camera.cpp | 2 + src/graphics/PostProcess.cpp | 257 ++++++++++++++++++++++++++++++ src/graphics/Renderer.cpp | 176 ++++++++++++++++++++ src/graphics/Scene.cpp | 31 ++++ src/ui/MainGame.cpp | 41 ++--- 17 files changed, 1030 insertions(+), 86 deletions(-) create mode 100644 include/graphics/Camera.h create mode 100644 include/graphics/PostProcess.h create mode 100644 include/graphics/Renderer.h create mode 100644 include/graphics/Scene.h create mode 100644 include/graphics/ShaderCommon.inl create mode 100644 src/graphics/Camera.cpp create mode 100644 src/graphics/PostProcess.cpp create mode 100644 src/graphics/Renderer.cpp create mode 100644 src/graphics/Scene.cpp diff --git a/UnitTests/VorbGraphics.cpp b/UnitTests/VorbGraphics.cpp index cc647318a..576ffccc3 100644 --- a/UnitTests/VorbGraphics.cpp +++ b/UnitTests/VorbGraphics.cpp @@ -101,6 +101,9 @@ class ImageViewer : public vui::IGameScreen { if (m_tex.id != 0) glDeleteTextures(1, &m_tex.id); } + virtual void registerRendering(vg::Renderer& renderer) override { + } + virtual void update(const vui::GameTime& gameTime) { if (m_bmp.data) { glBindTexture(GL_TEXTURE_2D, m_tex.id); @@ -199,6 +202,9 @@ class FontViewer : public vui::IGameScreen { font.dispose(); } + virtual void registerRendering(vg::Renderer& renderer) override { + } + virtual void update(const vui::GameTime& gameTime) { // Empty } @@ -315,6 +321,9 @@ void main() { program.dispose(); } + virtual void registerRendering(vg::Renderer& renderer) override { + } + virtual void update(const vui::GameTime& gameTime) { yaw += (f32)(gameTime.elapsed * yawInput); pitch += (f32)(gameTime.elapsed * pitchInput); @@ -418,6 +427,9 @@ class SpriteBatchTester : public vui::IGameScreen { batch.dispose(); } + virtual void registerRendering(vg::Renderer& renderer) override { + } + virtual void update(const vui::GameTime& gameTime) { angle += 0.1f; } @@ -673,6 +685,9 @@ void main() { delete[] mRestInv; } + virtual void registerRendering(vg::Renderer& renderer) override { + } + virtual void update(const vui::GameTime& gameTime) { } virtual void draw(const vui::GameTime& gameTime) { diff --git a/UnitTests/VorbUI.cpp b/UnitTests/VorbUI.cpp index eeef7daee..7b1cf2aef 100644 --- a/UnitTests/VorbUI.cpp +++ b/UnitTests/VorbUI.cpp @@ -30,39 +30,39 @@ struct Vertex { class TestScreen : public vui::IGameScreen { public: - virtual i32 getNextScreen() const { + virtual i32 getNextScreen() const override { return SCREEN_INDEX_NO_SCREEN; } - virtual i32 getPreviousScreen() const { + virtual i32 getPreviousScreen() const override { return SCREEN_INDEX_NO_SCREEN; } - virtual void build() { + virtual void build() override { } - virtual void destroy(const vui::GameTime& gameTime) { + virtual void destroy(const vui::GameTime& gameTime) override { } - virtual void onEntry(const vui::GameTime& gameTime) { + virtual void onEntry(const vui::GameTime& gameTime) override { } - virtual void onExit(const vui::GameTime& gameTime) { + virtual void onExit(const vui::GameTime& gameTime) override { } - virtual void update(const vui::GameTime& gameTime) { + virtual void registerRendering(vg::Renderer& renderer) override { } - virtual void draw(const vui::GameTime& gameTime) { + virtual void update(const vui::GameTime& gameTime) override { } }; class WidgetTestScreen : public vui::IGameScreen { public: - virtual i32 getNextScreen() const { + virtual i32 getNextScreen() const override { return SCREEN_INDEX_NO_SCREEN; } - virtual i32 getPreviousScreen() const { + virtual i32 getPreviousScreen() const override { return SCREEN_INDEX_NO_SCREEN; } - virtual void build() { + virtual void build() override { } - virtual void destroy(const vui::GameTime& gameTime) { + virtual void destroy(const vui::GameTime& gameTime) override { } - virtual void onEntry(const vui::GameTime& gameTime) { + virtual void onEntry(const vui::GameTime& gameTime) override { font.init("Data/chintzy.ttf", 32); form.init("main", this, f32v4(0.0f, 0.0f, (f32)m_viewportDims.x, (f32)m_viewportDims.y)); @@ -90,14 +90,17 @@ class WidgetTestScreen : public vui::IGameScreen { // env.init(&form, &m_game->getWindow()); // env.loadForm("data/scripts/Form1.lua"); } - virtual void onExit(const vui::GameTime& gameTime) { + virtual void onExit(const vui::GameTime& gameTime) override { form.dispose(); // font.dispose(); } - virtual void update(const vui::GameTime& gameTime) { + virtual void registerRendering(vg::Renderer& renderer) override { + // Empty + } + virtual void update(const vui::GameTime& gameTime) override { // form.update(); } - virtual void draw(const vui::GameTime& gameTime) { + virtual void onRenderFrame(const vui::GameTime& gameTime) override { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); form.draw(); } diff --git a/Vorb.vcxproj b/Vorb.vcxproj index 010325f3d..63cf0e163 100644 --- a/Vorb.vcxproj +++ b/Vorb.vcxproj @@ -294,6 +294,7 @@ $(SolutionDir)DepsBuildCopy.bat + @@ -314,6 +315,9 @@ $(SolutionDir)DepsBuildCopy.bat + + + @@ -416,6 +420,7 @@ $(SolutionDir)DepsBuildCopy.bat + @@ -430,6 +435,9 @@ $(SolutionDir)DepsBuildCopy.bat + + + @@ -537,6 +545,7 @@ $(SolutionDir)DepsBuildCopy.bat + diff --git a/Vorb.vcxproj.filters b/Vorb.vcxproj.filters index fe503bcf6..f024dce3b 100644 --- a/Vorb.vcxproj.filters +++ b/Vorb.vcxproj.filters @@ -279,6 +279,18 @@ Examples + + Graphics + + + Graphics + + + Graphics + + + Graphics + @@ -715,6 +727,18 @@ Core\Math + + Graphics + + + Graphics + + + Graphics + + + Graphics + @@ -870,6 +894,9 @@ Core\Math + + Graphics + diff --git a/include/graphics/Camera.h b/include/graphics/Camera.h new file mode 100644 index 000000000..382e258c2 --- /dev/null +++ b/include/graphics/Camera.h @@ -0,0 +1,42 @@ +// +// Camera.h +// Vorb Engine +// +// Created by Benjamin Arnold on 20 Jun 2016 +// Copyright 2014 Regrowth Studios +// All Rights Reserved +// + +/*! \file Camera.h +* @brief +* Basic 3D camera implementation. +* +*/ + +#pragma once + +#ifndef Vorb_Camera_h__ +//! @cond DOXY_SHOW_HEADER_GUARDS +#define Vorb_Camera_h__ +//! @endcond + +#ifndef VORB_USING_PCH +#include "types.h" +#endif // !VORB_USING_PCH +namespace vorb { +namespace graphics { + + class Camera { + public: + + private: + f64v3 m_focalPoint = f64v3(0.0); + f64v3 m_position = f64v3(0.0); + }; + +} +} +namespace vg = vorb::graphics; + +#endif // !Vorb_Camera_h__ + diff --git a/include/graphics/DepthState.h b/include/graphics/DepthState.h index 4c882b74a..7c08e7b36 100644 --- a/include/graphics/DepthState.h +++ b/include/graphics/DepthState.h @@ -40,7 +40,7 @@ namespace vorb { bool shouldRead; bool shouldWrite; - // Comparison Against Destination Depth For Pixel Write/Discard + // Comparison A(ainst Destination Depth For Pixel Write/Discard DepthFunction depthFunc; // Always Draw Without Touching Depth diff --git a/include/graphics/PostProcess.h b/include/graphics/PostProcess.h new file mode 100644 index 000000000..7d5db3fd0 --- /dev/null +++ b/include/graphics/PostProcess.h @@ -0,0 +1,104 @@ +// +// PostProcess.h +// Vorb Engine +// +// Created by Benjamin Arnold on 22 Jun 2016 +// Copyright 2014 Regrowth Studios +// All Rights Reserved +// + +/*! \file PostProcess.h +* @brief A PostProcess stage for processing a drawn FBO. +* +* +*/ + +#pragma once + +#ifndef Vorb_PostProcess_h__ +//! @cond DOXY_SHOW_HEADER_GUARDS +#define Vorb_PostProcess_h__ +//! @endcond + +#ifndef VORB_USING_PCH +#include "types.h" +#endif // !VORB_USING_PCH + +#include "FullQuadVBO.h" +#include "GLProgram.h" +#include "GLRenderTarget.h" + +namespace vorb { +namespace graphics { + + // Forward Declare + class GLRenderTarget; + class Renderer; + + /************************************************************************/ + /* Base */ + /************************************************************************/ + class IPostProcess { + public: + friend class Renderer; + + /*! @brief Loads shaders and other graphical objects. + * Gets called by SceneRenderer. + */ + virtual void load() = 0; + + /*! @brief Renders the post process + * Gets called by SceneRenderer. + */ + virtual void render() = 0; + + /*! @brief Frees resources. + */ + virtual void dispose() = 0; + + /*! @brief Unregisters from the SceneRenderer + */ + virtual void unregister(); + + protected: + Renderer* m_renderer = nullptr; ///< Renderer that owns this. + }; + + /************************************************************************/ + /* Bloom */ + /************************************************************************/ + class PostProcessBloom : public IPostProcess { + public: + void init (ui32 windowWidth, ui32 windowHeight); + void setParams(ui32 gaussianN = 20, f32 gaussianVariance = 36.0f, f32 lumaThreshold = 0.75f); + void load () override; + virtual void render () override; + virtual void dispose () override; + + private: + void renderStage (vg::GLProgram& program); + void uploadUniforms(); + + /// Shaders + vg::GLProgram m_programLuma; + vg::GLProgram m_programGaussianFirst; + vg::GLProgram m_programGaussianSecond; + + vg::FullQuadVBO m_quad; + + vg::GLRenderTarget m_fbos[2]; + + ui32 m_windowWidth = 0; + ui32 m_windowHeight = 0; + + /// Parameters + ui32 m_gaussianN = 20; ///< Threshold for filtering image luma for bloom bluring + f32 m_gaussianVariance = 36.0f; ///< Radius number for gaussian blur. Must be less than 50. + f32 m_lumaThreshold = 0.75f; ///< Gaussian variance for blur pass + }; +} +} +namespace vg = vorb::graphics; + +#endif // !Vorb_PostProcess_h__ + diff --git a/include/graphics/Renderer.h b/include/graphics/Renderer.h new file mode 100644 index 000000000..0af18d07e --- /dev/null +++ b/include/graphics/Renderer.h @@ -0,0 +1,126 @@ +// +// Renderer.h +// Vorb Engine +// +// Created by Benjamin Arnold on 22 Jun 2016 +// Copyright 2014 Regrowth Studios +// All Rights Reserved +// + +/*! \file Renderer.h +* @brief Handles rendering of scenes and post processes to a vui::GameWindow. +* +* +*/ + +#pragma once + +#ifndef Vorb_Renderer_h__ +//! @cond DOXY_SHOW_HEADER_GUARDS +#define Vorb_Renderer_h__ +//! @endcond + +#ifndef VORB_USING_PCH +#include "types.h" +#endif // !VORB_USING_PCH + +#include "../ui/GameWindow.h" +#include "../VorbPreDecl.inl" + +DECL_VUI(struct GameTime); + +namespace vorb { + namespace graphics { + + // Forward Declare + class GLRenderTarget; + class IScene; + class IPostProcess; + + /// Manages storage and rendering of scenes and post processes + class Renderer { + public: + Renderer(); + virtual ~Renderer(); + + /*! @brief Initializes the SceneRenderer for the GameWindow. + * Do not call twice without first destroying. + */ + virtual bool init(vui::GameWindow* window); + + /*! @brief Loads any scenes or postProcessed that need to be loaded. + */ + virtual void load(); + + /*! @brief Registers a scene for load and then rendering. + */ + virtual void registerScene(IScene* scene); + + /*! @brief Unregisters a scene for rendering. + * Does not dispose the scene. + * @return true on success. + */ + virtual bool unregisterScene(IScene* scene); + + /*! @brief Registers a PostProcess for load and then rendering. + */ + virtual void registerPostProcesses(IPostProcess* postProcess); + + /*! @brief Unregisters a PostProcess for rendering. + * Does not dispose the PostProcess. + * @return true on success. + */ + virtual bool unregisterPostProcesses(IPostProcess* postProcess); + + /*! @Brief sets background color for window + */ + virtual void setBackgroundColor(const color4& color); + virtual void setBackgroundColor(const f32v4& color); + + /*! @Brief clears depth and color buffer and sets viewport. + */ + virtual void beginRenderFrame(); + + /*! @Brief Sync the window and display to the screen. + */ + virtual void sync(ui32 frameTime) { m_window->sync(frameTime); } + + /*! @brief Renders all scenes in order that they were registered + * Does not sync. Call vui::GameWindow::sync() to draw to the screen. + */ + virtual void renderScenes(const vui::GameTime& gameTime); + + /*! @brief Renders all postprocess stages. Call after renderScenes. + */ + virtual void renderPostProcesses(); + + /*! @brief Cleans up the renderer and disposes all scenes and postprocesses. + */ + virtual void dispose(); + + /*! + */ + + /************************************************************************/ + /* Accessors */ + /************************************************************************/ + const std::vector& getScenes() const { return m_scenes; } + const std::vector& getPostProcesses() const { return m_postProcesses; } + bool isInitialized() const { return m_isInitialized; } + + protected: + bool m_isInitialized = false; + vui::GameWindow* m_window = nullptr; ///< The window we are targeting. + + std::vector m_scenes; ///< Scenes that are loaded and ready to render + std::vector m_sceneLoadQueue; ///< Scenes that need to load + std::vector m_postProcesses; ///< PostPorcesses that are loaded and ready to render + std::vector m_postProcessesLoadQueue; ///< PostProcesses that need to load + }; + } +} +namespace vg = vorb::graphics; + + +#endif // !Vorb_Renderer_h__ + diff --git a/include/graphics/Scene.h b/include/graphics/Scene.h new file mode 100644 index 000000000..e9f0019e6 --- /dev/null +++ b/include/graphics/Scene.h @@ -0,0 +1,83 @@ +// +// Scene.h +// Vorb Engine +// +// Created by Benjamin Arnold on 22 Jun 2016 +// Copyright 2014 Regrowth Studios +// All Rights Reserved +// + +/*! \file Scene.h +* @brief A complete graphical scene for rendering to the screen. +* +* +*/ + +#pragma once + +#ifndef Vorb_Scene_h__ +//! @cond DOXY_SHOW_HEADER_GUARDS +#define Vorb_Scene_h__ +//! @endcond + +#ifndef VORB_USING_PCH +#include "types.h" +#endif // !VORB_USING_PCH + +#include "../VorbPreDecl.inl" + +DECL_VUI(struct GameTime); + +namespace vorb { + namespace graphics { + + // Forward Declare + class Camera; + class GLRenderTarget; + class Renderer; + + /// A scene, complete with its own camera and rendering logic. + class IScene { + public: + friend class Renderer; + + IScene(); + virtual ~IScene(); + + // You should implement your own initialization logic + + /*! @brief Sets up the camera. + * Override this to provide a custom camera implementation. + */ + virtual void initCamera(); + + /* @brief Load all graphics objects. + * Gets called by SceneRenderer. + */ + virtual void load() = 0; + + /*! @brief Renders a scene. + * + */ + virtual void render(const vui::GameTime& gameTime) = 0; + + /*! @brief Unregisters from the SceneRenderer + */ + virtual void unregister(); + + /*! @brief Frees any resources and resets state. + */ + virtual void dispose(); + + Camera* getCamera() { return m_camera.get(); } + + protected: + std::unique_ptr m_camera = nullptr; + Renderer* m_renderer = nullptr; ///< Renderer that owns this. + }; + } +} +namespace vg = vorb::graphics; + +#endif // !Vorb_Scene_h__ + diff --git a/include/graphics/ShaderCommon.inl b/include/graphics/ShaderCommon.inl new file mode 100644 index 000000000..3699a2403 --- /dev/null +++ b/include/graphics/ShaderCommon.inl @@ -0,0 +1,50 @@ +// +// ShaderCommon.inl +// Vorb Engine +// +// Created by Benjamin Arnold on 25 Jun 2016 +// Copyright 2014 Regrowth Studios +// All Rights Reserved +// + +/*! \file ShaderCommon.inl +* @brief Common shader code to be reused. +* +* +*/ + +#pragma once + +#ifndef Vorb_ShaderCommon_inl__ +//! @cond DOXY_SHOW_HEADER_GUARDS +#define Vorb_ShaderCommon_inl__ +//! @endcond + +#ifndef VORB_USING_PCH +#include "types.h" +#endif // !VORB_USING_PCH + +namespace vorb { +namespace graphics { +namespace shadercommon { + + /// For just passing UV through the vertex shader + const cString PASSTHROUGH_VERT_SRC = R"( +// Input +in vec2 vPosition; // Position in screen space + +// Output +out vec2 fUV; + +void main() { + fUV = (vPosition + 1.0) / 2.0; + gl_Position = vec4(vPosition, 0, 1); +})"; + +} +} +} +namespace vg = vorb::graphics; + +#endif // !Vorb_ShaderCommon_inl__ + diff --git a/include/ui/IGameScreen.h b/include/ui/IGameScreen.h index 7d0f9e696..b23165344 100644 --- a/include/ui/IGameScreen.h +++ b/include/ui/IGameScreen.h @@ -17,31 +17,27 @@ #include "MainGame.h" #include "FocusController.h" +#include "../VorbPreDecl.inl" + +DECL_VG(class Renderer); namespace vorb { namespace ui { #define SCREEN_INDEX_NO_SCREEN -1 #define SCREEN_INDEX_NO_START_SELECTED -2 - // A Screen Must Be In One Of These States enum class ScreenState { - // The Screen Is Doing Nothing At The Moment NONE, - // The Screen Is Running (Current Active) RUNNING, - // Exit Request To The Main Game EXIT_APPLICATION, - // Request To Move To The Next Screen - CHANGE_NEXT, - // Request To Move To The Previous Screen - CHANGE_PREVIOUS + CHANGE_NEXT, ///< Go to next screen. + CHANGE_PREVIOUS ///< Go to previous screen. }; - // Common Interface For A Game Screen + // Common interface for a game screen class IGameScreen { public: - IGameScreen() - : m_state(ScreenState::NONE) { + IGameScreen() { // empty } @@ -49,22 +45,28 @@ namespace vorb { // empty } - // All Screens Should Have A Parent + /*! @brief All screens should have a parent. + */ void setParentGame(MainGame* game, i32 index) { m_game = game; m_index = index; } - // The Screen's Location In The List + /*! @brief The screen's location in the list. + */ i32 getIndex() const { return m_index; } - // Returns Screen Index When Called To Change Screens + /*! @brief Returns screen index when called to change to next screen. + */ virtual i32 getNextScreen() const = 0; + /*! @brief Returns screen index when called to change to previous screen. + */ virtual i32 getPreviousScreen() const = 0; - // Screen State Functions + /*! @brief Screen state functions + */ ScreenState getState() const { return m_state; } @@ -72,23 +74,39 @@ namespace vorb { m_state = ScreenState::RUNNING; } - // Called At The Beginning And End Of The Application + /*! @brief Called At the beginning and end of the application. + */ virtual void build() = 0; + + /*! @brief Destroys all resources + */ virtual void destroy(const vui::GameTime& gameTime) = 0; - // Called When A Screen Enters And Exits Focus + /*! @brief Called when a screen enters and exits focus. + */ virtual void onEntry(const vui::GameTime& gameTime) = 0; virtual void onExit(const vui::GameTime& gameTime) = 0; + + /*! @brief Initializes and register any scenes for rendering. + * @param renderer: Register scenes and postprocesses with this to enable rendering. + */ + virtual void registerRendering(vg::Renderer& renderer) = 0; + + /*! @brief Called after the screen clear, but before the Renderer begins to render. + * It is recommended to do rendering via Scenes and PostProcesses rather than here, + * but you can optionally do rendering here. + */ + virtual void onRenderFrame(const vui::GameTime& gameTime) {}; - // Called In The Main Update Loop + /*! @brief Called In the main update loop. + */ virtual void update(const vui::GameTime& gameTime) = 0; - virtual void draw(const vui::GameTime& gameTime) = 0; protected: - ScreenState m_state; - MainGame* m_game = nullptr; + ScreenState m_state = ScreenState::NONE; + MainGame* m_game = nullptr; FocusController m_focusController; private: - // Location In The ScreenList + // Location in the screenList i32 m_index = -1; }; diff --git a/include/ui/MainGame.h b/include/ui/MainGame.h index 55d45b666..fbf272c6a 100644 --- a/include/ui/MainGame.h +++ b/include/ui/MainGame.h @@ -14,7 +14,7 @@ #pragma once #ifndef Vorb_MainGame_h__ -//! @cond DOXY_SHOW_HEADER_GUARDS +//! @cond DOXY_SHOW_HEADER_GUARDS2 #define Vorb_MainGame_h__ //! @endcond @@ -26,6 +26,7 @@ #include "../Events.hpp" #include "GameWindow.h" #include "ScreenList.h" +#include "../graphics/Renderer.h" namespace vorb { namespace ui { @@ -54,23 +55,6 @@ namespace vorb { */ virtual ~MainGame(); - /*! @brief Retrieve this application's window. - * - * @warning Do not attempt to set the window to a value other than its own. - * - * @return This game's window reference. - */ - GameWindow& getWindow() { - return m_window; - } - /*! @brief Retrieve this application's window. - * - * @return This game's window reference. - */ - const GameWindow& getWindow() const { - return m_window; - } - /*! @brief Initialize UI libraries and begin running the main loop. * * The run method blocks the thread on which it is called and it makes it the @@ -102,6 +86,29 @@ namespace vorb { const f32& getFps() const { return m_fps; } + + /************************************************************************/ + /* Accessors */ + /************************************************************************/ + /*! @brief Retrieve this application's window. + * + * @warning Do not attempt to set the window to a value other than its own. + * + * @return This game's window reference. + */ + GameWindow& getWindow() { + return m_window; + } + /*! @brief Retrieve this application's window. + * + * @return This game's window reference. + */ + const GameWindow& getWindow() const { + return m_window; + } + + vg::Renderer& getRenderer() { return m_renderer; } + protected: VORB_NON_COPYABLE(MainGame); VORB_NON_MOVABLE(MainGame); @@ -120,16 +127,19 @@ namespace vorb { void onUpdateFrame(); void onRenderFrame(); - GameWindow m_window; ///< The application's window + GameWindow m_window; ///< The application's window. + + vg::Renderer m_renderer; ///< Renders scenes to m_window. f32 m_fps = 0.0f; ///< Cached FPS value of the main loop UNIT_SPACE(MILLISECONDS) ui32 m_lastMS = 0; ///< Value of the last obtained clock time. + GameTime m_curTime; ///< The current known application time. GameTime m_lastTime; ///< The last time value, used for delta purposes. - bool m_isRunning = false; ///< Flag if the application thinks it's running. - ScreenList m_screenList; ///< List of the managed screens. - IGameScreen* m_screen = nullptr; ///< The active screen. + bool m_isRunning = false; ///< Flag if the application thinks it's running. + IGameScreen* m_screen = nullptr; ///< The active screen. + ScreenList m_screenList; ///< List of the managed screens. volatile bool m_signalQuit = false; ///< Tracks application's request to quit. }; diff --git a/src/graphics/Camera.cpp b/src/graphics/Camera.cpp new file mode 100644 index 000000000..028186eff --- /dev/null +++ b/src/graphics/Camera.cpp @@ -0,0 +1,2 @@ +#include "stdafx.h" +#include "graphics/Camera.h" \ No newline at end of file diff --git a/src/graphics/PostProcess.cpp b/src/graphics/PostProcess.cpp new file mode 100644 index 000000000..3c790f6fc --- /dev/null +++ b/src/graphics/PostProcess.cpp @@ -0,0 +1,257 @@ +#include "stdafx.h" +#include "graphics/PostProcess.h" +#include "VorbAssert.hpp" +#include "math/VorbMath.hpp" +#include "graphics/ShaderCommon.inl" +#include "graphics/ShaderManager.h" +#include "graphics/Renderer.h" + +/************************************************************************/ +/* IPostProcess */ +/************************************************************************/ + +void vg::IPostProcess::unregister() { + vorb_assert(m_renderer, "PostProcess unregistered without having renderer."); + + m_renderer->unregisterPostProcesses(this); +} + +/************************************************************************/ +/* Bloom */ +/************************************************************************/ + +#define BLOOM_TEXTURE_SLOT_COLOR 0 // texture slot to bind color texture which luma info will be extracted +#define BLOOM_TEXTURE_SLOT_LUMA 0 // texture slot to bind luma texture +#define BLOOM_TEXTURE_SLOT_BLUR 1 // texture slot to bind blur texture + +#pragma region BloomShaderCode + +/// Bloom shaders created by Isaque Dutra on 2 June 2015 +/// Copyright 2015 Regrowth Studios +/// All Rights Reserved + +const cString BLOOM_LUMA_FRAG_SRC = R"( +/// This fragment shader filters out the image's parts with luma (brightness) +/// stronger than uniform unLumaThresh and scales it proportionally passing it +/// to be blurred by next stages. +/// Taken from OpenGL 4.0 Shading Language Cookbook, First Edition, by David Wolff + +// input +in vec2 fUV; + +// output +out vec4 pColor; + +// uniforms +uniform sampler2D unTexColor; // Texture with rendering color from previous stage +uniform float unLumaThresh; // Threshold for filtering image luma for bloom bluring + +// returns the average brightness of a pixel +float luma(vec3 color) { + return 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b; +} + +void main() { + + vec4 val = texture(unTexColor, fUV); + pColor = val * clamp( luma(val.rgb) - unLumaThresh, 0.0, 1.0 ) * (1.0 / (1.0 - unLumaThresh)); + pColor = vec4(pColor.rgb, 1.0); +})"; + +const cString BLOOM_GAUSS1_FRAG_SRC = R"( +/// This fragment shader implements first stage of two-pass gaussian blur. +/// It blurs only the brighest parts filtered out by the Luma stage. +/// Taken from OpenGL 4.0 Shading Language Cookbook, First Edition, by David Wolff + +// input +in vec2 fUV; + +//output +out vec4 pColor; + +// uniforms +uniform int unHeight; // Window height +uniform sampler2D unTexLuma; // Texture with brighest parts of image +uniform int unGaussianN; // Radius for gaussian blur +uniform float unWeight[50]; // Gaussian weights + +// first pass of gaussian blur, in the y direction +void main() { + float dy = 1.0 / float(unHeight); + + vec4 sum = texture(unTexLuma, fUV) * unWeight[0]; + for(int i=1; iunregisterPostProcesses(this); + } +} + +void vg::PostProcessBloom::renderStage(vg::GLProgram& program) { + + program.use(); + program.enableVertexAttribArrays(); + + glDisable(GL_DEPTH_TEST); + m_quad.draw(); + glEnable(GL_DEPTH_TEST); + + program.disableVertexAttribArrays(); + program.unuse(); +} + +void vg::PostProcessBloom::uploadUniforms() { + m_programLuma.use(); + glUniform1i(m_programLuma.getUniform("unTexColor"), BLOOM_TEXTURE_SLOT_COLOR); + glUniform1f(m_programLuma.getUniform("unLumaThresh"), m_lumaThreshold); + m_programLuma.unuse(); + + m_programGaussianFirst.use(); + glUniform1i(m_programGaussianFirst.getUniform("unTexLuma"), BLOOM_TEXTURE_SLOT_LUMA); + glUniform1i(m_programGaussianFirst.getUniform("unHeight"), m_windowHeight); + glUniform1i(m_programGaussianFirst.getUniform("unGaussianN"), m_gaussianN); + m_programGaussianFirst.unuse(); + + m_programGaussianSecond.use(); + glUniform1i(m_programGaussianSecond.getUniform("unTexColor"), BLOOM_TEXTURE_SLOT_COLOR); + glUniform1i(m_programGaussianSecond.getUniform("unTexBlur"), BLOOM_TEXTURE_SLOT_BLUR); + glUniform1i(m_programGaussianSecond.getUniform("unWidth"), m_windowWidth); + glUniform1i(m_programGaussianSecond.getUniform("unGaussianN"), m_gaussianN); + m_programGaussianSecond.unuse(); +} \ No newline at end of file diff --git a/src/graphics/Renderer.cpp b/src/graphics/Renderer.cpp new file mode 100644 index 000000000..b97e46a19 --- /dev/null +++ b/src/graphics/Renderer.cpp @@ -0,0 +1,176 @@ +#include "stdafx.h" +#include "graphics/Renderer.h" +#include "graphics/Scene.h" +#include "graphics/PostProcess.h" + +vg::Renderer::Renderer() { + // Empty +} + +vg::Renderer::~Renderer() { + if (!m_isInitialized) { + dispose(); + } +} + +bool vg::Renderer::init(vui::GameWindow* window) { + + vorb_assert(!m_isInitialized, "Renderer was initialized twice."); + + m_window = window; + m_isInitialized = true; +} + +void vg::Renderer::load() { + for (IScene* s : m_sceneLoadQueue) { + s->load(); + m_scenes.push_back(s); + } + m_sceneLoadQueue.clear(); + + for (IPostProcess* p : m_postProcesses) { + p->load(); + m_postProcesses.push_back(p); + } + m_postProcesses.clear(); +} + +void vg::Renderer::registerScene(IScene* scene) { + // Make sure the scene doesn't exist + vorb_assert(std::find(m_scenes.begin(), m_scenes.end(), scene) == m_scenes.end(), "Scene already added to SceneRenderer."); + + scene->m_renderer = this; + + m_sceneLoadQueue.push_back(scene); +} + +bool vg::Renderer::unregisterScene(IScene* scene) { + auto& it = std::find(m_scenes.begin(), m_scenes.end(), scene); + if (it != m_scenes.end()) { + // Remove the scene + m_scenes.erase(it); + scene->m_renderer = nullptr; + return true; + } else { + // Check the load list instead + auto& it2 = std::find(m_sceneLoadQueue.begin(), m_sceneLoadQueue.end(), scene); + if (it != m_scenes.end()) { + m_sceneLoadQueue.erase(it2); + scene->m_renderer = nullptr; + return true; + } + } + return false; +} + +void vg::Renderer::registerPostProcesses(IPostProcess* postProcess) { + // Make sure the PostProcess doesn't exist + vorb_assert(std::find(m_postProcesses.begin(), m_postProcesses.end(), postProcess) == m_postProcesses.end(), "PostProcess already added to SceneRenderer."); + + postProcess->m_renderer = this; + + m_postProcessesLoadQueue.push_back(postProcess); +} + +bool vg::Renderer::unregisterPostProcesses(IPostProcess* postProcess) { + auto& it = std::find(m_postProcesses.begin(), m_postProcesses.end(), postProcess); + if (it != m_postProcesses.end()) { + // Remove the PostProcess + m_postProcesses.erase(it); + postProcess->m_renderer = nullptr; + return true; + } else { + // Check the load list instead + auto& it2 = std::find(m_postProcessesLoadQueue.begin(), m_postProcessesLoadQueue.end(), postProcess); + if (it != m_postProcesses.end()) { + m_postProcessesLoadQueue.erase(it2); + postProcess->m_renderer = nullptr; + return true; + } + } + return false; +} + +void vg::Renderer::setBackgroundColor(const color4& color) { + setBackgroundColor(f32v4((f32)color.r * 255.0f, + (f32)color.g * 255.0f, + (f32)color.b * 255.0f, + (f32)color.a * 255.0f)); +} + +void vg::Renderer::setBackgroundColor(const f32v4& color) { +#if defined(VORB_IMPL_GRAPHICS_OPENGL) + glClearColor(color.r, color.g, color.b, color.a); +#else + throw "Only OpenGL background color is supported by the renderer" +#endif +} + +void vg::Renderer::beginRenderFrame() { + +#if defined(VORB_IMPL_GRAPHICS_OPENGL) + + glViewport(0, 0, m_window->getWidth(), m_window->getHeight()); + // TODO(Ben): Allow optional clearing + glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); + +#elif defined(VORB_IMPL_GRAPHICS_D3D) + + { + +#if defined(VORB_DX_9) + + D3DVIEWPORT9 vp; + vp.X = 0; + vp.Y = 0; + vp.Width = m_window.getWidth(); + vp.Height = m_window.getHeight(); + vp.MinZ = 0.0f; + vp.MaxZ = 1.0f; + VG_DX_DEVICE(m_window.getContext())->SetViewport(&vp); + +#endif + + } + +#if defined(VORB_DX_9) + + VG_DX_DEVICE(m_window.getContext())->BeginScene(); + +#endif +#endif +} + +void vg::Renderer::renderScenes(const vui::GameTime& gameTime) { + for (IScene* s : m_scenes) { + s->render(gameTime); + } +} + +void vg::Renderer::renderPostProcesses() { + for (IPostProcess* p : m_postProcesses) { + p->render(); + } +} + +void vg::Renderer::dispose() { + for (IScene* s : m_scenes) { + s->dispose(); + } + std::vector().swap(m_scenes); + for (IScene* s : m_sceneLoadQueue) { + s->dispose(); + } + std::vector().swap(m_sceneLoadQueue); + + for (IPostProcess* p : m_postProcesses) { + p->dispose(); + } + std::vector().swap(m_postProcesses); + for (IPostProcess* p : m_postProcessesLoadQueue) { + p->dispose(); + } + std::vector().swap(m_postProcessesLoadQueue); + + m_isInitialized = false; +} diff --git a/src/graphics/Scene.cpp b/src/graphics/Scene.cpp new file mode 100644 index 000000000..0ec86d945 --- /dev/null +++ b/src/graphics/Scene.cpp @@ -0,0 +1,31 @@ +#include "stdafx.h" +#include "graphics/Scene.h" +#include "graphics/Camera.h" +#include "graphics/Renderer.h" + +vg::IScene::IScene() { + // Empty +} + +vg::IScene::~IScene() { + // Empty +} + +void vg::IScene::initCamera() { + m_camera = std::make_unique(); +} + +void vg::IScene::unregister() { + vorb_assert(m_renderer, "Scene unregistered without having renderer."); + + m_renderer->unregisterScene(this); +} + +void vg::IScene::dispose() { + m_camera.reset(); + + // Unregister if we need to. + if (m_renderer) { + m_renderer->unregisterScene(this); + } +} \ No newline at end of file diff --git a/src/ui/MainGame.cpp b/src/ui/MainGame.cpp index 0ae8965fb..4509bb1b7 100644 --- a/src/ui/MainGame.cpp +++ b/src/ui/MainGame.cpp @@ -63,6 +63,7 @@ bool vui::MainGame::init() { // Run The First Game Screen m_screen->setRunning(); m_screen->onEntry(m_lastTime); + m_screen->registerRendering(m_renderer); } // Set last known time @@ -81,8 +82,11 @@ bool vui::MainGame::initSystems() { // Set A Default OpenGL State vg::DepthState::FULL.set(); vg::RasterizerState::CULL_CLOCKWISE.set(); -#elif defined(VORB_IMPL_GRAPHICS_D3D) + m_renderer.init(&m_window); + +#elif defined(VORB_IMPL_GRAPHICS_D3D) + #endif return true; @@ -93,6 +97,7 @@ void vui::MainGame::exitGame() { m_screen = nullptr; } m_screenList.destroy(m_lastTime); + m_renderer.dispose(); onExit(); m_window.dispose(); m_isRunning = false; @@ -112,6 +117,7 @@ bool vui::MainGame::checkScreenChange() { if (m_screen != nullptr) { m_screen->setRunning(); m_screen->onEntry(m_curTime); + m_screen->registerRendering(m_renderer); } return true; case ScreenState::CHANGE_PREVIOUS: @@ -120,6 +126,7 @@ bool vui::MainGame::checkScreenChange() { if (m_screen != nullptr) { m_screen->setRunning(); m_screen->onEntry(m_curTime); + m_screen->registerRendering(m_renderer); } return true; case ScreenState::EXIT_APPLICATION: @@ -162,7 +169,7 @@ void vui::MainGame::run() { // Refresh time information for this frame refreshElapsedTime(); - // Scree logic + // Screen logic if (!checkScreenChange()) { // Update onUpdateFrame(); @@ -174,7 +181,7 @@ void vui::MainGame::run() { // Swap buffers and synchronize time-step and window input ui32 curMS = MS_TIME; - m_window.sync(curMS - m_lastMS); + m_renderer.sync(curMS - m_lastMS); // Get the FPS m_fps = fpsCounter.endFrame(); @@ -207,29 +214,13 @@ void vui::MainGame::onUpdateFrame() { m_screen->update(m_curTime); } void vui::MainGame::onRenderFrame() { -#if defined(VORB_IMPL_GRAPHICS_OPENGL) - // TODO: Investigate Removing This - glViewport(0, 0, m_window.getWidth(), m_window.getHeight()); -#elif defined(VORB_IMPL_GRAPHICS_D3D) - { -#if defined(VORB_DX_9) - D3DVIEWPORT9 vp; - vp.X = 0; - vp.Y = 0; - vp.Width = m_window.getWidth(); - vp.Height = m_window.getHeight(); - vp.MinZ = 0.0f; - vp.MaxZ = 1.0f; - VG_DX_DEVICE(m_window.getContext())->SetViewport(&vp); -#endif - } -#if defined(VORB_DX_9) - VG_DX_DEVICE(m_window.getContext())->BeginScene(); -#endif -#endif + m_renderer.beginRenderFrame(); + + // TODO(Ben): Deferred - // Draw the screen - m_screen->draw(m_curTime); + m_screen->onRenderFrame(m_curTime); + m_renderer.renderScenes(m_curTime); + m_renderer.renderPostProcesses(); #if defined(VORB_IMPL_GRAPHICS_D3D) #if defined(VORB_DX_9) From 2cecb246b1580956f2d2e077734df7ee80ed37f8 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Sat, 25 Jun 2016 18:11:43 -0700 Subject: [PATCH 03/27] Load graphics and fix random typo --- include/graphics/DepthState.h | 2 +- src/ui/MainGame.cpp | 4 ++++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/include/graphics/DepthState.h b/include/graphics/DepthState.h index 7c08e7b36..4c882b74a 100644 --- a/include/graphics/DepthState.h +++ b/include/graphics/DepthState.h @@ -40,7 +40,7 @@ namespace vorb { bool shouldRead; bool shouldWrite; - // Comparison A(ainst Destination Depth For Pixel Write/Discard + // Comparison Against Destination Depth For Pixel Write/Discard DepthFunction depthFunc; // Always Draw Without Touching Depth diff --git a/src/ui/MainGame.cpp b/src/ui/MainGame.cpp index 4509bb1b7..94913655e 100644 --- a/src/ui/MainGame.cpp +++ b/src/ui/MainGame.cpp @@ -214,6 +214,10 @@ void vui::MainGame::onUpdateFrame() { m_screen->update(m_curTime); } void vui::MainGame::onRenderFrame() { + // Load anything that needs loading + // TODO(Ben): Support asynchronous. + m_renderer.load(); + m_renderer.beginRenderFrame(); // TODO(Ben): Deferred From d3e91af47678018e71c0c44afe510b3f14643308 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Sat, 25 Jun 2016 18:23:49 -0700 Subject: [PATCH 04/27] Fixed post process load bug --- src/graphics/Renderer.cpp | 4 ++-- src/ui/MainGame.cpp | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/graphics/Renderer.cpp b/src/graphics/Renderer.cpp index b97e46a19..939a099a8 100644 --- a/src/graphics/Renderer.cpp +++ b/src/graphics/Renderer.cpp @@ -28,11 +28,11 @@ void vg::Renderer::load() { } m_sceneLoadQueue.clear(); - for (IPostProcess* p : m_postProcesses) { + for (IPostProcess* p : m_postProcessesLoadQueue) { p->load(); m_postProcesses.push_back(p); } - m_postProcesses.clear(); + m_postProcessesLoadQueue.clear(); } void vg::Renderer::registerScene(IScene* scene) { diff --git a/src/ui/MainGame.cpp b/src/ui/MainGame.cpp index 94913655e..fb46886d5 100644 --- a/src/ui/MainGame.cpp +++ b/src/ui/MainGame.cpp @@ -209,10 +209,12 @@ void vui::MainGame::refreshElapsedTime() { m_curTime.elapsed = et; m_curTime.total += et; } + void vui::MainGame::onUpdateFrame() { // Perform the screen's update logic m_screen->update(m_curTime); } + void vui::MainGame::onRenderFrame() { // Load anything that needs loading // TODO(Ben): Support asynchronous. From 43e17c0a84994ce2cf10915c50b000999d2de624 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Sat, 25 Jun 2016 18:37:42 -0700 Subject: [PATCH 05/27] Fixed initialization bug in Bloom. --- src/graphics/PostProcess.cpp | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/src/graphics/PostProcess.cpp b/src/graphics/PostProcess.cpp index 3c790f6fc..13a89067f 100644 --- a/src/graphics/PostProcess.cpp +++ b/src/graphics/PostProcess.cpp @@ -178,6 +178,10 @@ void vg::PostProcessBloom::load() { } void vg::PostProcessBloom::render() { + // No depth testing + // TODO(Ben): Don't fuck with existing state. Need state manager. + glDisable(GL_DEPTH_TEST); + // Get initial bound FBO and bound color texture to use it on final pass GLint initial_fbo, initial_texture; // Bad performance @@ -210,6 +214,9 @@ void vg::PostProcessBloom::render() { // Restore original FBO glBindFramebuffer(GL_FRAMEBUFFER, initial_fbo); renderStage(m_programGaussianSecond); + + // TODO(Ben): Need state manager + glEnable(GL_DEPTH_TEST); } void vg::PostProcessBloom::dispose() { @@ -228,7 +235,6 @@ void vg::PostProcessBloom::renderStage(vg::GLProgram& program) { program.use(); program.enableVertexAttribArrays(); - glDisable(GL_DEPTH_TEST); m_quad.draw(); glEnable(GL_DEPTH_TEST); @@ -254,4 +260,22 @@ void vg::PostProcessBloom::uploadUniforms() { glUniform1i(m_programGaussianSecond.getUniform("unWidth"), m_windowWidth); glUniform1i(m_programGaussianSecond.getUniform("unGaussianN"), m_gaussianN); m_programGaussianSecond.unuse(); + + // Calculate gaussian weights + f32 weights[50], sum; + weights[0] = gauss(0, m_gaussianVariance); + sum = weights[0]; + for (ui32 i = 1; i < m_gaussianN; i++) { + weights[i] = gauss(i, m_gaussianVariance); + sum += 2 * weights[i]; + } + for (ui32 i = 0; i < m_gaussianN; i++) { + weights[i] = weights[i] / sum; + } + m_programGaussianFirst.use(); + glUniform1fv(m_programGaussianFirst.getUniform("unWeight[0]"), m_gaussianN, weights); + m_programGaussianFirst.unuse(); + m_programGaussianSecond.use(); + glUniform1fv(m_programGaussianSecond.getUniform("unWeight[0]"), m_gaussianN, weights); + m_programGaussianSecond.unuse(); } \ No newline at end of file From d0a46425355022aeac3a303b04895eb0a0efd7c2 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Sat, 25 Jun 2016 18:48:56 -0700 Subject: [PATCH 06/27] Added GL error checking to vorb --- include/Graphics.h | 43 +++++++++++++++++++++++++++++++++++++++++++ src/ui/MainGame.cpp | 9 +++++++++ 2 files changed, 52 insertions(+) diff --git a/include/Graphics.h b/include/Graphics.h index 106442583..7d1987d54 100644 --- a/include/Graphics.h +++ b/include/Graphics.h @@ -20,6 +20,49 @@ #if defined(VORB_IMPL_GRAPHICS_OPENGL) #include + +// GL Error checking +// TODO(Ben): This could go somewhere else. +namespace vorb { + namespace graphics { + inline bool checkGlError(const nString& descriptor, OUT nString& errorMsg) { + GLenum error = glGetError(); + if (error != GL_NO_ERROR) { + switch (error) { + case GL_INVALID_ENUM: + errorMsg = ("GL error at " + descriptor + ". Error code 1280: GL_INVALID_ENUM"); + break; + case GL_INVALID_VALUE: + errorMsg = ("GL error at " + descriptor + ". Error code 1281: GL_INVALID_VALUE"); + break; + case GL_INVALID_OPERATION: + errorMsg = ("GL error at " + descriptor + ". Error code 1282: GL_INVALID_OPERATION"); + break; + case GL_STACK_OVERFLOW: + errorMsg = ("GL error at " + descriptor + ". Error code 1283: GL_STACK_OVERFLOW"); + break; + case GL_STACK_UNDERFLOW: + errorMsg = ("GL error at " + descriptor + ". Error code 1284: GL_STACK_UNDERFLOW"); + break; + case GL_OUT_OF_MEMORY: + errorMsg = ("GL error at " + descriptor + ". Error code 1285: GL_OUT_OF_MEMORY"); + break; + case GL_INVALID_FRAMEBUFFER_OPERATION: + errorMsg = ("GL error at " + descriptor + ". Error code 1285: GL_INVALID_FRAMEBUFFER_OPERATION"); + break; + default: + errorMsg = ("GL error at " + descriptor + ". Error code " + std::to_string(error) + ": UNKNOWN"); + break; + } + return true; + } + return false; + } + } +} +namespace vg = vorb::graphics; + + #elif defined(VORB_IMPL_GRAPHICS_D3D) // Make sure we get correct DirectX version #if defined(VORB_DX_11) diff --git a/src/ui/MainGame.cpp b/src/ui/MainGame.cpp index fb46886d5..edbf32866 100644 --- a/src/ui/MainGame.cpp +++ b/src/ui/MainGame.cpp @@ -2,6 +2,7 @@ #include "ui/MainGame.h" #include +#include #if defined(VORB_IMPL_UI_SDL) #if defined(VORB_OS_WINDOWS) @@ -228,6 +229,14 @@ void vui::MainGame::onRenderFrame() { m_renderer.renderScenes(m_curTime); m_renderer.renderPostProcesses(); + // Check for OpenGL errors +#if defined(VORB_IMPL_GRAPHICS_OPENGL) +#ifndef defined(NDEBUG) + nString msg; + vorb_assert(!vg::checkGlError("vui::MainGame::onRenderFrame()", msg), msg.c_str()); +#endif +#endif + #if defined(VORB_IMPL_GRAPHICS_D3D) #if defined(VORB_DX_9) VG_DX_DEVICE(m_window.getContext())->EndScene(); From 53a9952d0237c45a0ababb56afe6c9ca019b8211 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Sat, 25 Jun 2016 19:07:00 -0700 Subject: [PATCH 07/27] Fixed another bloom init issue. --- src/graphics/PostProcess.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/graphics/PostProcess.cpp b/src/graphics/PostProcess.cpp index 13a89067f..b843d70b5 100644 --- a/src/graphics/PostProcess.cpp +++ b/src/graphics/PostProcess.cpp @@ -163,8 +163,8 @@ void vg::PostProcessBloom::load() { m_quad.init(); // initialize FBOs m_fbos[0].setSize(m_windowWidth, m_windowHeight); - m_fbos[0].setSize(m_windowWidth, m_windowHeight); - m_fbos[1].init(vg::TextureInternalFormat::RGBA32F, 0, vg::TextureFormat::RGBA, vg::TexturePixelType::FLOAT); + m_fbos[1].setSize(m_windowWidth, m_windowHeight); + m_fbos[0].init(vg::TextureInternalFormat::RGBA32F, 0, vg::TextureFormat::RGBA, vg::TexturePixelType::FLOAT); m_fbos[1].init(vg::TextureInternalFormat::RGBA32F, 0, vg::TextureFormat::RGBA, vg::TexturePixelType::FLOAT); // Load shaders From e5c3bd49228f29720b0e227dcea02ce67f1d9c87 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Sat, 25 Jun 2016 21:47:23 -0700 Subject: [PATCH 08/27] GBuffer rendering, optimized bloom. Working first pass. --- UnitTests/VorbGraphics.cpp | 76 ++++++++++++------------ UnitTests/VorbUI.cpp | 2 + include/graphics/GBuffer.h | 16 ++--- include/graphics/PostProcess.h | 25 +++++++- include/graphics/Renderer.h | 10 +++- include/graphics/ShaderCommon.inl | 12 ++++ include/ui/IGameScreen.h | 2 +- src/graphics/GBuffer.cpp | 8 +-- src/graphics/PostProcess.cpp | 97 ++++++++++++++++++------------- src/graphics/Renderer.cpp | 51 +++++++++++++++- 10 files changed, 205 insertions(+), 94 deletions(-) diff --git a/UnitTests/VorbGraphics.cpp b/UnitTests/VorbGraphics.cpp index 576ffccc3..9f59c541d 100644 --- a/UnitTests/VorbGraphics.cpp +++ b/UnitTests/VorbGraphics.cpp @@ -33,19 +33,19 @@ struct ImageTestFormats { class ImageViewer : public vui::IGameScreen { public: - virtual i32 getNextScreen() const { + virtual i32 getNextScreen() const override { return SCREEN_INDEX_NO_SCREEN; } - virtual i32 getPreviousScreen() const { + virtual i32 getPreviousScreen() const override { return SCREEN_INDEX_NO_SCREEN; } - virtual void build() { + virtual void build() override { } - virtual void destroy(const vui::GameTime& gameTime) { + virtual void destroy(const vui::GameTime& gameTime) override { } - virtual void onEntry(const vui::GameTime& gameTime) { + virtual void onEntry(const vui::GameTime& gameTime) override { m_imageFormat = m_testFormats[0]; m_hooks.addAutoHook(vui::InputDispatcher::window.onFile, [&] (Sender, const vui::WindowFileEvent& e) { auto bmp = vg::ImageIO().load(e.file, m_imageFormat.format); @@ -95,7 +95,7 @@ class ImageViewer : public vui::IGameScreen { vg::SamplerState::LINEAR_WRAP.set(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); } - virtual void onExit(const vui::GameTime& gameTime) { + virtual void onExit(const vui::GameTime& gameTime) override { m_sb.dispose(); if (m_bmp.data) vg::ImageIO::free(m_bmp); if (m_tex.id != 0) glDeleteTextures(1, &m_tex.id); @@ -104,7 +104,7 @@ class ImageViewer : public vui::IGameScreen { virtual void registerRendering(vg::Renderer& renderer) override { } - virtual void update(const vui::GameTime& gameTime) { + virtual void update(const vui::GameTime& gameTime) override { if (m_bmp.data) { glBindTexture(GL_TEXTURE_2D, m_tex.id); m_tex.width = m_bmp.width; @@ -120,7 +120,7 @@ class ImageViewer : public vui::IGameScreen { m_bmp = {}; } } - virtual void draw(const vui::GameTime& gameTime) { + virtual void onRenderFrame(const vui::GameTime& gameTime) override { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); @@ -186,18 +186,18 @@ class FontViewer : public vui::IGameScreen { return SCREEN_INDEX_NO_SCREEN; } - virtual void build() { + virtual void build() override { // Empty } - virtual void destroy(const vui::GameTime& gameTime) { + virtual void destroy(const vui::GameTime& gameTime) override { // Empty } - virtual void onEntry(const vui::GameTime& gameTime) { + virtual void onEntry(const vui::GameTime& gameTime) override { batch.init(); font.init("Data/chintzy.ttf", 32); } - virtual void onExit(const vui::GameTime& gameTime) { + virtual void onExit(const vui::GameTime& gameTime) override { batch.dispose(); font.dispose(); } @@ -205,10 +205,10 @@ class FontViewer : public vui::IGameScreen { virtual void registerRendering(vg::Renderer& renderer) override { } - virtual void update(const vui::GameTime& gameTime) { + virtual void update(const vui::GameTime& gameTime) override { // Empty } - virtual void draw(const vui::GameTime& gameTime) { + virtual void onRenderFrame(const vui::GameTime& gameTime) override { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); batch.begin(); @@ -223,14 +223,14 @@ class FontViewer : public vui::IGameScreen { class TorusViewer : public vui::IGameScreen { public: - virtual i32 getNextScreen() const { + virtual i32 getNextScreen() const override { return SCREEN_INDEX_NO_SCREEN; } - virtual i32 getPreviousScreen() const { + virtual i32 getPreviousScreen() const override { return SCREEN_INDEX_NO_SCREEN; } - virtual void build() { + virtual void build() override { pitchInput = 0; yawInput = 0; pool.addAutoHook(vui::InputDispatcher::key.onKeyDown, [&] (Sender, const vui::KeyEvent& e) { @@ -251,11 +251,11 @@ class TorusViewer : public vui::IGameScreen { } }); } - virtual void destroy(const vui::GameTime& gameTime) { + virtual void destroy(const vui::GameTime& gameTime) override { pool.dispose(); } - virtual void onEntry(const vui::GameTime& gameTime) { + virtual void onEntry(const vui::GameTime& gameTime) override { glGenBuffers(1, &verts); glGenBuffers(1, &inds); glGenVertexArrays(1, &vdecl); @@ -313,7 +313,7 @@ void main() { spriteBatch.init(); } - virtual void onExit(const vui::GameTime& gameTime) { + virtual void onExit(const vui::GameTime& gameTime) override { glDeleteBuffers(1, &verts); glDeleteBuffers(1, &inds); glDeleteVertexArrays(1, &vdecl); @@ -324,13 +324,13 @@ void main() { virtual void registerRendering(vg::Renderer& renderer) override { } - virtual void update(const vui::GameTime& gameTime) { + virtual void update(const vui::GameTime& gameTime) override { yaw += (f32)(gameTime.elapsed * yawInput); pitch += (f32)(gameTime.elapsed * pitchInput); yaw = fmod(yaw + 6.28f, 6.28f); pitch = fmod(pitch + 6.28f, 6.28f); } - virtual void draw(const vui::GameTime& gameTime) { + virtual void onRenderFrame(const vui::GameTime& gameTime) override { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); f32v3 eye; @@ -385,21 +385,21 @@ void main() { class SpriteBatchTester : public vui::IGameScreen { public: - virtual i32 getNextScreen() const { + virtual i32 getNextScreen() const override { return SCREEN_INDEX_NO_SCREEN; } - virtual i32 getPreviousScreen() const { + virtual i32 getPreviousScreen() const override { return SCREEN_INDEX_NO_SCREEN; } - virtual void build() { + virtual void build() override { // Empty } - virtual void destroy(const vui::GameTime& gameTime) { + virtual void destroy(const vui::GameTime& gameTime) override { // Empty } - virtual void onEntry(const vui::GameTime& gameTime) { + virtual void onEntry(const vui::GameTime& gameTime) override { batch.init(); // Init sprites @@ -423,17 +423,17 @@ class SpriteBatchTester : public vui::IGameScreen { vg::SamplerState::POINT_WRAP.set(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, 0); } - virtual void onExit(const vui::GameTime& gameTime) { + virtual void onExit(const vui::GameTime& gameTime) override { batch.dispose(); } virtual void registerRendering(vg::Renderer& renderer) override { } - virtual void update(const vui::GameTime& gameTime) { + virtual void update(const vui::GameTime& gameTime) override { angle += 0.1f; } - virtual void draw(const vui::GameTime& gameTime) { + virtual void onRenderFrame(const vui::GameTime& gameTime) override { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); timer.start(); @@ -533,19 +533,19 @@ class AnimViewer : public vui::IGameScreen { mWorld[bone.index] = mWorld[bone.index] * mRestInv[bone.index]; } - virtual i32 getNextScreen() const { + virtual i32 getNextScreen() const override { return SCREEN_INDEX_NO_SCREEN; } - virtual i32 getPreviousScreen() const { + virtual i32 getPreviousScreen() const override { return SCREEN_INDEX_NO_SCREEN; } - virtual void build() { + virtual void build() override { } - virtual void destroy(const vui::GameTime& gameTime) { + virtual void destroy(const vui::GameTime& gameTime) override { } - virtual void onEntry(const vui::GameTime& gameTime) { + virtual void onEntry(const vui::GameTime& gameTime) override { std::unordered_map attrmap; vg::VertexAttributeIndexed vai; vai.type = vg::VertexAttributeUsage::Position; @@ -677,7 +677,7 @@ void main() { glClearColor(1, 1, 1, 1); glClearDepth(1.0); } - virtual void onExit(const vui::GameTime& gameTime) { + virtual void onExit(const vui::GameTime& gameTime) override { delete[] skeleton.bones; delete[] skeleton.frames; delete[] skeleton.childrenArray; @@ -688,9 +688,9 @@ void main() { virtual void registerRendering(vg::Renderer& renderer) override { } - virtual void update(const vui::GameTime& gameTime) { + virtual void update(const vui::GameTime& gameTime) override { } - virtual void draw(const vui::GameTime& gameTime) { + virtual void onRenderFrame(const vui::GameTime& gameTime) override { vg::DepthState::FULL.set(); vg::RasterizerState::CULL_NONE.set(); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); diff --git a/UnitTests/VorbUI.cpp b/UnitTests/VorbUI.cpp index 7b1cf2aef..d8ffccf6a 100644 --- a/UnitTests/VorbUI.cpp +++ b/UnitTests/VorbUI.cpp @@ -46,6 +46,8 @@ class TestScreen : public vui::IGameScreen { } virtual void registerRendering(vg::Renderer& renderer) override { } + virtual void onRenderFrame(const vui::GameTime& gameTime) override { + } virtual void update(const vui::GameTime& gameTime) override { } }; diff --git a/include/graphics/GBuffer.h b/include/graphics/GBuffer.h index b4c60b7c2..71f0671d6 100644 --- a/include/graphics/GBuffer.h +++ b/include/graphics/GBuffer.h @@ -69,17 +69,16 @@ namespace vorb { /// Create the value-based render targets /// @return Self - GBuffer& init(const Array& attachments, vg::TextureInternalFormat lightFormat); + GBuffer& init(const std::vector& attachments, vg::TextureInternalFormat lightFormat); /// Attach a depth buffer to this GBuffer /// @param depthFormat: Precision used for depth buffer /// @return Self GBuffer& initDepth(TextureInternalFormat depthFormat = TextureInternalFormat::DEPTH_COMPONENT32); - /// Attack a depth and stencil buffer to this GBuffer + /// Attach a depth and stencil buffer to this GBuffer /// @param depthFormat: Precision used for depth and stencil buffer /// @return Self GBuffer& initDepthStencil(TextureInternalFormat depthFormat = TextureInternalFormat::DEPTH24_STENCIL8); - void initTarget(const ui32v2& _size, const ui32& texID, const GBufferAttachment& attachment); /// Destroy all render targets void dispose(); @@ -139,12 +138,15 @@ namespace vorb { return m_texDepth; } private: + + void initTarget(const ui32v2& _size, const ui32& texID, const GBufferAttachment& attachment); + ui32v2 m_size; ///< The width and height of the GBuffer - VGFramebuffer m_fboGeom; ///< The rendering target for geometry - VGFramebuffer m_fboLight; ///< The rendering target for light - Array m_textures; ///< An array of all the textures - VGTexture m_texDepth = 0; ///< Depth texture of GBuffer + VGFramebuffer m_fboGeom; ///< The rendering target for geometry + VGFramebuffer m_fboLight; ///< The rendering target for light + std::vector m_textures; ///< An array of all the textures + VGTexture m_texDepth = 0; ///< Depth texture of GBuffer }; } } diff --git a/include/graphics/PostProcess.h b/include/graphics/PostProcess.h index 7d5db3fd0..5148cc999 100644 --- a/include/graphics/PostProcess.h +++ b/include/graphics/PostProcess.h @@ -67,9 +67,10 @@ namespace graphics { /************************************************************************/ /* Bloom */ /************************************************************************/ + /// Renders the bloom post process. class PostProcessBloom : public IPostProcess { public: - void init (ui32 windowWidth, ui32 windowHeight); + void init (ui32 windowWidth, ui32 windowHeight, ui32 renderFBO = 0); void setParams(ui32 gaussianN = 20, f32 gaussianVariance = 36.0f, f32 lumaThreshold = 0.75f); void load () override; virtual void render () override; @@ -91,11 +92,33 @@ namespace graphics { ui32 m_windowWidth = 0; ui32 m_windowHeight = 0; + ui32 m_renderFBO = 0; ///< FBO to render the final image to + /// Parameters ui32 m_gaussianN = 20; ///< Threshold for filtering image luma for bloom bluring f32 m_gaussianVariance = 36.0f; ///< Radius number for gaussian blur. Must be less than 50. f32 m_lumaThreshold = 0.75f; ///< Gaussian variance for blur pass }; + + /************************************************************************/ + /* Passthrough */ + /************************************************************************/ + /// Simply renders a texture from one FBO to another. + class PostProcessPassthrough : public IPostProcess { + public: + + void init(ui32 textureUnit) { m_textureUnit = textureUnit; } + virtual void load() override; + virtual void render() override; + virtual void dispose() override; + + private: + ui32 m_textureUnit = 0; + vg::GLProgram m_program; + + // TODO(Ben): Quad sharing + vg::FullQuadVBO m_quad; + }; } } namespace vg = vorb::graphics; diff --git a/include/graphics/Renderer.h b/include/graphics/Renderer.h index 0af18d07e..27d04cfb4 100644 --- a/include/graphics/Renderer.h +++ b/include/graphics/Renderer.h @@ -33,9 +33,10 @@ namespace vorb { namespace graphics { // Forward Declare - class GLRenderTarget; + class GBuffer; class IScene; class IPostProcess; + class PostProcessPassthrough; /// Manages storage and rendering of scenes and post processes class Renderer { @@ -86,11 +87,11 @@ namespace vorb { virtual void sync(ui32 frameTime) { m_window->sync(frameTime); } /*! @brief Renders all scenes in order that they were registered - * Does not sync. Call vui::GameWindow::sync() to draw to the screen. */ virtual void renderScenes(const vui::GameTime& gameTime); /*! @brief Renders all postprocess stages. Call after renderScenes. + * You must call this even if you don't use post processing in order to render the gbuffer. */ virtual void renderPostProcesses(); @@ -112,6 +113,11 @@ namespace vorb { bool m_isInitialized = false; vui::GameWindow* m_window = nullptr; ///< The window we are targeting. + std::unique_ptr m_postProcessPassthrough = nullptr; ///< Default post process when none are active + + // TODO(Ben): FBO Cache + std::unique_ptr m_gBuffer; + std::vector m_scenes; ///< Scenes that are loaded and ready to render std::vector m_sceneLoadQueue; ///< Scenes that need to load std::vector m_postProcesses; ///< PostPorcesses that are loaded and ready to render diff --git a/include/graphics/ShaderCommon.inl b/include/graphics/ShaderCommon.inl index 3699a2403..c823c512c 100644 --- a/include/graphics/ShaderCommon.inl +++ b/include/graphics/ShaderCommon.inl @@ -41,6 +41,18 @@ void main() { gl_Position = vec4(vPosition, 0, 1); })"; + const cString TEXTURE_FRAG_SRC = R"( +// Output +in vec2 fUV; + +uniform sampler2D unSampler; + +out vec4 pColor; + +void main() { + pColor = texture(unSampler, fUV); +})"; + } } } diff --git a/include/ui/IGameScreen.h b/include/ui/IGameScreen.h index b23165344..b1070c9a2 100644 --- a/include/ui/IGameScreen.h +++ b/include/ui/IGameScreen.h @@ -96,7 +96,7 @@ namespace vorb { * It is recommended to do rendering via Scenes and PostProcesses rather than here, * but you can optionally do rendering here. */ - virtual void onRenderFrame(const vui::GameTime& gameTime) {}; + virtual void onRenderFrame(const vui::GameTime& gameTime) = 0; /*! @brief Called In the main update loop. */ diff --git a/src/graphics/GBuffer.cpp b/src/graphics/GBuffer.cpp index a68f47df3..7c04d1032 100644 --- a/src/graphics/GBuffer.cpp +++ b/src/graphics/GBuffer.cpp @@ -18,9 +18,9 @@ void vg::GBuffer::initTarget(const ui32v2& _size, const ui32& texID, const vg::G SamplerState::POINT_CLAMP.set(GL_TEXTURE_2D); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + attachment.number, GL_TEXTURE_2D, texID, 0); } -vg::GBuffer& vg::GBuffer::init(const Array& attachments, vg::TextureInternalFormat lightFormat) { +vg::GBuffer& vg::GBuffer::init(const std::vector& attachments, vg::TextureInternalFormat lightFormat) { // Create texture targets - m_textures.setData(attachments.size() + 1); + m_textures.resize(attachments.size() + 1); glGenTextures((GLsizei)m_textures.size(), &m_textures[0]); // Make the framebuffer @@ -105,8 +105,8 @@ void vg::GBuffer::dispose() { m_fboLight = 0; } if (m_textures.size() != 0) { - glDeleteTextures((GLsizei)m_textures.size(), &m_textures[0]); - m_textures.setData(0); + glDeleteTextures((GLsizei)m_textures.size(), m_textures.data()); + std::vector().swap(m_textures); } if (m_texDepth != 0) { glDeleteTextures(1, &m_texDepth); diff --git a/src/graphics/PostProcess.cpp b/src/graphics/PostProcess.cpp index b843d70b5..54047be10 100644 --- a/src/graphics/PostProcess.cpp +++ b/src/graphics/PostProcess.cpp @@ -27,6 +27,7 @@ void vg::IPostProcess::unregister() { #pragma region BloomShaderCode /// Bloom shaders created by Isaque Dutra on 2 June 2015 +/// Optimized and ported to Vorb by Ben Arnold on 15 June 2016 /// Copyright 2015 Regrowth Studios /// All Rights Reserved @@ -52,10 +53,8 @@ float luma(vec3 color) { } void main() { - - vec4 val = texture(unTexColor, fUV); - pColor = val * clamp( luma(val.rgb) - unLumaThresh, 0.0, 1.0 ) * (1.0 / (1.0 - unLumaThresh)); - pColor = vec4(pColor.rgb, 1.0); + vec3 val = texture(unTexColor, fUV).rgb; + pColor = vec4(val * clamp(luma(val.rgb) - unLumaThresh, 0.0, 1.0) / (1.0 - unLumaThresh), 1.0); })"; const cString BLOOM_GAUSS1_FRAG_SRC = R"( @@ -70,24 +69,22 @@ in vec2 fUV; out vec4 pColor; // uniforms -uniform int unHeight; // Window height +uniform float unInvHeight; // 1 / Window height uniform sampler2D unTexLuma; // Texture with brighest parts of image uniform int unGaussianN; // Radius for gaussian blur uniform float unWeight[50]; // Gaussian weights // first pass of gaussian blur, in the y direction void main() { - float dy = 1.0 / float(unHeight); - vec4 sum = texture(unTexLuma, fUV) * unWeight[0]; - for(int i=1; i(m_window->getWidth(), m_window->getHeight()); + + // TODO(Ben): More than color. + std::vector attachments; + { // Color + attachments.emplace_back(); + GBufferAttachment& color = attachments.back(); + color.format = GBUFFER_INTERNAL_FORMAT_COLOR; + color.pixelFormat = vg::TextureFormat::RGBA; + color.pixelType = vg::TexturePixelType::FLOAT; + color.number = 0; + } + m_gBuffer->init(attachments, GBUFFER_INTERNAL_FORMAT_LIGHT); + m_gBuffer->initDepth(); } void vg::Renderer::load() { @@ -108,9 +125,11 @@ void vg::Renderer::setBackgroundColor(const f32v4& color) { void vg::Renderer::beginRenderFrame() { + // Bind the GBuffer for drawing + m_gBuffer->useGeometry(); + #if defined(VORB_IMPL_GRAPHICS_OPENGL) - glViewport(0, 0, m_window->getWidth(), m_window->getHeight()); // TODO(Ben): Allow optional clearing glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT); @@ -148,9 +167,33 @@ void vg::Renderer::renderScenes(const vui::GameTime& gameTime) { } void vg::Renderer::renderPostProcesses() { + + // Disable FBO (Render to screen) + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glViewport(0, 0, m_window->getWidth(), m_window->getHeight()); + + // Bind color + m_gBuffer->bindGeometryTexture(0, 0); + + // Do all post processing for (IPostProcess* p : m_postProcesses) { p->render(); } + + // If we have no post processes, we have to render the g buffer to the screen manually. + if (m_postProcesses.empty()) { + // Lazy load + if (!m_postProcessPassthrough) { + m_postProcessPassthrough = std::make_unique(); + m_postProcessPassthrough->init(0); + m_postProcessPassthrough->load(); + } + // Bind color + m_gBuffer->bindGeometryTexture(0, 0); + // Render color to screen + m_postProcessPassthrough->render(); + } + } void vg::Renderer::dispose() { @@ -172,5 +215,11 @@ void vg::Renderer::dispose() { } std::vector().swap(m_postProcessesLoadQueue); + if (m_postProcessPassthrough) { + m_postProcessPassthrough->dispose(); + m_postProcessPassthrough.reset(); + m_postProcessPassthrough = nullptr; + } + m_isInitialized = false; } From 261e17673e5ac5f98064c25d8404a0ef4b360298 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Sun, 26 Jun 2016 10:27:06 -0700 Subject: [PATCH 09/27] Added Camera implementation --- include/graphics/Camera.h | 104 ++++++++++++++++++++++++++++++++++- src/graphics/Camera.cpp | 100 ++++++++++++++++++++++++++++++++- src/graphics/PostProcess.cpp | 4 +- 3 files changed, 203 insertions(+), 5 deletions(-) diff --git a/include/graphics/Camera.h b/include/graphics/Camera.h index 382e258c2..90adb07e9 100644 --- a/include/graphics/Camera.h +++ b/include/graphics/Camera.h @@ -23,15 +23,115 @@ #ifndef VORB_USING_PCH #include "types.h" #endif // !VORB_USING_PCH + +#include "Frustum.h" + namespace vorb { namespace graphics { class Camera { public: + Camera(); + virtual ~Camera(); + + /*! @brief Sets up the camera. + */ + virtual void init(f32 aspectRatio); + + /*! @brief Updates the camera matrices. Call every frame before render. + */ + virtual void update(); + + /*! @brief Check if a point is in the camera frustum. + */ + bool pointInFrustum (const f32v3& pos) const { return m_frustum.pointInFrustum(pos); } + /*! @brief Check if a sphere is in the camera frustum. + */ + bool sphereInFrustum(const f32v3& pos, f32 radius) const { return m_frustum.sphereInFrustum(pos, radius); } + + /************************************************************************/ + /* Mutators */ + /************************************************************************/ + /*! @brief Translate the camera. + */ + void offsetPosition(const f32v3& offset); + void offsetPosition(const f64v3& offset); + /*! @brief Apply a quaternion rotation to the camera. + */ + virtual void rotate(const f32q& rot); + /*! @brief Rotate with pitch and yaw. + */ + virtual void rotate(f32 yaw, f32 pitch); + /*! @brief Rotate with pitch and yaw in an FPS style with (0, 1, 0) as the UP vector. + * @param clampVerticalRotation: Set to true to not allow rotation beyond absolute up or down. + */ + virtual void rotateAbsoluteUp(f32 yaw, f32 pitch, bool clampVerticalRotation = false); + + /*! @brief Apply roll rotation only. + */ + virtual void roll(f32 roll); + + void setOrientation (const f64q& orientation); + void setFocalPoint (const f64v3& focalPoint) { m_focalPoint = focalPoint; m_viewChanged = true; } + void setPosition (const f64v3& position) { m_focalPoint = m_position = position; m_focalLength = 0; m_viewChanged = true; } + void setDirection (const f32v3& direction) { m_direction = direction; m_viewChanged = true; } + void setRight (const f32v3& right) { m_right = right; m_viewChanged = true; } + void setUp (const f32v3& up) { m_up = up; m_viewChanged = true; } + void setClippingPlane(f32 zNear, f32 zFar) { m_zNear = zNear; m_zFar = zFar; m_projectionChanged = true; } + void setFieldOfView (f32 fieldOfView) { m_fieldOfView = fieldOfView; m_projectionChanged = true; } + void setFocalLength (f32 focalLength) { m_focalLength = focalLength; m_viewChanged = true; } + void setAspectRatio (f32 aspectRatio) { m_aspectRatio = aspectRatio; m_projectionChanged = true; } + + /************************************************************************/ + /* Accessors */ + /************************************************************************/ + const f64v3& getPosition () const { return m_position; } + const f64v3& getFocalPoint () const { return m_focalPoint; } + const f64& getFocalLength () const { return m_focalLength; } + const f64& getMaxFocalLength() const { return m_maxFocalLength; } + + const f32v3& getDirection() const { return m_direction; } + const f32v3& getRight () const { return m_right; } + const f32v3& getUp () const { return m_up; } + const f32m4& getProjectionMatrix () const { return m_projectionMatrix; } + const f32m4& getViewMatrix () const { return m_viewMatrix; } + const f32m4& getViewProjectionMatrix() const { return m_viewProjectionMatrix; } + + const f32& getNearClip () const { return m_zNear; } + const f32& getFarClip () const { return m_zFar; } + const f32& getFieldOfView() const { return m_fieldOfView; } + const f32& getAspectRatio() const { return m_aspectRatio; } + + const Frustum& getFrustum() const { return m_frustum; } private: - f64v3 m_focalPoint = f64v3(0.0); - f64v3 m_position = f64v3(0.0); + void updateView (); + void updateProjection(); + + f64v3 m_focalPoint = f64v3(0.0); + f64v3 m_position = f64v3(0.0); + f64 m_focalLength = 0.0; + f64 m_maxFocalLength = 0.0; + + f32 m_zNear = 0.1f; + f32 m_zFar = 10000.0f; + f32 m_fieldOfView = 75.0f; + f32 m_aspectRatio = 0.0f; + + f32v3 m_direction = f32v3(1.0f, 0.0f, 0.0f); + f32v3 m_right = f32v3(0.0f, 0.0f, 1.0f); + f32v3 m_up = f32v3(0.0f, 1.0f, 0.0f); + + f32m4 m_projectionMatrix; + f32m4 m_viewMatrix; + f32m4 m_viewProjectionMatrix; + + bool m_viewChanged = true; + bool m_projectionChanged = true; + + Frustum m_frustum; + + static const f32v3 UP_ABSOLUTE; }; } diff --git a/src/graphics/Camera.cpp b/src/graphics/Camera.cpp index 028186eff..61f510144 100644 --- a/src/graphics/Camera.cpp +++ b/src/graphics/Camera.cpp @@ -1,2 +1,100 @@ #include "stdafx.h" -#include "graphics/Camera.h" \ No newline at end of file +#include "graphics/Camera.h" + +const f32v3 vg::Camera::UP_ABSOLUTE = f32v3(0.0f, 1.0f, 0.0f); + +vg::Camera::Camera() { + // Empty +} + +vg::Camera::~Camera() { + +} + +void vg::Camera::init(f32 aspectRatio) { + m_aspectRatio = aspectRatio; +} + +void vg::Camera::update() { + bool updateFrustum = false; + if (m_viewChanged) { + updateView(); + m_viewChanged = false; + updateFrustum = true; + } + if (m_projectionChanged) { + updateProjection(); + m_projectionChanged = false; + updateFrustum = true; + } + + if (updateFrustum) { + m_viewProjectionMatrix = m_projectionMatrix * m_viewMatrix; + m_frustum.updateFromWVP(m_viewProjectionMatrix); + } +} + +void vg::Camera::offsetPosition(const f32v3& offset) { + m_position += offset; + m_viewChanged = true; +} + +void vg::Camera::offsetPosition(const f64v3& offset) { + m_position += offset; + m_viewChanged = true; +} + +void vg::Camera::rotate(const f32q& rot) { + m_direction = rot * m_direction; + m_right = rot * m_right; + m_up = vmath::normalize(vmath::cross(m_right, m_direction)); + + m_viewChanged = true; +} + +void vg::Camera::rotate(f32 yaw, f32 pitch) { + f32q upQuat = vmath::angleAxis(pitch, m_right); + f32q rightQuat = vmath::angleAxis(yaw, m_up); + + rotate(upQuat * rightQuat); +} + +void vg::Camera::rotateAbsoluteUp(f32 yaw, f32 pitch, bool clampVerticalRotation /*= false*/) { + f32q upQuat = vmath::angleAxis(pitch, m_right); + f32q rightQuat = vmath::angleAxis(yaw, UP_ABSOLUTE); + + f32v3 previousDirection = m_direction; + f32v3 previousUp = m_up; + f32v3 previousRight = m_right; + + rotate(upQuat * rightQuat); + + if (clampVerticalRotation && m_up.y < 0) { + m_direction = previousDirection; + m_up = previousUp; + m_right = previousRight; + rotateAbsoluteUp(yaw, 0.0f); + } +} + +void vg::Camera::roll(f32 roll) { + f32q frontQuat = vmath::angleAxis(roll, m_direction); + + rotate(frontQuat); +} + +void vg::Camera::setOrientation(const f64q& orientation) { + m_direction = orientation * f64v3(0.0, 0.0, 1.0); + m_right = orientation * f64v3(1.0, 0.0, 0.0); + m_up = orientation * f64v3(0.0, 1.0, 0.0); + m_viewChanged = true; +} + +void vg::Camera::updateView() { + m_viewMatrix = vmath::lookAt(f32v3(0.0f), m_direction, m_up); +} + +void vg::Camera::updateProjection() { + m_frustum.setCamInternals(m_fieldOfView, m_aspectRatio, m_zNear, m_zFar); + m_projectionMatrix = vmath::perspective(m_fieldOfView, m_aspectRatio, m_zNear, m_zFar); +} diff --git a/src/graphics/PostProcess.cpp b/src/graphics/PostProcess.cpp index 54047be10..ed2406149 100644 --- a/src/graphics/PostProcess.cpp +++ b/src/graphics/PostProcess.cpp @@ -79,7 +79,7 @@ void main() { vec3 sum = texture(unTexLuma, fUV).rgb * unWeight[0]; float offset = unInvHeight; - for(int i = 1; i <= unGaussianN; i++) { + for(int i = 1; i < unGaussianN; i++) { sum += (texture(unTexLuma, vec2(fUV.x, fUV.y + offset)).rgb + texture(unTexLuma, vec2(fUV.x, fUV.y - offset)).rgb) * unWeight[i]; offset += unInvHeight; @@ -117,7 +117,7 @@ void main() { vec3 val = texture(unTexColor, fUV).rgb; vec3 sum = texture(unTexBlur, fUV).rgb * unWeight[0]; float offset = unInvWidth; - for(int i = 1; i <= unGaussianN; i++) { + for(int i = 1; i < unGaussianN; i++) { sum += (texture(unTexBlur, vec2(fUV.x + offset, fUV.y)).rgb + texture(unTexBlur, vec2(fUV.x - offset, fUV.y)).rgb) * unWeight[i]; offset += unInvWidth; From 4974695d61ab2aeb28bc1d8a0e412d944ffa943d Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Sun, 26 Jun 2016 16:52:25 -0700 Subject: [PATCH 10/27] Better camera orientation control --- include/graphics/Camera.h | 9 ++++++++- src/graphics/Camera.cpp | 8 ++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/include/graphics/Camera.h b/include/graphics/Camera.h index 90adb07e9..a69da284a 100644 --- a/include/graphics/Camera.h +++ b/include/graphics/Camera.h @@ -35,6 +35,7 @@ namespace graphics { virtual ~Camera(); /*! @brief Sets up the camera. + * Does not set up the view or projection matrices, you must call update(). */ virtual void init(f32 aspectRatio); @@ -71,7 +72,13 @@ namespace graphics { */ virtual void roll(f32 roll); - void setOrientation (const f64q& orientation); + /*! @brief Sets orientation using a direction. Up and direction need not be orthogonal, direction will take priority. + */ + void setOrientation(const f32v3& direction, const f32v3& up); + /*! @brief Sets orientation to a quaternion. + */ + void setOrientation(const f64q& orientation); + void setFocalPoint (const f64v3& focalPoint) { m_focalPoint = focalPoint; m_viewChanged = true; } void setPosition (const f64v3& position) { m_focalPoint = m_position = position; m_focalLength = 0; m_viewChanged = true; } void setDirection (const f32v3& direction) { m_direction = direction; m_viewChanged = true; } diff --git a/src/graphics/Camera.cpp b/src/graphics/Camera.cpp index 61f510144..634d95c67 100644 --- a/src/graphics/Camera.cpp +++ b/src/graphics/Camera.cpp @@ -83,6 +83,14 @@ void vg::Camera::roll(f32 roll) { rotate(frontQuat); } +void vg::Camera::setOrientation(const f32v3& direction, const f32v3& up) { + m_direction = direction; + m_up = up; + m_right = vmath::normalize(vmath::cross(m_up, m_direction)); + // We calculate up again to guarantee orthogonality + m_up = vmath::normalize(vmath::cross(m_right, m_direction)); +} + void vg::Camera::setOrientation(const f64q& orientation) { m_direction = orientation * f64v3(0.0, 0.0, 1.0); m_right = orientation * f64v3(1.0, 0.0, 0.0); From 317c5e9e068ef581492081570ab6e00c97aefbf6 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Sun, 26 Jun 2016 18:19:13 -0700 Subject: [PATCH 11/27] Static function pointers for spritebatch --- include/graphics/Scene.h | 5 +++-- include/graphics/SpriteBatch.h | 10 +++++----- src/graphics/SpriteBatch.cpp | 2 +- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/include/graphics/Scene.h b/include/graphics/Scene.h index e9f0019e6..a4eb43eff 100644 --- a/include/graphics/Scene.h +++ b/include/graphics/Scene.h @@ -46,8 +46,9 @@ namespace vorb { // You should implement your own initialization logic - /*! @brief Sets up the camera. - * Override this to provide a custom camera implementation. + /*! @brief Allocates the Camera. + * Override this to provide a custom camera implementation or set up camera location. + * Does not get called by renderer, you must call this manually. */ virtual void initCamera(); diff --git a/include/graphics/SpriteBatch.h b/include/graphics/SpriteBatch.h index 530455b5d..3815be246 100644 --- a/include/graphics/SpriteBatch.h +++ b/include/graphics/SpriteBatch.h @@ -91,7 +91,7 @@ namespace vorb { static void disposeProgram(); private: struct Glyph; struct Vertex; - typedef void(SpriteBatch::*QuadBuildFunc)(const Glyph*, Vertex*); + typedef void(*QuadBuildFunc)(const Glyph*, Vertex*); struct Glyph { Glyph(QuadBuildFunc f, VGTexture tex, const f32v4& uvRect, const f32v2& uvTiling, const f32v2& position, const f32v2& offset, const f32v2& size, f32 rotation, const color4& tint, f32 depth); @@ -133,12 +133,12 @@ namespace vorb { static bool SSMBackToFront(Glyph* g1, Glyph* g2) { return g1->depth > g2->depth; } /// Quad builders - void buildQuad(const Glyph* g, Vertex* verts); - void buildQuadOffset(const Glyph* g, Vertex* verts); - void buildQuadRotated(const Glyph* g, Vertex* verts); + static void buildQuad(const Glyph* g, Vertex* verts); + static void buildQuadOffset(const Glyph* g, Vertex* verts); + static void buildQuadRotated(const Glyph* g, Vertex* verts); /// For color gradients - void calcColor(Vertex& vtl, Vertex& vtr, Vertex& vbl, Vertex& vbr, const Glyph* g); + static void calcColor(Vertex& vtl, Vertex& vtr, Vertex& vbl, Vertex& vbr, const Glyph* g); std::vector m_glyphs; ///< Glyph data std::vector m_glyphPtrs; ///< Pointers to glyphs for fast sorting diff --git a/src/graphics/SpriteBatch.cpp b/src/graphics/SpriteBatch.cpp index 872a9b6fe..da8e41478 100644 --- a/src/graphics/SpriteBatch.cpp +++ b/src/graphics/SpriteBatch.cpp @@ -372,7 +372,7 @@ void vg::SpriteBatch::generateBatches() { m_batches.back().set(indexCount, g->tex); } // Call builder function - (this->*g->func)(g, verts + vi); + g->func(g, verts + vi); vi += VERTS_PER_QUAD; indexCount += INDICES_PER_QUAD; } From 8025d5cc4257ed11195336227032015026b0a10f Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Sun, 26 Jun 2016 18:30:50 -0700 Subject: [PATCH 12/27] Not plural. --- include/graphics/Renderer.h | 4 ++-- src/graphics/PostProcess.cpp | 4 ++-- src/graphics/Renderer.cpp | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/graphics/Renderer.h b/include/graphics/Renderer.h index 27d04cfb4..4c6bea7cb 100644 --- a/include/graphics/Renderer.h +++ b/include/graphics/Renderer.h @@ -65,13 +65,13 @@ namespace vorb { /*! @brief Registers a PostProcess for load and then rendering. */ - virtual void registerPostProcesses(IPostProcess* postProcess); + virtual void registerPostProcess(IPostProcess* postProcess); /*! @brief Unregisters a PostProcess for rendering. * Does not dispose the PostProcess. * @return true on success. */ - virtual bool unregisterPostProcesses(IPostProcess* postProcess); + virtual bool unregisterPostProcess(IPostProcess* postProcess); /*! @Brief sets background color for window */ diff --git a/src/graphics/PostProcess.cpp b/src/graphics/PostProcess.cpp index ed2406149..e51675487 100644 --- a/src/graphics/PostProcess.cpp +++ b/src/graphics/PostProcess.cpp @@ -13,7 +13,7 @@ void vg::IPostProcess::unregister() { vorb_assert(m_renderer, "PostProcess unregistered without having renderer."); - m_renderer->unregisterPostProcesses(this); + m_renderer->unregisterPostProcess(this); } /************************************************************************/ @@ -224,7 +224,7 @@ void vg::PostProcessBloom::dispose() { // Unregister if we need to. if (m_renderer) { - m_renderer->unregisterPostProcesses(this); + m_renderer->unregisterPostProcess(this); } } diff --git a/src/graphics/Renderer.cpp b/src/graphics/Renderer.cpp index d418ed7ac..fafc4e318 100644 --- a/src/graphics/Renderer.cpp +++ b/src/graphics/Renderer.cpp @@ -80,7 +80,7 @@ bool vg::Renderer::unregisterScene(IScene* scene) { return false; } -void vg::Renderer::registerPostProcesses(IPostProcess* postProcess) { +void vg::Renderer::registerPostProcess(IPostProcess* postProcess) { // Make sure the PostProcess doesn't exist vorb_assert(std::find(m_postProcesses.begin(), m_postProcesses.end(), postProcess) == m_postProcesses.end(), "PostProcess already added to SceneRenderer."); @@ -89,7 +89,7 @@ void vg::Renderer::registerPostProcesses(IPostProcess* postProcess) { m_postProcessesLoadQueue.push_back(postProcess); } -bool vg::Renderer::unregisterPostProcesses(IPostProcess* postProcess) { +bool vg::Renderer::unregisterPostProcess(IPostProcess* postProcess) { auto& it = std::find(m_postProcesses.begin(), m_postProcesses.end(), postProcess); if (it != m_postProcesses.end()) { // Remove the PostProcess From 2e6632479db9d5fdd867eb9853db2bd8b610a4fe Mon Sep 17 00:00:00 2001 From: czaloj Date: Sun, 26 Jun 2016 21:41:05 -0700 Subject: [PATCH 13/27] This actually works... --- include/Events.hpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/Events.hpp b/include/Events.hpp index a73eee9bc..78da23278 100644 --- a/include/Events.hpp +++ b/include/Events.hpp @@ -87,9 +87,9 @@ class DelegateBase { static_assert(sizeof(Deleter) == sizeof(ptrdiff_t), "Integral pointer conversion is flawed"); ~DelegateBase() { - // if (m_flagRequiresDeletion != 0) { - // ((Deleter)m_deleters[m_deleter])(m_caller); - // } + if (m_flagRequiresDeletion != 0) { + ((Deleter)m_deleters[m_deleter])(m_caller); + } } Caller m_caller; From 41939880d51b1d2de1d2a86d4f9c71a83bd5e3cf Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Sun, 3 Jul 2016 16:05:53 -0700 Subject: [PATCH 14/27] Added more voxel module code --- Vorb.vcxproj | 3 + Vorb.vcxproj.filters | 9 + include/graphics/Scene.h | 2 +- include/voxel/BlockPack.h | 135 ++++++++++ include/voxel/Chunk.h | 45 ++++ include/voxel/SmartVoxelContainer.hpp | 360 ++++++++++++++++++++++++++ src/graphics/Scene.cpp | 3 +- 7 files changed, 555 insertions(+), 2 deletions(-) create mode 100644 include/voxel/BlockPack.h create mode 100644 include/voxel/Chunk.h create mode 100644 include/voxel/SmartVoxelContainer.hpp diff --git a/Vorb.vcxproj b/Vorb.vcxproj index 63cf0e163..499b9ed67 100644 --- a/Vorb.vcxproj +++ b/Vorb.vcxproj @@ -525,7 +525,10 @@ $(SolutionDir)DepsBuildCopy.bat + + + diff --git a/Vorb.vcxproj.filters b/Vorb.vcxproj.filters index f024dce3b..bd1f1a92c 100644 --- a/Vorb.vcxproj.filters +++ b/Vorb.vcxproj.filters @@ -739,6 +739,15 @@ Graphics + + Voxel + + + Voxel + + + Voxel + diff --git a/include/graphics/Scene.h b/include/graphics/Scene.h index a4eb43eff..f009fd18d 100644 --- a/include/graphics/Scene.h +++ b/include/graphics/Scene.h @@ -50,7 +50,7 @@ namespace vorb { * Override this to provide a custom camera implementation or set up camera location. * Does not get called by renderer, you must call this manually. */ - virtual void initCamera(); + virtual void initCamera(f32 aspectRatio); /* @brief Load all graphics objects. * Gets called by SceneRenderer. diff --git a/include/voxel/BlockPack.h b/include/voxel/BlockPack.h new file mode 100644 index 000000000..23af49690 --- /dev/null +++ b/include/voxel/BlockPack.h @@ -0,0 +1,135 @@ +// +// BlockPack.h +// Vorb Engine +// +// Created by Benjamin Arnold on 3 Jul 2016 +// Copyright 2014 Regrowth Studios +// All Rights Reserved +// + +/*! \file BlockPack.h +* @brief Flyweight container for blocks. +* +* +*/ + +#pragma once + +#ifndef Vorb_BlockPack_h__ +//! @cond DOXY_SHOW_HEADER_GUARDS +#define Vorb_BlockPack_h__ +//! @endcond + +#ifndef VORB_USING_PCH +#include "types.h" +#endif // !VORB_USING_PCH + +namespace vorb { +namespace voxel { + + typedef nString BlockIdentifier; ///< Unique identifier key for blocks + + /*! @brief All custom Block types should inherit from this. + */ + template + class Block { + public: + virtual ~Block() {}; + BlockIdentifier SID; + BLOCKID ID; + }; + + /*! @brief A pack of blocks. + * You should define a BLOCK type that inherits from vvox::Block. + * INDEX should be an unsigned integer type, that is larger than the maximum number of unique blocks. + */ + template + class BlockPack { + public: + BlockPack() {} + + /*! @brief Add a block to the pack, and overwrite a block of the same BlockIdentifier + * Will invalidate existing BLOCK* pointers. Store BlockIDs instead. + * @return the BLOCKID of the new block, or of the existing block if this block is already added. + */ + BLOCKID append(BLOCK& block) { + const BLOCK* curBlock; + BLOCKID rv; + if (curBlock = hasBlock(block.SID)) { + rv = curBlock->ID; + block.ID = rv; + // Overwrite block + *const_cast(curBlock) = block; + } else { + rv = m_blockList.size(); + block.ID = rv; + // Add a new block + m_blockList.push_back(block); + // Set the correct index + m_blockMap[block.SID] = rv; + } + onBlockAddition(block.ID); + return rv; + } + + void reserveID(const BlockIdentifier& sid, const BLOCKID& id) { + if (id >= m_blockList.size()) m_blockList.resize(id + 1); + m_blockMap[sid] = id; + m_blockList[id].ID = id; + } + + + const BLOCK* hasBlock(const BLOCKID& id) const { + if (id >= m_blockList.size()) { + return nullptr; + } else { + return &m_blockList[id]; + } + } + const BLOCK* hasBlock(const BlockIdentifier& sid) const { + auto v = m_blockMap.find(sid); + if (v == m_blockMap.end()) { + return nullptr; + } else { + return &m_blockList[v->second]; + } + } + + size_t size() const { + return m_blockList.size(); + } + + /************************************************************************/ + /* BLOCK accessors */ + /************************************************************************/ + BLOCK& operator[](const size_t& index) { + return m_blockList[index]; + } + const BLOCK& operator[](const size_t& index) const { + return m_blockList[index]; + } + BLOCK& operator[](const BlockIdentifier& sid) { + return m_blockList[m_blockMap.at(sid)]; + } + const BLOCK& operator[](const BlockIdentifier& sid) const { + return m_blockList[m_blockMap.at(sid)]; + } + const INDEX& getBlockIndex(const BlockIdentifier& sid) const { + return m_blockMap.at(sid); + } + + const std::unordered_map& getBlockMap() const { return m_blockMap; } + const std::vector& getBlockList() const { return m_blockList; } + + Event onBlockAddition; ///< Signaled when a block is loaded + private: + std::unordered_map m_blockMap; ///< Blocks indices organized by identifiers + std::vector m_blockList; ///< BLOCK data list + }; + +} +} +namespace vvox = vorb::voxel; + +#endif // !Vorb_BlockPack_h__ + diff --git a/include/voxel/Chunk.h b/include/voxel/Chunk.h new file mode 100644 index 000000000..62db7582c --- /dev/null +++ b/include/voxel/Chunk.h @@ -0,0 +1,45 @@ +// +// Chunk.h +// Vorb Engine +// +// Created by Benjamin Arnold on 3 Jul 2016 +// Copyright 2014 Regrowth Studios +// All Rights Reserved +// + +/*! \file Chunk.h +* @brief Defines a voxel chunk for the vorb engine. +* +* +*/ + +#pragma once + +#ifndef Vorb_Chunk_h__ +//! @cond DOXY_SHOW_HEADER_GUARDS +#define Vorb_Chunk_h__ +//! @endcond + +#ifndef VORB_USING_PCH +#include "types.h" +#endif // !VORB_USING_PCH + +#include "SmartVoxelContainer.hpp" + +namespace vorb { +namespace voxel { + + // Type for block data and tertiary data + template + class Chunk { + public: + SmartVoxelContainer blockData; + SmartVoxelContainer tertiaryData; + }; + + +} +} +namespace vvox = vorb::voxel; + +#endif // !Vorb_Chunk_h__ diff --git a/include/voxel/SmartVoxelContainer.hpp b/include/voxel/SmartVoxelContainer.hpp new file mode 100644 index 000000000..61554475a --- /dev/null +++ b/include/voxel/SmartVoxelContainer.hpp @@ -0,0 +1,360 @@ +// +// SmartVoxelContainer.h +// Vorb Engine +// +// Created by Benjamin Arnold on 14 Nov 2014 +// Copyright 2014 Regrowth Studios +// All Rights Reserved +// +// Summary: +// +// + +#pragma once + +#ifndef SmartVoxelContainer_h__ +#define SmartVoxelContainer_h__ + +#include + +#include "../FixedSizeArrayRecycler.hpp" +#include "IntervalTree.h" + +#define QUIET_FRAMES_UNTIL_COMPRESS 60 +#define ACCESS_COUNT_UNTIL_DECOMPRESS 5 + +// TODO(Cristian): We'll see how to fit it into Vorb +namespace vorb { + namespace voxel { + + const ui32 DEFAULT_CHUNK_SIZE = 32 * 32 * 32; + + static ui32 MAX_COMPRESSIONS_PER_FRAME = UINT_MAX; ///< You can optionally set this in order to limit changes per frame + static ui32 totalContainerCompressions = 1; ///< Set this to 1 each frame + + template class SmartVoxelContainer; + + template + class SmartHandle { + friend class SmartVoxelContainer; + public: + operator const T&() const; + + SmartHandle& operator= (T data); + SmartHandle& operator= (const SmartHandle& o); + + SmartHandle(SmartHandle&& o) : + m_container(o.m_container), + m_index(o.m_index) { + // TODO: Empty for now try to null it out + } + SmartHandle(const SmartHandle& o) = delete; + SmartHandle& operator= (SmartHandle&& o) = delete; + private: + SmartHandle(SmartVoxelContainer& container, size_t index) : + m_container(container), + m_index(index) { + // Empty + } + + SmartVoxelContainer& m_container; ///< The parent container that created the handle + size_t m_index; ///< The index of this handle into the smart container + }; + + /// This should be called once per frame to reset totalContainerChanges + inline void clearContainerCompressionsCounter() { + totalContainerCompressions = 1; ///< Start at 1 so that integer overflow handles the default case + } + + enum class VoxelStorageState { + FLAT_ARRAY = 0, + INTERVAL_TREE = 1 + }; + + template + class SmartVoxelContainer { + friend class SmartHandle; + public: + /// Constructor + SmartVoxelContainer() { + // Empty + } + /*! @brief Construct the container with a provided recycler. + * + * @param arrayRecycler: The recycler to be used in place of the default generated recycler. + */ + SmartVoxelContainer(vcore::FixedSizeArrayRecycler* arrayRecycler) { + setArrayRecycler(arrayRecycler); + } + + /*! @brief Change the array recycler. + * + * @param arrayRecycler: The recycler to be used in place of the default generated recycler. + */ + void setArrayRecycler(vcore::FixedSizeArrayRecycler* arrayRecycler) { + _arrayRecycler = arrayRecycler; + } + + SmartHandle operator[] (size_t index) { + return std::move(SmartHandle(*this, index)); + } + const T& operator[] (size_t index) const { + return (getters[(size_t)_state])(this, index); + } + + /*! @brief Initializes the container + * + * @param state: Determines if compression or flat array should be used. + */ + inline void init(VoxelStorageState state) { + _state = state; + if (_state == VoxelStorageState::FLAT_ARRAY) { + _dataArray = _arrayRecycler->create(); + } + } + + /*! @brief Initializes the container + * + * @param state: Determines if compression or flat array should be used. + * @param value: Value to initialize all voxel data to. + */ + inline void init(VoxelStorageState state, T value) { + _state = state; + if (_state == VoxelStorageState::FLAT_ARRAY) { + _dataArray = _arrayRecycler->create(); + for (size_t i = 0; i < SIZE; i++) { + _dataArray[i] = value; + } + } + } + + /// Creates the tree using a sorted array of data. + /// The number of voxels should add up to the size + /// @param state: Initial state of the container + /// @param data: The sorted array used to populate the container + inline void initFromSortedArray(VoxelStorageState state, + const std::vector ::LNode>& data) { + _state = state; + _accessCount = 0; + _quietFrames = 0; + if (_state == VoxelStorageState::INTERVAL_TREE) { + _dataTree.initFromSortedArray(data); + _dataTree.checkTreeValidity(); + } else { + _dataArray = _arrayRecycler->create(); + int index = 0; + for (size_t i = 0; i < data.size(); i++) { + // TODO(Ben): Optimize + for (int j = 0; j < data[i].length; j++) { + _dataArray[index++] = data[i].data; + } + } + } + } + inline void initFromSortedArray(VoxelStorageState state, + const typename IntervalTree::LNode data[], size_t size) { + _state = state; + _accessCount = 0; + _quietFrames = 0; + if (_state == VoxelStorageState::INTERVAL_TREE) { + _dataTree.initFromSortedArray(data, size); + _dataTree.checkTreeValidity(); + } else { + _dataArray = _arrayRecycler->create(); + int index = 0; + for (size_t i = 0; i < size; i++) { + for (int j = 0; j < data[i].length; j++) { + _dataArray[index++] = data[i].data; + } + } + } + } + + inline void changeState(VoxelStorageState newState, std::mutex& dataLock) { + if (newState == _state) return; + if (newState == VoxelStorageState::INTERVAL_TREE) { + compress(dataLock); + } else { + uncompress(dataLock); + } + _quietFrames = 0; + _accessCount = 0; + } + + /// Updates the container. Call once per frame + /// @param dataLock: The mutex that guards the data + inline void update(std::mutex& dataLock) { + // If access count is higher than the threshold, this is not a quiet frame + if (_accessCount >= ACCESS_COUNT_UNTIL_DECOMPRESS) { + _quietFrames = 0; + } else { + _quietFrames++; + } + + if (_state == VoxelStorageState::INTERVAL_TREE) { + // Check if we should uncompress the data + if (_quietFrames == 0) { + uncompress(dataLock); + } + } else { + // Check if we should compress the data + if (_quietFrames >= QUIET_FRAMES_UNTIL_COMPRESS && totalContainerCompressions <= MAX_COMPRESSIONS_PER_FRAME) { + compress(dataLock); + } + } + _accessCount = 0; + } + + /// Clears the container and frees memory + inline void clear() { + _accessCount = 0; + _quietFrames = 0; + if (_state == VoxelStorageState::INTERVAL_TREE) { + _dataTree.clear(); + } else if (_dataArray) { + _arrayRecycler->recycle(_dataArray); + _dataArray = nullptr; + } + } + + /// Uncompressed the interval tree into a buffer. + /// May only be called when getState() == VoxelStorageState::INTERVAL_TREE + /// or you will get a null access violation. + /// @param buffer: Buffer of memory to store the result + inline void uncompressIntoBuffer(T* buffer) { _dataTree.uncompressIntoBuffer(buffer); } + + /// Getters + const VoxelStorageState& getState() const { + return _state; + } + T* getDataArray() { + return _dataArray; + } + const T* getDataArray() const { + return _dataArray; + } + IntervalTree& getTree() { + return _dataTree; + } + const IntervalTree& getTree() const { + return _dataTree; + } + + /// Gets the element at index + /// @param index: must be (0, SIZE] + /// @return The element + inline const T& get(size_t index) const { + return (getters[(size_t)_state])(this, index); + } + /// Sets the element at index + /// @param index: must be (0, SIZE] + /// @param value: The value to set at index + inline void set(size_t index, T value) { + _accessCount++; + (setters[(size_t)_state])(this, index, value); + } + private: + typedef const T& (*Getter)(const SmartVoxelContainer*, size_t); + typedef void(*Setter)(SmartVoxelContainer*, size_t, T); + + static const T& getInterval(const SmartVoxelContainer* container, size_t index) { + return container->_dataTree.getData(index); + } + static const T& getFlat(const SmartVoxelContainer* container, size_t index) { + return container->_dataArray[index]; + } + static void setInterval(SmartVoxelContainer* container, size_t index, T data) { + container->_dataTree.insert(index, data); + } + static void setFlat(SmartVoxelContainer* container, size_t index, T data) { + container->_dataArray[index] = data; + } + + static Getter getters[2]; + static Setter setters[2]; + + inline void uncompress(std::mutex& dataLock) { + dataLock.lock(); + _dataArray = _arrayRecycler->create(); + uncompressIntoBuffer(_dataArray); + // Free memory + _dataTree.clear(); + // Set the new state + _state = VoxelStorageState::FLAT_ARRAY; + dataLock.unlock(); + } + inline void compress(std::mutex& dataLock) { + dataLock.lock(); + // Sorted array for creating the interval tree + // Using stack array to avoid allocations, beware stack overflow + IntervalTree::LNode data[SIZE]; + int index = 0; + data[0].set(0, 1, _dataArray[0]); + // Set the data + for (int i = 1; i < SIZE; ++i) { + if (_dataArray[i] == data[index].data) { + ++(data[index].length); + } else { + data[++index].set(i, 1, _dataArray[i]); + } + } + // Set new state + _state = VoxelStorageState::INTERVAL_TREE; + // Create the tree + _dataTree.initFromSortedArray(data, index + 1); + + dataLock.unlock(); + + // Recycle memory + _arrayRecycler->recycle(_dataArray); + _dataArray = nullptr; + + totalContainerCompressions++; + } + + IntervalTree _dataTree; ///< Interval tree of voxel data + + T* _dataArray = nullptr; ///< pointer to an array of voxel data + int _accessCount = 0; ///< Number of times the container was accessed this frame + int _quietFrames = 0; ///< Number of frames since we have had heavy updates + + VoxelStorageState _state = VoxelStorageState::FLAT_ARRAY; ///< Current data structure state + + vcore::FixedSizeArrayRecycler* _arrayRecycler = nullptr; ///< For recycling the voxel arrays + }; + + /*template + inline SmartHandle::operator const T&() const { + return m_container[m_index]; + }*/ + template + inline SmartHandle::operator const T&() const { + return (m_container.getters[(size_t)m_container.getState()])(&m_container, m_index); + } + template + inline SmartHandle& SmartHandle::operator= (T data) { + m_container.set(m_index, data); + return *this; + } + template + inline SmartHandle& SmartHandle::operator= (const SmartHandle& o) { + m_container.set(m_index, o.m_container[o.m_index]); + return *this; + } + + template + typename SmartVoxelContainer::Getter SmartVoxelContainer::getters[2] = { + SmartVoxelContainer::getFlat, + SmartVoxelContainer::getInterval + }; + template + typename SmartVoxelContainer::Setter SmartVoxelContainer::setters[2] = { + SmartVoxelContainer::setFlat, + SmartVoxelContainer::setInterval + }; + + } +} +namespace vvox = vorb::voxel; + +#endif // SmartVoxelContainer_h__ \ No newline at end of file diff --git a/src/graphics/Scene.cpp b/src/graphics/Scene.cpp index 0ec86d945..027a3daa1 100644 --- a/src/graphics/Scene.cpp +++ b/src/graphics/Scene.cpp @@ -11,8 +11,9 @@ vg::IScene::~IScene() { // Empty } -void vg::IScene::initCamera() { +void vg::IScene::initCamera(f32 aspectRatio) { m_camera = std::make_unique(); + m_camera->init(aspectRatio); } void vg::IScene::unregister() { From 300bf24f9672524fb3daa9feb2e98fe3720230f2 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Mon, 4 Jul 2016 15:10:14 -0700 Subject: [PATCH 15/27] Updated some voxel stuff. Still not quite done. --- include/voxel/BlockPack.h | 14 ++++++++------ include/voxel/Chunk.h | 11 ++++++++--- include/voxel/VoxCommon.h | 12 +++++++++--- include/voxel/VoxelMeshAlg.h | 13 ++++++------- include/voxel/VoxelMesherCulled.h | 30 +++++++++++++++++++----------- src/voxel/VoxCommon.cpp | 6 ------ 6 files changed, 50 insertions(+), 36 deletions(-) diff --git a/include/voxel/BlockPack.h b/include/voxel/BlockPack.h index 23af49690..82d7a1fb8 100644 --- a/include/voxel/BlockPack.h +++ b/include/voxel/BlockPack.h @@ -24,6 +24,8 @@ #include "types.h" #endif // !VORB_USING_PCH +#include "../Events.hpp" + namespace vorb { namespace voxel { @@ -41,9 +43,9 @@ namespace voxel { /*! @brief A pack of blocks. * You should define a BLOCK type that inherits from vvox::Block. - * INDEX should be an unsigned integer type, that is larger than the maximum number of unique blocks. + * BLOCKINDEX should be an unsigned integer type, that is larger than the maximum number of unique blocks. */ - template + template class BlockPack { public: BlockPack() {} @@ -114,16 +116,16 @@ namespace voxel { const BLOCK& operator[](const BlockIdentifier& sid) const { return m_blockList[m_blockMap.at(sid)]; } - const INDEX& getBlockIndex(const BlockIdentifier& sid) const { + const BLOCKINDEX& getBlockIndex(const BlockIdentifier& sid) const { return m_blockMap.at(sid); } - const std::unordered_map& getBlockMap() const { return m_blockMap; } + const std::unordered_map& getBlockMap() const { return m_blockMap; } const std::vector& getBlockList() const { return m_blockList; } - Event onBlockAddition; ///< Signaled when a block is loaded + Event onBlockAddition; ///< Signaled when a block is loaded private: - std::unordered_map m_blockMap; ///< Blocks indices organized by identifiers + std::unordered_map m_blockMap; ///< Blocks indices organized by identifiers std::vector m_blockList; ///< BLOCK data list }; diff --git a/include/voxel/Chunk.h b/include/voxel/Chunk.h index 62db7582c..07997b53c 100644 --- a/include/voxel/Chunk.h +++ b/include/voxel/Chunk.h @@ -30,11 +30,16 @@ namespace vorb { namespace voxel { // Type for block data and tertiary data - template + template class Chunk { public: - SmartVoxelContainer blockData; - SmartVoxelContainer tertiaryData; + + const ui32 width = WIDTH; + const ui32 size = WIDTH * WIDTH * WIDTH; + + SmartVoxelContainer blockData; + SmartVoxelContainer tertiaryData; + }; diff --git a/include/voxel/VoxCommon.h b/include/voxel/VoxCommon.h index 028a20539..0c5316ba5 100644 --- a/include/voxel/VoxCommon.h +++ b/include/voxel/VoxCommon.h @@ -51,13 +51,19 @@ namespace vorb { /// Create a cardinal direction /// @param a: Axis - /// @param positive: True if facing in positive direction /// @return Cardinal direction - Cardinal toCardinal(const Axis& a, const bool& positive); + Cardinal toCardinalPositive(const Axis& a, const bool& positive) { + return (Cardinal)((ui8)a << 1) | Cardinal::POSITIVE; + } + Cardinal toCardinalNegative(const Axis& a, const bool& positive) { + return (Cardinal)((ui8)a << 1) | Cardinal::NEGATIVE; + } /// Extract axis information from cardinal direction /// @param c: Cardinal direction /// @return Axis of the cardinal direction - Axis toAxis(const Cardinal& c); + inline Axis toAxis(const Cardinal& c) { + return (Axis)((ui8)c >> 1); + } } } namespace vvox = vorb::voxel; diff --git a/include/voxel/VoxelMeshAlg.h b/include/voxel/VoxelMeshAlg.h index 8f698dcd2..8c9138b59 100644 --- a/include/voxel/VoxelMeshAlg.h +++ b/include/voxel/VoxelMeshAlg.h @@ -30,8 +30,8 @@ namespace vorb { /// Two flags specifying which faces are visible struct VoxelFaces { public: - bool block1Face : 1; ///< First block face's visibility - bool block2Face : 1; ///< Second block face's visibility + bool face1; ///< First block face's visibility + bool face2; ///< Second block face's visibility }; /// A quad created in a voxel mesh surface @@ -49,20 +49,19 @@ namespace vorb { /// @param startIndex: The index of the first vertex /// @return Array of quad vertex indices template - CALLER_DELETE T* generateQuadIndices(const ui32& quads, T startIndex = 0) { + void generateQuadIndices(OUT std::vector& inds, ui32 quads, T startIndex = 0) { size_t ic = quads * 6; - T* inds = new T[ic]; + inds.resize(ic); T vi = startIndex; for (size_t ii = 0; ii < ic;) { inds[ii++] = vi; - inds[ii++] = vi + 2; - inds[ii++] = vi + 1; inds[ii++] = vi + 1; inds[ii++] = vi + 2; + inds[ii++] = vi + 2; inds[ii++] = vi + 3; + inds[ii++] = vi; vi += 4; } - return inds; } } } diff --git a/include/voxel/VoxelMesherCulled.h b/include/voxel/VoxelMesherCulled.h index ae46eebcd..c28272f91 100644 --- a/include/voxel/VoxelMesherCulled.h +++ b/include/voxel/VoxelMesherCulled.h @@ -28,6 +28,11 @@ namespace vorb { namespace voxel { namespace meshalg { + enum class VoxelOcclusion { + DRAW_A = 0, + DRAW_B = 1, + OCCLUDE = 2 + }; /// Construct a voxel mesh, generating and culling desired face pairs /// @tparam T: Voxel data type /// @tparam API: Type of API object that handles culled meshing @@ -48,8 +53,8 @@ namespace vorb { }; ui32v3 pos; - size_t l1 = size.x; - size_t l2 = l1 * size.z; + size_t rowSize = size.x; + size_t layerSize = rowSize * size.z; for (size_t axis = 0; axis < 3; axis++) { ui32& fAxis = pos[SWEEPS[axis].x]; @@ -58,31 +63,34 @@ namespace vorb { ui32v3 sizes(size[SWEEPS[axis].x], size[SWEEPS[axis].y], size[SWEEPS[axis].z]); VoxelQuad qNeg, qPos; - qPos.direction = toCardinal(AXES[axis], true); - qNeg.direction = toCardinal(AXES[axis], false); + qPos.direction = toCardinalPositive(AXES[axis]); + qNeg.direction = toCardinalNegative(AXES[axis]); qNeg.size = qPos.size = ui32v2(1, 1); - for (fAxis = 1; fAxis < sizes.x; fAxis++) { - for (uAxis = 1; uAxis < sizes.y - 1; uAxis++) { - for (vAxis = 1; vAxis < sizes.z - 1; vAxis++) { + for (fAxis = 1; fAxis < sizes.x - 1; fAxis++) { + for (uAxis = 0; uAxis < sizes.y; uAxis++) { + for (vAxis = 0; vAxis < sizes.z; vAxis++) { fAxis--; qPos.voxelPosition = pos; - qPos.startIndex = pos.y * l2 + pos.z * l1 + pos.x; + qPos.startIndex = pos.y * layerSize + pos.z * rowSize + pos.x; const T& v1 = data[qPos.startIndex]; fAxis++; qNeg.voxelPosition = pos; - qNeg.startIndex = pos.y * l2 + pos.z * l1 + pos.x; + qNeg.startIndex = pos.y * layerSize + pos.z * rowSize + pos.x; const T& v2 = data[qNeg.startIndex]; VoxelFaces f = api->occludes(v1, v2, AXES[axis]); - if (f.block1Face && fAxis != 1) api->result(qPos); - if (f.block2Face && fAxis != sizes.x - 1) api->result(qNeg); + if (f.face1) api->result(qPos, v1); + if (f.face2) api->result(qNeg, v2); } } } + + //TODO(Ben) Outer Edge } } + } } } diff --git a/src/voxel/VoxCommon.cpp b/src/voxel/VoxCommon.cpp index 1a55358b4..6146c142b 100644 --- a/src/voxel/VoxCommon.cpp +++ b/src/voxel/VoxCommon.cpp @@ -1,9 +1,3 @@ #include "stdafx.h" #include "voxel/VoxCommon.h" -vvox::Cardinal vvox::toCardinal(const Axis& a, const bool& positive) { - return (Cardinal)(a << 1) | (positive ? Cardinal::POSITIVE : Cardinal::NEGATIVE); -} -vvox::Axis vvox::toAxis(const Cardinal& a) { - return (Axis)(a >> 1); -} \ No newline at end of file From 26c4386acbec20282cd8cac55926f9f8d9e52abd Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Mon, 4 Jul 2016 15:34:31 -0700 Subject: [PATCH 16/27] Fixed some compile errors with BOTW --- include/graphics/ShaderCommon.inl | 16 +++++++++++++--- include/voxel/VoxCommon.h | 4 ++-- src/graphics/PostProcess.cpp | 8 ++++---- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/include/graphics/ShaderCommon.inl b/include/graphics/ShaderCommon.inl index c823c512c..cc9beeeee 100644 --- a/include/graphics/ShaderCommon.inl +++ b/include/graphics/ShaderCommon.inl @@ -29,7 +29,7 @@ namespace graphics { namespace shadercommon { /// For just passing UV through the vertex shader - const cString PASSTHROUGH_VERT_SRC = R"( + const cString const PASSTHROUGH_2D_VERT_SRC = R"( // Input in vec2 vPosition; // Position in screen space @@ -41,8 +41,8 @@ void main() { gl_Position = vec4(vPosition, 0, 1); })"; - const cString TEXTURE_FRAG_SRC = R"( -// Output + const cString const TEXTURE_FRAG_SRC = R"( +// Input in vec2 fUV; uniform sampler2D unSampler; @@ -53,6 +53,16 @@ void main() { pColor = texture(unSampler, fUV); })"; + const cString const COLOR_FRAG_SRC = R"( +// Input +in vec4 fColor; + +out vec4 pColor; + +void main() { + pColor = fColor; +})"; + } } } diff --git a/include/voxel/VoxCommon.h b/include/voxel/VoxCommon.h index 0c5316ba5..744374907 100644 --- a/include/voxel/VoxCommon.h +++ b/include/voxel/VoxCommon.h @@ -52,10 +52,10 @@ namespace vorb { /// Create a cardinal direction /// @param a: Axis /// @return Cardinal direction - Cardinal toCardinalPositive(const Axis& a, const bool& positive) { + inline Cardinal toCardinalPositive(const Axis& a) { return (Cardinal)((ui8)a << 1) | Cardinal::POSITIVE; } - Cardinal toCardinalNegative(const Axis& a, const bool& positive) { + inline Cardinal toCardinalNegative(const Axis& a) { return (Cardinal)((ui8)a << 1) | Cardinal::NEGATIVE; } /// Extract axis information from cardinal direction diff --git a/src/graphics/PostProcess.cpp b/src/graphics/PostProcess.cpp index e51675487..1bc3c9cd6 100644 --- a/src/graphics/PostProcess.cpp +++ b/src/graphics/PostProcess.cpp @@ -167,9 +167,9 @@ void vg::PostProcessBloom::load() { // Load shaders // TODO(Ben): Error checking // TODO(Ben): Re-use compiled vertex shader - m_programLuma = ShaderManager::createProgram(shadercommon::PASSTHROUGH_VERT_SRC, BLOOM_LUMA_FRAG_SRC); - m_programGaussianFirst = ShaderManager::createProgram(shadercommon::PASSTHROUGH_VERT_SRC, BLOOM_GAUSS1_FRAG_SRC); - m_programGaussianSecond = ShaderManager::createProgram(shadercommon::PASSTHROUGH_VERT_SRC, BLOOM_GAUSS2_FRAG_SRC); + m_programLuma = ShaderManager::createProgram(shadercommon::PASSTHROUGH_2D_VERT_SRC, BLOOM_LUMA_FRAG_SRC); + m_programGaussianFirst = ShaderManager::createProgram(shadercommon::PASSTHROUGH_2D_VERT_SRC, BLOOM_GAUSS1_FRAG_SRC); + m_programGaussianSecond = ShaderManager::createProgram(shadercommon::PASSTHROUGH_2D_VERT_SRC, BLOOM_GAUSS2_FRAG_SRC); uploadUniforms(); } @@ -278,7 +278,7 @@ void vg::PostProcessBloom::uploadUniforms() { } void vorb::graphics::PostProcessPassthrough::load() { - m_program = ShaderManager::createProgram(shadercommon::PASSTHROUGH_VERT_SRC, shadercommon::TEXTURE_FRAG_SRC); + m_program = ShaderManager::createProgram(shadercommon::PASSTHROUGH_2D_VERT_SRC, shadercommon::TEXTURE_FRAG_SRC); m_program.use(); glUniform1i(m_program.getUniform("unSampler"), m_textureUnit); m_program.unuse(); From b1a2c081f85652eb1cea099e87acdab835e44397 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Mon, 4 Jul 2016 17:48:34 -0700 Subject: [PATCH 17/27] Allow passing in version for shadermanager. Full gbuffer. --- include/graphics/Renderer.h | 7 +++++++ include/graphics/ShaderManager.h | 6 +++++- src/graphics/PostProcess.cpp | 14 ++++---------- src/graphics/Renderer.cpp | 33 ++++++++++++++++++++++++-------- src/graphics/ShaderManager.cpp | 7 +++++-- src/ui/MainGame.cpp | 2 -- 6 files changed, 46 insertions(+), 23 deletions(-) diff --git a/include/graphics/Renderer.h b/include/graphics/Renderer.h index 4c6bea7cb..e5b7c2e6b 100644 --- a/include/graphics/Renderer.h +++ b/include/graphics/Renderer.h @@ -32,6 +32,13 @@ DECL_VUI(struct GameTime); namespace vorb { namespace graphics { + enum class GBUFFER_TEXTURE_UNITS { + COLOR = 0, + POSITION, + NORMAL, + COUNT + }; + // Forward Declare class GBuffer; class IScene; diff --git a/include/graphics/ShaderManager.h b/include/graphics/ShaderManager.h index c180dadc7..2d2f509a7 100644 --- a/include/graphics/ShaderManager.h +++ b/include/graphics/ShaderManager.h @@ -27,13 +27,13 @@ #include "../Events.hpp" #include "../VorbPreDecl.inl" #include "../io/Path.h" +#include "GLProgram.h" DECL_VIO(class IOManager) namespace vorb { namespace graphics { - class GLProgram; typedef std::map GLProgramMap; /// Static class that handles caching, creation, and destruction of GLPrograms @@ -44,11 +44,13 @@ namespace vorb { /// Does not register to global cache. /// @param vertSrc: Source code for vertex shader /// @param fragSrc: Source code for fragment shader + /// @param version: Optional shader version /// @param vertIOM: Optional IOManager for vert #include lookups /// @param fragIOM: Optional IOManager for frag #include lookups /// @param defines: #defines for the program /// @return the created program. static GLProgram createProgram(const cString vertSrc, const cString fragSrc, + ShaderLanguageVersion version = DEFAULT_SHADING_LANGUAGE_VERSION, vio::IOManager* vertIOM = nullptr, vio::IOManager* fragIOM = nullptr, cString defines = nullptr); @@ -56,10 +58,12 @@ namespace vorb { /// Does not register to global cache. /// @param vertPath: Path to vertex shader /// @param fragPath: Path to fragment shader + /// @param version: Optional shader version /// @param iom: Optional IOManager for loading /// @param defines: #defines for the program /// @return the created program. static GLProgram createProgramFromFile(const vio::Path& vertPath, const vio::Path& fragPath, + ShaderLanguageVersion version = DEFAULT_SHADING_LANGUAGE_VERSION, vio::IOManager* iom = nullptr, cString defines = nullptr); /// Disposes and deallocates all globally cached programs and clears the cache diff --git a/src/graphics/PostProcess.cpp b/src/graphics/PostProcess.cpp index 1bc3c9cd6..80b946af9 100644 --- a/src/graphics/PostProcess.cpp +++ b/src/graphics/PostProcess.cpp @@ -22,7 +22,8 @@ void vg::IPostProcess::unregister() { #define BLOOM_TEXTURE_SLOT_COLOR 0 // texture slot to bind color texture which luma info will be extracted #define BLOOM_TEXTURE_SLOT_LUMA 0 // texture slot to bind luma texture -#define BLOOM_TEXTURE_SLOT_BLUR 1 // texture slot to bind blur texture +#define BLOOM_TEXTURE_SLOT_BLUR 3 // texture slot to bind blur texture +static_assert(BLOOM_TEXTURE_SLOT_BLUR >= (ui32)vg::GBUFFER_TEXTURE_UNITS::COUNT, "Blur will collide with GBuffer"); #pragma region BloomShaderCode @@ -179,17 +180,10 @@ void vg::PostProcessBloom::render() { // TODO(Ben): Don't fuck with existing state. Need state manager. glDisable(GL_DEPTH_TEST); - // Get initial bound FBO and bound color texture to use it on final pass - int initial_texture; - // TODO(Ben): Bad performance - glGetIntegerv(GL_TEXTURE_BINDING_2D, &initial_texture); - - // color texture should be bound on GL_TEXTURE0 slot - // luma pass rendering on temporary FBO 1 m_fbos[0].use(); glActiveTexture(GL_TEXTURE0 + BLOOM_TEXTURE_SLOT_COLOR); - glBindTexture(GL_TEXTURE_2D, initial_texture); + glBindTexture(GL_TEXTURE_2D, (ui32)GBUFFER_TEXTURE_UNITS::COLOR); renderStage(m_programLuma); // first gaussian blur pass rendering on temporary FBO 2 @@ -201,7 +195,7 @@ void vg::PostProcessBloom::render() { // second gaussian blur pass rendering. Sum initial color with blur color. glActiveTexture(GL_TEXTURE0 + BLOOM_TEXTURE_SLOT_COLOR); - glBindTexture(GL_TEXTURE_2D, initial_texture); + glBindTexture(GL_TEXTURE_2D, (ui32)GBUFFER_TEXTURE_UNITS::COLOR); glActiveTexture(GL_TEXTURE0 + BLOOM_TEXTURE_SLOT_BLUR); m_fbos[1].bindTexture(); diff --git a/src/graphics/Renderer.cpp b/src/graphics/Renderer.cpp index fafc4e318..583934671 100644 --- a/src/graphics/Renderer.cpp +++ b/src/graphics/Renderer.cpp @@ -24,15 +24,30 @@ bool vg::Renderer::init(vui::GameWindow* window) { // Set up GBuffer for deferred rendering. m_gBuffer = std::make_unique(m_window->getWidth(), m_window->getHeight()); - // TODO(Ben): More than color. std::vector attachments; { // Color attachments.emplace_back(); - GBufferAttachment& color = attachments.back(); - color.format = GBUFFER_INTERNAL_FORMAT_COLOR; - color.pixelFormat = vg::TextureFormat::RGBA; - color.pixelType = vg::TexturePixelType::FLOAT; - color.number = 0; + GBufferAttachment& a = attachments.back(); + a.format = GBUFFER_INTERNAL_FORMAT_COLOR; + a.pixelFormat = vg::TextureFormat::RGBA; + a.pixelType = vg::TexturePixelType::FLOAT; + a.number = (ui32)GBUFFER_TEXTURE_UNITS::COLOR; + } + { // Position + attachments.emplace_back(); + GBufferAttachment& a = attachments.back(); + a.format = vg::TextureInternalFormat::RGB16F; + a.pixelFormat = vg::TextureFormat::RGB; + a.pixelType = vg::TexturePixelType::FLOAT; + a.number = (ui32)GBUFFER_TEXTURE_UNITS::POSITION; + } + { // Normal + attachments.emplace_back(); + GBufferAttachment& a = attachments.back(); + a.format = vg::TextureInternalFormat::RGB16F; + a.pixelFormat = vg::TextureFormat::RGB; + a.pixelType = vg::TexturePixelType::FLOAT; + a.number = (ui32)GBUFFER_TEXTURE_UNITS::NORMAL; } m_gBuffer->init(attachments, GBUFFER_INTERNAL_FORMAT_LIGHT); m_gBuffer->initDepth(); @@ -172,8 +187,10 @@ void vg::Renderer::renderPostProcesses() { glBindFramebuffer(GL_FRAMEBUFFER, 0); glViewport(0, 0, m_window->getWidth(), m_window->getHeight()); - // Bind color - m_gBuffer->bindGeometryTexture(0, 0); + // Bind gbuffer textures + m_gBuffer->bindGeometryTexture((ui32)GBUFFER_TEXTURE_UNITS::COLOR, (ui32)GBUFFER_TEXTURE_UNITS::COLOR); + m_gBuffer->bindGeometryTexture((ui32)GBUFFER_TEXTURE_UNITS::POSITION, (ui32)GBUFFER_TEXTURE_UNITS::POSITION); + m_gBuffer->bindGeometryTexture((ui32)GBUFFER_TEXTURE_UNITS::NORMAL, (ui32)GBUFFER_TEXTURE_UNITS::NORMAL); // Do all post processing for (IPostProcess* p : m_postProcesses) { diff --git a/src/graphics/ShaderManager.cpp b/src/graphics/ShaderManager.cpp index e38cdb071..25eee63bb 100644 --- a/src/graphics/ShaderManager.cpp +++ b/src/graphics/ShaderManager.cpp @@ -1,6 +1,5 @@ #include "stdafx.h" #include "graphics/ShaderManager.h" -#include "graphics/GLProgram.h" #include "graphics/ShaderParser.h" #include "io/IOManager.h" @@ -13,6 +12,7 @@ vg::GLProgramMap vg::ShaderManager::m_programMap; vg::GLProgram vg::ShaderManager::m_nilProgram; vg::GLProgram vg::ShaderManager::createProgram(const cString vertSrc, const cString fragSrc, + ShaderLanguageVersion version /*= DEFAULT_SHADING_LANGUAGE_VERSION*/, vio::IOManager* vertIOM /*= nullptr*/, vio::IOManager* fragIOM /*= nullptr*/, cString defines /*= nullptr*/) { @@ -38,6 +38,7 @@ vg::GLProgram vg::ShaderManager::createProgram(const cString vertSrc, const cStr // Create vertex shader ShaderSource srcVert; srcVert.stage = vg::ShaderType::VERTEX_SHADER; + srcVert.version = version; if (defines) srcVert.sources.push_back(defines); srcVert.sources.push_back(parsedVertSrc.c_str()); if (!program.addShader(srcVert)) { @@ -51,6 +52,7 @@ vg::GLProgram vg::ShaderManager::createProgram(const cString vertSrc, const cStr // Create the fragment shader ShaderSource srcFrag; srcFrag.stage = vg::ShaderType::FRAGMENT_SHADER; + srcVert.version = version; if (defines) srcFrag.sources.push_back(defines); srcFrag.sources.push_back(parsedFragSrc.c_str()); if (!program.addShader(srcFrag)) { @@ -74,6 +76,7 @@ vg::GLProgram vg::ShaderManager::createProgram(const cString vertSrc, const cStr } vg::GLProgram vg::ShaderManager::createProgramFromFile(const vio::Path& vertPath, const vio::Path& fragPath, + ShaderLanguageVersion version /*= DEFAULT_SHADING_LANGUAGE_VERSION*/, vio::IOManager* iom /*= nullptr*/, cString defines /*= nullptr*/) { vio::IOManager ioManager; vio::Path vertSearchDir; @@ -110,7 +113,7 @@ vg::GLProgram vg::ShaderManager::createProgramFromFile(const vio::Path& vertPath return m_nilProgram; } - return createProgram(vertSrc.c_str(), fragSrc.c_str(), &vertIOM, &fragIOM, defines); + return createProgram(vertSrc.c_str(), fragSrc.c_str(), version, &vertIOM, &fragIOM, defines); } void vg::ShaderManager::disposeAllPrograms() { diff --git a/src/ui/MainGame.cpp b/src/ui/MainGame.cpp index edbf32866..9d4b4dd3c 100644 --- a/src/ui/MainGame.cpp +++ b/src/ui/MainGame.cpp @@ -223,8 +223,6 @@ void vui::MainGame::onRenderFrame() { m_renderer.beginRenderFrame(); - // TODO(Ben): Deferred - m_screen->onRenderFrame(m_curTime); m_renderer.renderScenes(m_curTime); m_renderer.renderPostProcesses(); From c82de5491a5eb965f8c0882a44c6d369d735d9e2 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Mon, 4 Jul 2016 18:12:31 -0700 Subject: [PATCH 18/27] Fixed version bug --- src/graphics/ShaderManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/graphics/ShaderManager.cpp b/src/graphics/ShaderManager.cpp index 25eee63bb..4ce3fd4f6 100644 --- a/src/graphics/ShaderManager.cpp +++ b/src/graphics/ShaderManager.cpp @@ -52,7 +52,7 @@ vg::GLProgram vg::ShaderManager::createProgram(const cString vertSrc, const cStr // Create the fragment shader ShaderSource srcFrag; srcFrag.stage = vg::ShaderType::FRAGMENT_SHADER; - srcVert.version = version; + srcFrag.version = version; if (defines) srcFrag.sources.push_back(defines); srcFrag.sources.push_back(parsedFragSrc.c_str()); if (!program.addShader(srcFrag)) { From 54c6aaf9d9c116b8c38b52f64867894e68bdaca9 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Mon, 4 Jul 2016 18:26:12 -0700 Subject: [PATCH 19/27] Fixed bloom to work with gbuffer properly --- src/graphics/PostProcess.cpp | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/src/graphics/PostProcess.cpp b/src/graphics/PostProcess.cpp index 80b946af9..30f37ed46 100644 --- a/src/graphics/PostProcess.cpp +++ b/src/graphics/PostProcess.cpp @@ -20,10 +20,9 @@ void vg::IPostProcess::unregister() { /* Bloom */ /************************************************************************/ -#define BLOOM_TEXTURE_SLOT_COLOR 0 // texture slot to bind color texture which luma info will be extracted -#define BLOOM_TEXTURE_SLOT_LUMA 0 // texture slot to bind luma texture -#define BLOOM_TEXTURE_SLOT_BLUR 3 // texture slot to bind blur texture -static_assert(BLOOM_TEXTURE_SLOT_BLUR >= (ui32)vg::GBUFFER_TEXTURE_UNITS::COUNT, "Blur will collide with GBuffer"); +#define BLOOM_TEXTURE_SLOT_LUMA 4 // texture slot to bind luma texture +#define BLOOM_TEXTURE_SLOT_BLUR 5 // texture slot to bind blur texture +static_assert(BLOOM_TEXTURE_SLOT_LUMA >= (ui32)vg::GBUFFER_TEXTURE_UNITS::COUNT, "Bloom will collide with GBuffer"); #pragma region BloomShaderCode @@ -180,10 +179,8 @@ void vg::PostProcessBloom::render() { // TODO(Ben): Don't fuck with existing state. Need state manager. glDisable(GL_DEPTH_TEST); - // luma pass rendering on temporary FBO 1 + // luma pass rendering on temporary FBO 1 using the color gbuffer m_fbos[0].use(); - glActiveTexture(GL_TEXTURE0 + BLOOM_TEXTURE_SLOT_COLOR); - glBindTexture(GL_TEXTURE_2D, (ui32)GBUFFER_TEXTURE_UNITS::COLOR); renderStage(m_programLuma); // first gaussian blur pass rendering on temporary FBO 2 @@ -194,9 +191,6 @@ void vg::PostProcessBloom::render() { m_fbos[1].unuse(m_windowWidth, m_windowHeight); // second gaussian blur pass rendering. Sum initial color with blur color. - glActiveTexture(GL_TEXTURE0 + BLOOM_TEXTURE_SLOT_COLOR); - glBindTexture(GL_TEXTURE_2D, (ui32)GBUFFER_TEXTURE_UNITS::COLOR); - glActiveTexture(GL_TEXTURE0 + BLOOM_TEXTURE_SLOT_BLUR); m_fbos[1].bindTexture(); @@ -235,7 +229,7 @@ void vg::PostProcessBloom::renderStage(vg::GLProgram& program) { void vg::PostProcessBloom::uploadUniforms() { m_programLuma.use(); - glUniform1i(m_programLuma.getUniform("unTexColor"), BLOOM_TEXTURE_SLOT_COLOR); + glUniform1i(m_programLuma.getUniform("unTexColor"), (ui32)vg::GBUFFER_TEXTURE_UNITS::COLOR); glUniform1f(m_programLuma.getUniform("unLumaThresh"), m_lumaThreshold); m_programLuma.unuse(); @@ -246,7 +240,7 @@ void vg::PostProcessBloom::uploadUniforms() { m_programGaussianFirst.unuse(); m_programGaussianSecond.use(); - glUniform1i(m_programGaussianSecond.getUniform("unTexColor"), BLOOM_TEXTURE_SLOT_COLOR); + glUniform1i(m_programGaussianSecond.getUniform("unTexColor"), (ui32)vg::GBUFFER_TEXTURE_UNITS::COLOR); glUniform1i(m_programGaussianSecond.getUniform("unTexBlur"), BLOOM_TEXTURE_SLOT_BLUR); glUniform1f(m_programGaussianSecond.getUniform("unInvWidth"), 1.0f / (f32)m_windowWidth); glUniform1i(m_programGaussianSecond.getUniform("unGaussianN"), m_gaussianN); From ced8e13c6c5c721c088c13a4509db34661dd9eb2 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Mon, 4 Jul 2016 18:31:00 -0700 Subject: [PATCH 20/27] Allow runtime passthrough texture change --- include/graphics/PostProcess.h | 1 + src/graphics/PostProcess.cpp | 16 +++++++++++++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/include/graphics/PostProcess.h b/include/graphics/PostProcess.h index 5148cc999..7cca016a1 100644 --- a/include/graphics/PostProcess.h +++ b/include/graphics/PostProcess.h @@ -108,6 +108,7 @@ namespace graphics { public: void init(ui32 textureUnit) { m_textureUnit = textureUnit; } + void setTextureUnit(ui32 textureUnit); virtual void load() override; virtual void render() override; virtual void dispose() override; diff --git a/src/graphics/PostProcess.cpp b/src/graphics/PostProcess.cpp index 30f37ed46..660ec479a 100644 --- a/src/graphics/PostProcess.cpp +++ b/src/graphics/PostProcess.cpp @@ -265,7 +265,17 @@ void vg::PostProcessBloom::uploadUniforms() { m_programGaussianSecond.unuse(); } -void vorb::graphics::PostProcessPassthrough::load() { +void vg::PostProcessPassthrough::setTextureUnit(ui32 textureUnit) { + m_textureUnit = textureUnit; + // Update if needed + if (m_program.isLinked()) { + m_program.use(); + glUniform1i(m_program.getUniform("unSampler"), m_textureUnit); + m_program.unuse(); + } +} + +void vg::PostProcessPassthrough::load() { m_program = ShaderManager::createProgram(shadercommon::PASSTHROUGH_2D_VERT_SRC, shadercommon::TEXTURE_FRAG_SRC); m_program.use(); glUniform1i(m_program.getUniform("unSampler"), m_textureUnit); @@ -274,13 +284,13 @@ void vorb::graphics::PostProcessPassthrough::load() { m_quad.init(); } -void vorb::graphics::PostProcessPassthrough::render() { +void vg::PostProcessPassthrough::render() { m_program.use(); m_quad.draw(); m_program.unuse(); } -void vorb::graphics::PostProcessPassthrough::dispose() { +void vg::PostProcessPassthrough::dispose() { m_program.dispose(); m_quad.dispose(); } From caa245963767af1a46fde3b1de1fa90c76021f2e Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Thu, 7 Jul 2016 03:03:56 -0700 Subject: [PATCH 21/27] Better postprocessing API and various fixes. --- include/graphics/FullQuadVBO.h | 23 +++-- include/graphics/PostProcess.h | 67 ++++++++++---- include/graphics/Renderer.h | 1 + include/graphics/ShaderParser.h | 1 + src/graphics/FullQuadVBO.cpp | 6 +- src/graphics/PostProcess.cpp | 155 ++++++++++++++++++-------------- src/graphics/Renderer.cpp | 7 +- 7 files changed, 163 insertions(+), 97 deletions(-) diff --git a/include/graphics/FullQuadVBO.h b/include/graphics/FullQuadVBO.h index ba56995be..c2bbc0d87 100644 --- a/include/graphics/FullQuadVBO.h +++ b/include/graphics/FullQuadVBO.h @@ -22,28 +22,35 @@ #include "../types.h" #endif // !VORB_USING_PCH +#include "gtypes.h" + namespace vorb { namespace graphics { /// Wrapper over common functionality to draw a quad across the entire screen class FullQuadVBO { public: - /// Initialize all OpenGL resources - /// @param attrLocation: Position attribute location for VAO + /*! @brief Initialize all graphics resources. + * @param attrLocation: Position attribute location for VAO + */ void init(i32 attrLocation = 0); - /// Dispose all OpenGL resources + /*! @brief Dispose all graphics resources + */ void dispose(); - /// Binds vertex array, index buffer, and issues a draw command + /*! @brief Binds vertex array, index buffer, and issues a draw command. + */ void draw(); + + bool isInitialized() const { return m_vao != 0; } private: union { struct { - ui32 m_vb; ///< Vertex buffer ID - ui32 m_ib; ///< Index buffer ID + VGVertexBuffer m_vb; + VGIndexBuffer m_ib; }; - ui32 m_buffers[2]; ///< Storage for both buffers used by this mesh + VGVertexBuffer m_buffers[2]; }; - ui32 m_vao; ///< VAO with vertex attribute pointing to 0 + VGVertexArray m_vao = 0; }; } } diff --git a/include/graphics/PostProcess.h b/include/graphics/PostProcess.h index 7cca016a1..5abba6bde 100644 --- a/include/graphics/PostProcess.h +++ b/include/graphics/PostProcess.h @@ -35,6 +35,9 @@ namespace graphics { class GLRenderTarget; class Renderer; + // TODO(Ben): Visual shader programming tool with live recompiling. + // TODO(Ben): FBO Cacheing. + /************************************************************************/ /* Base */ /************************************************************************/ @@ -57,43 +60,72 @@ namespace graphics { virtual void dispose() = 0; /*! @brief Unregisters from the SceneRenderer - */ + */ virtual void unregister(); + /*! @brief Gets and sets the input textures. + * Derived classes must allocate input texture space in m_inputTextures or setInputTexture will crash. + * setInputTextures should be preferred with multiple textures, as setInputTexture will redundantly upload uniforms. + */ + virtual void setInputTexture (ui32 index, VGTexture inputTexture) { m_inputTextures.at(index) = inputTexture; } + virtual void setInputTextures(std::vector inputTextures) { m_inputTextures = inputTextures; } + virtual const std::vector& getInputTextures() const { return m_inputTextures; } + + /*! @brief Gets and sets the input textures. + * It is assumed that the outputFBO is a format that works for this PostProcess. + */ + virtual void setOutputFBO(VGFramebuffer outputFBO) { m_outputFBO = outputFBO; } + virtual VGFramebuffer getOutputFBO() const { return m_outputFBO; } + + /*! @brief Checks if quad is created, and creates it if not. + * This must be called to initialize the shared quad. Recommend calling in + * any shader that needs the quad. + * @return False if the quad is already initialized. + */ + static bool tryInitQuad(); + + static vg::FullQuadVBO quad; ///< Optional shared VBO. Call dispose manually. + protected: + Renderer* m_renderer = nullptr; ///< Renderer that owns this. + + std::vector m_inputTextures; ///< Optional input textures, some PostProcesses may not use. + + VGFramebuffer m_outputFBO = 0; ///< FBO that we will write to. }; /************************************************************************/ /* Bloom */ /************************************************************************/ - /// Renders the bloom post process. + /*! @brief Renders the bloom post process: A gaussian blur of bright objects. + * + * @input Texture 0 as color. + * @output[0] Color. + */ class PostProcessBloom : public IPostProcess { public: - void init (ui32 windowWidth, ui32 windowHeight, ui32 renderFBO = 0); + void init (ui32 windowWidth, ui32 windowHeight, VGTexture inputColorTexture); void setParams(ui32 gaussianN = 20, f32 gaussianVariance = 36.0f, f32 lumaThreshold = 0.75f); void load () override; virtual void render () override; virtual void dispose () override; private: - void renderStage (vg::GLProgram& program); - void uploadUniforms(); + virtual void uploadShaderUniforms(); + + void renderStage (vg::GLProgram& program); /// Shaders vg::GLProgram m_programLuma; vg::GLProgram m_programGaussianFirst; vg::GLProgram m_programGaussianSecond; - vg::FullQuadVBO m_quad; - vg::GLRenderTarget m_fbos[2]; ui32 m_windowWidth = 0; ui32 m_windowHeight = 0; - ui32 m_renderFBO = 0; ///< FBO to render the final image to - /// Parameters ui32 m_gaussianN = 20; ///< Threshold for filtering image luma for bloom bluring f32 m_gaussianVariance = 36.0f; ///< Radius number for gaussian blur. Must be less than 50. @@ -103,23 +135,24 @@ namespace graphics { /************************************************************************/ /* Passthrough */ /************************************************************************/ - /// Simply renders a texture from one FBO to another. + /*! @brief Simply renders a 2D texture from one FBO to another. + * + * @input Texture. + * @output[0] Texture. + */ class PostProcessPassthrough : public IPostProcess { public: - - void init(ui32 textureUnit) { m_textureUnit = textureUnit; } - void setTextureUnit(ui32 textureUnit); + void init(VGTexture inputTexture); virtual void load() override; virtual void render() override; virtual void dispose() override; + virtual void uploadShaderUniforms(); private: - ui32 m_textureUnit = 0; vg::GLProgram m_program; - - // TODO(Ben): Quad sharing - vg::FullQuadVBO m_quad; }; + + // TODO(Ben): Gaussian blur. } } namespace vg = vorb::graphics; diff --git a/include/graphics/Renderer.h b/include/graphics/Renderer.h index e5b7c2e6b..bfea03e00 100644 --- a/include/graphics/Renderer.h +++ b/include/graphics/Renderer.h @@ -115,6 +115,7 @@ namespace vorb { const std::vector& getScenes() const { return m_scenes; } const std::vector& getPostProcesses() const { return m_postProcesses; } bool isInitialized() const { return m_isInitialized; } + const vg::GBuffer* getGBuffer() const { return m_gBuffer.get(); } protected: bool m_isInitialized = false; diff --git a/include/graphics/ShaderParser.h b/include/graphics/ShaderParser.h index 94fd699ee..e00581345 100644 --- a/include/graphics/ShaderParser.h +++ b/include/graphics/ShaderParser.h @@ -37,6 +37,7 @@ DECL_VIO(class IOManager); namespace vorb { namespace graphics { + // TODO(Ben): Detect output location based on chronological order. class ShaderParser { public: /// Parses includes and semantics for a vertex shader diff --git a/src/graphics/FullQuadVBO.cpp b/src/graphics/FullQuadVBO.cpp index 926ee296c..99a47a7a8 100644 --- a/src/graphics/FullQuadVBO.cpp +++ b/src/graphics/FullQuadVBO.cpp @@ -25,13 +25,13 @@ void vg::FullQuadVBO::init(i32 attrLocation /*= 0*/) { } void vg::FullQuadVBO::dispose() { - if (m_buffers[0]) { + if (m_vao) { glDeleteBuffers(2, m_buffers); m_buffers[0] = 0; m_buffers[1] = 0; - } - if (m_vao) { + glDeleteVertexArrays(1, &m_vao); + m_vao = 0; ///< So we know we aren't initialized. } } diff --git a/src/graphics/PostProcess.cpp b/src/graphics/PostProcess.cpp index 660ec479a..8ed61de93 100644 --- a/src/graphics/PostProcess.cpp +++ b/src/graphics/PostProcess.cpp @@ -6,6 +6,8 @@ #include "graphics/ShaderManager.h" #include "graphics/Renderer.h" +vg::FullQuadVBO vg::IPostProcess::quad; + /************************************************************************/ /* IPostProcess */ /************************************************************************/ @@ -16,13 +18,21 @@ void vg::IPostProcess::unregister() { m_renderer->unregisterPostProcess(this); } +bool vg::IPostProcess::tryInitQuad() { + if (quad.isInitialized()) return false; + quad.init(); + return true; +} + +#define TEXTURE_SLOT_INPUT 4 +static_assert(TEXTURE_SLOT_INPUT >= (ui32)vg::GBUFFER_TEXTURE_UNITS::COUNT, "PostProcess input will collide with GBuffer"); + /************************************************************************/ /* Bloom */ /************************************************************************/ -#define BLOOM_TEXTURE_SLOT_LUMA 4 // texture slot to bind luma texture -#define BLOOM_TEXTURE_SLOT_BLUR 5 // texture slot to bind blur texture -static_assert(BLOOM_TEXTURE_SLOT_LUMA >= (ui32)vg::GBUFFER_TEXTURE_UNITS::COUNT, "Bloom will collide with GBuffer"); +#define BLOOM_TEXTURE_SLOT_LUMA 5 // texture slot to bind luma texture +#define BLOOM_TEXTURE_SLOT_BLUR 6 // texture slot to bind blur texture= #pragma region BloomShaderCode @@ -133,10 +143,10 @@ inline f32 gauss(int i, f32 sigma2) { return 1.0 / vmath::sqrt(2 * 3.14159265 * sigma2) * vmath::exp(-(i*i) / (2 * sigma2)); } -void vg::PostProcessBloom::init(ui32 windowWidth, ui32 windowHeight, ui32 renderFBO /*= 0*/) { +void vg::PostProcessBloom::init(ui32 windowWidth, ui32 windowHeight, VGTexture inputColorTexture) { m_windowWidth = windowWidth; m_windowHeight = windowHeight; - m_renderFBO = renderFBO; + m_inputTextures.resize(1, inputColorTexture); } void vg::PostProcessBloom::setParams(ui32 gaussianN /* = 20*/, float gaussianVariance /*= 36.0f*/, float lumaThreshold /*= 0.75f*/) { @@ -147,17 +157,14 @@ void vg::PostProcessBloom::setParams(ui32 gaussianN /* = 20*/, float gaussianVar m_gaussianVariance = gaussianVariance; m_lumaThreshold = lumaThreshold; - // If our shaders are already made, send uniforms again. - if (m_programLuma.isLinked()) { - uploadUniforms(); - } + uploadShaderUniforms(); } void vg::PostProcessBloom::load() { vorb_assert(m_windowWidth != 0 && m_windowHeight != 0, "PostProcessBloom was not initialized."); - m_quad.init(); + tryInitQuad(); // initialize FBOs m_fbos[0].setSize(m_windowWidth, m_windowHeight); m_fbos[1].setSize(m_windowWidth, m_windowHeight); @@ -171,7 +178,7 @@ void vg::PostProcessBloom::load() { m_programGaussianFirst = ShaderManager::createProgram(shadercommon::PASSTHROUGH_2D_VERT_SRC, BLOOM_GAUSS1_FRAG_SRC); m_programGaussianSecond = ShaderManager::createProgram(shadercommon::PASSTHROUGH_2D_VERT_SRC, BLOOM_GAUSS2_FRAG_SRC); - uploadUniforms(); + uploadShaderUniforms(); } void vg::PostProcessBloom::render() { @@ -181,6 +188,8 @@ void vg::PostProcessBloom::render() { // luma pass rendering on temporary FBO 1 using the color gbuffer m_fbos[0].use(); + glActiveTexture(GL_TEXTURE0 + TEXTURE_SLOT_INPUT); + glBindTexture(GL_TEXTURE_2D, m_inputTextures.at(0)); renderStage(m_programLuma); // first gaussian blur pass rendering on temporary FBO 2 @@ -195,7 +204,7 @@ void vg::PostProcessBloom::render() { m_fbos[1].bindTexture(); // Bind output FBO - glBindFramebuffer(GL_FRAMEBUFFER, m_renderFBO); + glBindFramebuffer(GL_FRAMEBUFFER, m_outputFBO); renderStage(m_programGaussianSecond); // TODO(Ben): Need state manager @@ -206,7 +215,6 @@ void vg::PostProcessBloom::dispose() { m_programLuma.dispose(); m_programGaussianFirst.dispose(); m_programGaussianSecond.dispose(); - m_quad.dispose(); m_fbos[0].dispose(); m_fbos[1].dispose(); @@ -216,81 +224,94 @@ void vg::PostProcessBloom::dispose() { } } +void vg::PostProcessBloom::uploadShaderUniforms() { + if (m_programGaussianSecond.isLinked()) { + m_programLuma.use(); + glUniform1i(m_programLuma.getUniform("unTexColor"), TEXTURE_SLOT_INPUT); + glUniform1f(m_programLuma.getUniform("unLumaThresh"), m_lumaThreshold); + m_programLuma.unuse(); + + m_programGaussianFirst.use(); + glUniform1i(m_programGaussianFirst.getUniform("unTexLuma"), BLOOM_TEXTURE_SLOT_LUMA); + glUniform1f(m_programGaussianFirst.getUniform("unInvHeight"), 1.0f / (f32)m_windowHeight); + glUniform1i(m_programGaussianFirst.getUniform("unGaussianN"), m_gaussianN); + m_programGaussianFirst.unuse(); + + m_programGaussianSecond.use(); + glUniform1i(m_programGaussianSecond.getUniform("unTexColor"), TEXTURE_SLOT_INPUT); + glUniform1i(m_programGaussianSecond.getUniform("unTexBlur"), BLOOM_TEXTURE_SLOT_BLUR); + glUniform1f(m_programGaussianSecond.getUniform("unInvWidth"), 1.0f / (f32)m_windowWidth); + glUniform1i(m_programGaussianSecond.getUniform("unGaussianN"), m_gaussianN); + m_programGaussianSecond.unuse(); + + // Calculate gaussian weights + f32 weights[50], sum; + weights[0] = gauss(0, m_gaussianVariance); + sum = weights[0]; + for (ui32 i = 1; i < m_gaussianN; i++) { + weights[i] = gauss(i, m_gaussianVariance); + sum += 2 * weights[i]; + } + for (ui32 i = 0; i < m_gaussianN; i++) { + weights[i] = weights[i] / sum; + } + m_programGaussianFirst.use(); + glUniform1fv(m_programGaussianFirst.getUniform("unWeight[0]"), m_gaussianN, weights); + m_programGaussianFirst.unuse(); + m_programGaussianSecond.use(); + glUniform1fv(m_programGaussianSecond.getUniform("unWeight[0]"), m_gaussianN, weights); + m_programGaussianSecond.unuse(); + } +} + void vg::PostProcessBloom::renderStage(vg::GLProgram& program) { program.use(); program.enableVertexAttribArrays(); - m_quad.draw(); + quad.draw(); program.disableVertexAttribArrays(); program.unuse(); } -void vg::PostProcessBloom::uploadUniforms() { - m_programLuma.use(); - glUniform1i(m_programLuma.getUniform("unTexColor"), (ui32)vg::GBUFFER_TEXTURE_UNITS::COLOR); - glUniform1f(m_programLuma.getUniform("unLumaThresh"), m_lumaThreshold); - m_programLuma.unuse(); - - m_programGaussianFirst.use(); - glUniform1i(m_programGaussianFirst.getUniform("unTexLuma"), BLOOM_TEXTURE_SLOT_LUMA); - glUniform1f(m_programGaussianFirst.getUniform("unInvHeight"), 1.0f / (f32)m_windowHeight); - glUniform1i(m_programGaussianFirst.getUniform("unGaussianN"), m_gaussianN); - m_programGaussianFirst.unuse(); - - m_programGaussianSecond.use(); - glUniform1i(m_programGaussianSecond.getUniform("unTexColor"), (ui32)vg::GBUFFER_TEXTURE_UNITS::COLOR); - glUniform1i(m_programGaussianSecond.getUniform("unTexBlur"), BLOOM_TEXTURE_SLOT_BLUR); - glUniform1f(m_programGaussianSecond.getUniform("unInvWidth"), 1.0f / (f32)m_windowWidth); - glUniform1i(m_programGaussianSecond.getUniform("unGaussianN"), m_gaussianN); - m_programGaussianSecond.unuse(); - - // Calculate gaussian weights - f32 weights[50], sum; - weights[0] = gauss(0, m_gaussianVariance); - sum = weights[0]; - for (ui32 i = 1; i < m_gaussianN; i++) { - weights[i] = gauss(i, m_gaussianVariance); - sum += 2 * weights[i]; - } - for (ui32 i = 0; i < m_gaussianN; i++) { - weights[i] = weights[i] / sum; - } - m_programGaussianFirst.use(); - glUniform1fv(m_programGaussianFirst.getUniform("unWeight[0]"), m_gaussianN, weights); - m_programGaussianFirst.unuse(); - m_programGaussianSecond.use(); - glUniform1fv(m_programGaussianSecond.getUniform("unWeight[0]"), m_gaussianN, weights); - m_programGaussianSecond.unuse(); -} - -void vg::PostProcessPassthrough::setTextureUnit(ui32 textureUnit) { - m_textureUnit = textureUnit; - // Update if needed - if (m_program.isLinked()) { - m_program.use(); - glUniform1i(m_program.getUniform("unSampler"), m_textureUnit); - m_program.unuse(); - } +void vg::PostProcessPassthrough::init(VGTexture inputTexture) { + m_inputTextures.resize(1, inputTexture); } void vg::PostProcessPassthrough::load() { m_program = ShaderManager::createProgram(shadercommon::PASSTHROUGH_2D_VERT_SRC, shadercommon::TEXTURE_FRAG_SRC); - m_program.use(); - glUniform1i(m_program.getUniform("unSampler"), m_textureUnit); - m_program.unuse(); - - m_quad.init(); + + uploadShaderUniforms(); + tryInitQuad(); } void vg::PostProcessPassthrough::render() { + + glDisable(GL_DEPTH_TEST); + + glBindFramebuffer(GL_FRAMEBUFFER, m_outputFBO); + + glActiveTexture(GL_TEXTURE0 + TEXTURE_SLOT_INPUT); + glBindTexture(GL_TEXTURE_2D, m_inputTextures.at(0)); + m_program.use(); - m_quad.draw(); + quad.draw(); m_program.unuse(); + + + glEnable(GL_DEPTH_TEST); } void vg::PostProcessPassthrough::dispose() { m_program.dispose(); - m_quad.dispose(); } + +void vg::PostProcessPassthrough::uploadShaderUniforms() { + if (m_program.isLinked()) { + // Can interrupt outer state + m_program.use(); + glUniform1i(m_program.getUniform("unSampler"), TEXTURE_SLOT_INPUT); + m_program.unuse(); + } +} \ No newline at end of file diff --git a/src/graphics/Renderer.cpp b/src/graphics/Renderer.cpp index 583934671..0178ec1fd 100644 --- a/src/graphics/Renderer.cpp +++ b/src/graphics/Renderer.cpp @@ -197,12 +197,12 @@ void vg::Renderer::renderPostProcesses() { p->render(); } - // If we have no post processes, we have to render the g buffer to the screen manually. + // If we have no post processes, we have to render the color to the screen manually. if (m_postProcesses.empty()) { // Lazy load if (!m_postProcessPassthrough) { m_postProcessPassthrough = std::make_unique(); - m_postProcessPassthrough->init(0); + m_postProcessPassthrough->init((ui32)GBUFFER_TEXTURE_UNITS::COLOR); m_postProcessPassthrough->load(); } // Bind color @@ -211,6 +211,9 @@ void vg::Renderer::renderPostProcesses() { m_postProcessPassthrough->render(); } + // Unset FBO + glBindFramebuffer(GL_FRAMEBUFFER, 0); + glViewport(0, 0, m_window->getWidth(), m_window->getHeight()); } void vg::Renderer::dispose() { From 1006cd49c83789907662adbac16025a7ffa5da2f Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Sat, 16 Jul 2016 12:02:44 -0700 Subject: [PATCH 22/27] Fixed innacurate comment --- include/graphics/PostProcess.h | 1 - 1 file changed, 1 deletion(-) diff --git a/include/graphics/PostProcess.h b/include/graphics/PostProcess.h index 5abba6bde..5db0ab07c 100644 --- a/include/graphics/PostProcess.h +++ b/include/graphics/PostProcess.h @@ -65,7 +65,6 @@ namespace graphics { /*! @brief Gets and sets the input textures. * Derived classes must allocate input texture space in m_inputTextures or setInputTexture will crash. - * setInputTextures should be preferred with multiple textures, as setInputTexture will redundantly upload uniforms. */ virtual void setInputTexture (ui32 index, VGTexture inputTexture) { m_inputTextures.at(index) = inputTexture; } virtual void setInputTextures(std::vector inputTextures) { m_inputTextures = inputTextures; } From 729324c4b5b306f52eae04fd16f5551337adde25 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Sat, 16 Jul 2016 14:32:29 -0700 Subject: [PATCH 23/27] Added first pass SSAO --- include/graphics/PostProcess.h | 37 +++++ src/graphics/PostProcess.cpp | 257 +++++++++++++++++++++++++++++++-- 2 files changed, 284 insertions(+), 10 deletions(-) diff --git a/include/graphics/PostProcess.h b/include/graphics/PostProcess.h index 5db0ab07c..903f9894b 100644 --- a/include/graphics/PostProcess.h +++ b/include/graphics/PostProcess.h @@ -34,6 +34,7 @@ namespace graphics { // Forward Declare class GLRenderTarget; class Renderer; + class Camera; // TODO(Ben): Visual shader programming tool with live recompiling. // TODO(Ben): FBO Cacheing. @@ -131,6 +132,42 @@ namespace graphics { f32 m_lumaThreshold = 0.75f; ///< Gaussian variance for blur pass }; + /************************************************************************/ + /* SSAO */ + /************************************************************************/ + /*! @brief Renders the SSAO post process: Screen space ambient occlusion. + */ + class PostProcessSSAO : public IPostProcess { + public: + void init(ui32 windowWidth, ui32 windowHeight, + VGTexture inputColorTexture, + VGTexture inputDepthTexture, + VGTexture inputNormalTexture, + vg::Camera* camera); + + void load() override; + virtual void render() override; + virtual void dispose() override; + + private: + virtual void uploadShaderUniforms(); + + /// Shaders + vg::GLProgram m_programSSAO; + vg::GLProgram m_programBlur; + + vg::Camera* m_camera = nullptr; + + VGTexture m_noiseTexture; + + vg::GLRenderTarget m_ssaoTarget; + + ui32 m_windowWidth = 0; + ui32 m_windowHeight = 0; + + std::vector m_sampleKernel; + }; + /************************************************************************/ /* Passthrough */ /************************************************************************/ diff --git a/src/graphics/PostProcess.cpp b/src/graphics/PostProcess.cpp index 8ed61de93..628117849 100644 --- a/src/graphics/PostProcess.cpp +++ b/src/graphics/PostProcess.cpp @@ -2,9 +2,14 @@ #include "graphics/PostProcess.h" #include "VorbAssert.hpp" #include "math/VorbMath.hpp" +#include "graphics/SamplerState.h" #include "graphics/ShaderCommon.inl" #include "graphics/ShaderManager.h" #include "graphics/Renderer.h" +#include "graphics/Camera.h" + +#include + vg::FullQuadVBO vg::IPostProcess::quad; @@ -24,15 +29,18 @@ bool vg::IPostProcess::tryInitQuad() { return true; } -#define TEXTURE_SLOT_INPUT 4 -static_assert(TEXTURE_SLOT_INPUT >= (ui32)vg::GBUFFER_TEXTURE_UNITS::COUNT, "PostProcess input will collide with GBuffer"); +#define TEXTURE_SLOT_INPUT0 4 +#define TEXTURE_SLOT_INPUT1 5 +#define TEXTURE_SLOT_INPUT2 6 +#define TEXTURE_SLOT_INPUT3 7 +static_assert(TEXTURE_SLOT_INPUT0 >= (ui32)vg::GBUFFER_TEXTURE_UNITS::COUNT, "PostProcess input will collide with GBuffer"); /************************************************************************/ /* Bloom */ /************************************************************************/ -#define BLOOM_TEXTURE_SLOT_LUMA 5 // texture slot to bind luma texture -#define BLOOM_TEXTURE_SLOT_BLUR 6 // texture slot to bind blur texture= +#define BLOOM_TEXTURE_SLOT_LUMA TEXTURE_SLOT_INPUT2 // texture slot to bind luma texture +#define BLOOM_TEXTURE_SLOT_BLUR TEXTURE_SLOT_INPUT3 // texture slot to bind blur texture #pragma region BloomShaderCode @@ -137,6 +145,106 @@ void main() { } )"; +/************************************************************************/ +/* SSAO */ +/************************************************************************/ + +#define SSAO_NOISE_TEXTURE_SIZE 4 +#define SSAO_SAMPLE_KERNEL_SIZE 32 +#define SSAO_BLUR_AMOUNT 2.0f + +const cString SSAO_FRAG_SRC = R"( +// Uniforms +uniform sampler2D unTexDepth; +uniform sampler2D unTexNormal; +uniform sampler2D unTexNoise; +const int SAMPLE_KERNEL_SIZE = 32; +uniform vec3 unSampleKernel[SAMPLE_KERNEL_SIZE]; +uniform vec2 unNoiseScale; +uniform float unRadius = 1.0; + +uniform mat4 unViewMatrix; +uniform mat4 unProjectionMatrix; +uniform mat4 unInvProjectionMatrix; + +// Input +in vec2 fUV; + +// Output +out float pColor; + +vec3 viewSpaceCoordinate(float depth) { + vec4 screenSpaceCoordinate = vec4(fUV.x * 2.0 - 1.0, fUV.y * 2.0 - 1.0, depth, 1.0); + screenSpaceCoordinate = unInvProjectionMatrix * screenSpaceCoordinate; + return screenSpaceCoordinate.xyz / screenSpaceCoordinate.w; +} + +// From this http://john-chapman-graphics.blogspot.de/2013/01/ssao-tutorial.html + +void main() { + float depth = texture(unTexDepth, fUV).r; + vec3 origin = viewSpaceCoordinate(depth); + + vec3 normal = (unViewMatrix * vec4(normalize(texture(unTexNormal, fUV).xyz), 1.0)).xyz; + + // Random sample kernel rotation + vec3 rotationVector = normalize(vec3(texture(unTexNoise, fUV * unNoiseScale).xy, 0.0)); + vec3 tangent = normalize(rotationVector - normal * dot(rotationVector, normal)); + vec3 bitangent = cross(normal, tangent); + mat3 tbn = mat3(tangent, bitangent, normal); + + float totalOcclusion = 0.0; + for (int i = 0; i < SAMPLE_KERNEL_SIZE; ++i) { + // Get sample position + vec3 sample = (tbn * unSampleKernel[i]) * unRadius + origin; + + // Project sample position + vec4 screenSpaceSample = unProjectionMatrix * vec4(sample, 1.0); + screenSpaceSample.xy /= screenSpaceSample.w; + screenSpaceSample.xy = screenSpaceSample.xy * 0.5 + 0.5; + // Get sample depth + float sampleDepth = texture(unTexDepth, screenSpaceSample.xy).r; + // Range check and accumulate + // TODO(Ben): No branching? + float rangeCheck = abs(depth - sampleDepth) < unRadius ? 1.0 : 0.0; + totalOcclusion += (sampleDepth < depth ? 1.0 : 0.0) * rangeCheck; + } + + pColor = (1.0 - totalOcclusion / float(SAMPLE_KERNEL_SIZE)); +} +)"; + +// TODO(Ben): 2 pass optimization? +const cString SSAO_BLUR_FRAG_SRC = R"( +// Uniforms +uniform sampler2D unTexSSAO; +uniform sampler2D unTexColor; +uniform float unBlurAmount; + +// Input +in vec2 fUV; + +// Output +out vec4 pColor; +out vec4 pNormal; + +void main() { + float blurSSAO = 0.0f; + vec2 texelSize = 1.0 / textureSize(unTexSSAO, 0); + int samples = 0; + for (float x = fUV.x - texelSize.x * unBlurAmount; x <= fUV.x + texelSize.x * unBlurAmount; x += texelSize.x) { + for (float y = fUV.y - texelSize.y * unBlurAmount; y <= fUV.y + texelSize.y * unBlurAmount; y += texelSize.y) { + blurSSAO += texture(unTexSSAO, vec2(x, y)).r; + samples++; + } + } + blurSSAO /= float(samples); + + vec4 textureColor = texture(unTexColor, fUV); + pColor = vec4(textureColor.rgb * blurSSAO, textureColor.a); +} +)"; + #pragma endregion inline f32 gauss(int i, f32 sigma2) { @@ -188,7 +296,7 @@ void vg::PostProcessBloom::render() { // luma pass rendering on temporary FBO 1 using the color gbuffer m_fbos[0].use(); - glActiveTexture(GL_TEXTURE0 + TEXTURE_SLOT_INPUT); + glActiveTexture(GL_TEXTURE0 + TEXTURE_SLOT_INPUT0); glBindTexture(GL_TEXTURE_2D, m_inputTextures.at(0)); renderStage(m_programLuma); @@ -227,7 +335,7 @@ void vg::PostProcessBloom::dispose() { void vg::PostProcessBloom::uploadShaderUniforms() { if (m_programGaussianSecond.isLinked()) { m_programLuma.use(); - glUniform1i(m_programLuma.getUniform("unTexColor"), TEXTURE_SLOT_INPUT); + glUniform1i(m_programLuma.getUniform("unTexColor"), TEXTURE_SLOT_INPUT0); glUniform1f(m_programLuma.getUniform("unLumaThresh"), m_lumaThreshold); m_programLuma.unuse(); @@ -238,7 +346,7 @@ void vg::PostProcessBloom::uploadShaderUniforms() { m_programGaussianFirst.unuse(); m_programGaussianSecond.use(); - glUniform1i(m_programGaussianSecond.getUniform("unTexColor"), TEXTURE_SLOT_INPUT); + glUniform1i(m_programGaussianSecond.getUniform("unTexColor"), TEXTURE_SLOT_INPUT0); glUniform1i(m_programGaussianSecond.getUniform("unTexBlur"), BLOOM_TEXTURE_SLOT_BLUR); glUniform1f(m_programGaussianSecond.getUniform("unInvWidth"), 1.0f / (f32)m_windowWidth); glUniform1i(m_programGaussianSecond.getUniform("unGaussianN"), m_gaussianN); @@ -275,6 +383,135 @@ void vg::PostProcessBloom::renderStage(vg::GLProgram& program) { program.unuse(); } +void vg::PostProcessSSAO::init(ui32 windowWidth, ui32 windowHeight, + VGTexture inputColorTexture, + VGTexture inputDepthTexture, + VGTexture inputNormalTexture, + vg::Camera* camera) { + m_windowWidth = windowWidth; + m_windowHeight = windowHeight; + m_inputTextures.resize(3, 0); + m_inputTextures[0] = inputColorTexture; + m_inputTextures[1] = inputDepthTexture; + m_inputTextures[2] = inputNormalTexture; + m_camera = camera; +} + +void vg::PostProcessSSAO::load() { + std::mt19937 randGenerator; + std::uniform_real_distribution range1(-1.0f, 1.0f); + std::uniform_real_distribution range2(0.0f, 1.0f); + + // Generate random data + i32 pixCount = SSAO_NOISE_TEXTURE_SIZE * SSAO_NOISE_TEXTURE_SIZE; + std::vector data(pixCount); + + for (i32 i = 0; i < pixCount; i++) { + // TODO(Ben): vec3? + data[i].x = range1(randGenerator); + data[i].y = range1(randGenerator); + data[i] = vmath::normalize(data[i]); + } + + // Build noise texture + glGenTextures(1, &m_noiseTexture); + glBindTexture(GL_TEXTURE_2D, m_noiseTexture); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RG16F, SSAO_NOISE_TEXTURE_SIZE, SSAO_NOISE_TEXTURE_SIZE, 0, GL_RG, GL_FLOAT, data.data()); + vg::SamplerState::POINT_WRAP.set(GL_TEXTURE_2D); + glBindTexture(GL_TEXTURE_2D, 0); + + m_ssaoTarget.setSize(m_windowWidth, m_windowHeight); + m_ssaoTarget.init(vg::TextureInternalFormat::R32F); + + m_sampleKernel.resize(SSAO_SAMPLE_KERNEL_SIZE); + for (unsigned int i = 0; i < SSAO_SAMPLE_KERNEL_SIZE; i++) { + m_sampleKernel[i] = vmath::normalize(f32v3(range1(randGenerator), + range1(randGenerator), + range2(randGenerator))); + // Use accelerating interpolation + f32 scale = (f32)i / (f32)SSAO_SAMPLE_KERNEL_SIZE; + scale = lerp(0.1f, 1.0f, scale * scale); + m_sampleKernel[i] *= scale; + } + + m_programSSAO = ShaderManager::createProgram(shadercommon::PASSTHROUGH_2D_VERT_SRC, SSAO_FRAG_SRC); + m_programBlur = ShaderManager::createProgram(shadercommon::PASSTHROUGH_2D_VERT_SRC, SSAO_BLUR_FRAG_SRC); + + uploadShaderUniforms(); +} + +void vg::PostProcessSSAO::render() { + glDisable(GL_BLEND); + glDisable(GL_DEPTH_TEST); + + const f32m4& projectionMatrix = m_camera->getProjectionMatrix(); + const f32m4& viewMatrix = m_camera->getViewMatrix(); + + { // SSAO pass + m_ssaoTarget.use(); + glClear(GL_COLOR_BUFFER_BIT); + + // Bind textures + glActiveTexture(GL_TEXTURE0 + TEXTURE_SLOT_INPUT0); + glBindTexture(GL_TEXTURE_2D, m_inputTextures.at(1)); + glActiveTexture(GL_TEXTURE0 + TEXTURE_SLOT_INPUT1); + glBindTexture(GL_TEXTURE_2D, m_inputTextures.at(2)); + glActiveTexture(GL_TEXTURE0 + TEXTURE_SLOT_INPUT2); + glBindTexture(GL_TEXTURE_2D, m_noiseTexture); + + m_programSSAO.use(); + + glUniformMatrix4fv(m_programSSAO.getUniform("unViewMatrix"), 1, false, &viewMatrix[0][0]); + glUniformMatrix4fv(m_programSSAO.getUniform("unProjectionMatrix"), 1, false, &projectionMatrix[0][0]); + glUniformMatrix4fv(m_programSSAO.getUniform("unInvProjectionMatrix"), 1, false, &vmath::inverse(projectionMatrix)[0][0]); + + quad.draw(); + } + + { // Blur pass + glBindFramebuffer(GL_FRAMEBUFFER, m_outputFBO); + glActiveTexture(GL_TEXTURE0 + TEXTURE_SLOT_INPUT0); + glBindTexture(GL_TEXTURE_2D, m_inputTextures.at(0)); + glActiveTexture(GL_TEXTURE0 + TEXTURE_SLOT_INPUT1); + glBindTexture(GL_TEXTURE_2D, m_ssaoTarget.getTextureID()); + + m_programBlur.use(); + + quad.draw(); + + vg::GLProgram::unuse(); + } + + glEnable(GL_DEPTH_TEST); + glEnable(GL_BLEND); +} + +void vg::PostProcessSSAO::dispose() { + if (m_noiseTexture) { + glDeleteTextures(1, &m_noiseTexture); + m_noiseTexture = 0; + } + m_programSSAO.dispose(); + m_ssaoTarget.dispose(); +} + +void vg::PostProcessSSAO::uploadShaderUniforms() { + m_programSSAO.use(); + glUniform1i(m_programSSAO.getUniform("unTexDepth"), TEXTURE_SLOT_INPUT0); + glUniform1i(m_programSSAO.getUniform("unTexNormal"), TEXTURE_SLOT_INPUT1); + glUniform1i(m_programSSAO.getUniform("unTexNoise"), TEXTURE_SLOT_INPUT2); + glUniform3fv(glGetUniformLocation(m_programSSAO.getID(), "unSampleKernel"), m_sampleKernel.size(), &m_sampleKernel.data()->x); + glUniform2f(m_programSSAO.getUniform("unNoiseScale"), + (f32)m_ssaoTarget.getWidth() / SSAO_NOISE_TEXTURE_SIZE, + (f32)m_ssaoTarget.getHeight() / SSAO_NOISE_TEXTURE_SIZE); + + m_programBlur.use(); + glUniform1i(m_programBlur.getUniform("unTexColor"), TEXTURE_SLOT_INPUT0); + glUniform1i(m_programBlur.getUniform("unTexSSAO"), TEXTURE_SLOT_INPUT1); + glUniform1f(m_programBlur.getUniform("unBlurAmount"), SSAO_BLUR_AMOUNT); + m_programBlur.unuse(); +} + void vg::PostProcessPassthrough::init(VGTexture inputTexture) { m_inputTextures.resize(1, inputTexture); } @@ -292,7 +529,7 @@ void vg::PostProcessPassthrough::render() { glBindFramebuffer(GL_FRAMEBUFFER, m_outputFBO); - glActiveTexture(GL_TEXTURE0 + TEXTURE_SLOT_INPUT); + glActiveTexture(GL_TEXTURE0 + TEXTURE_SLOT_INPUT0); glBindTexture(GL_TEXTURE_2D, m_inputTextures.at(0)); m_program.use(); @@ -311,7 +548,7 @@ void vg::PostProcessPassthrough::uploadShaderUniforms() { if (m_program.isLinked()) { // Can interrupt outer state m_program.use(); - glUniform1i(m_program.getUniform("unSampler"), TEXTURE_SLOT_INPUT); + glUniform1i(m_program.getUniform("unSampler"), TEXTURE_SLOT_INPUT0); m_program.unuse(); } -} \ No newline at end of file +} From 13f5351dca79d4ac7342f84228f65b7c2047188b Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Sat, 16 Jul 2016 15:06:36 -0700 Subject: [PATCH 24/27] Added first pass input mapper --- Vorb.vcxproj | 2 + Vorb.vcxproj.filters | 6 ++ include/ui/InputMapper.h | 142 +++++++++++++++++++++++++ src/ui/InputMapper.cpp | 216 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 366 insertions(+) create mode 100644 include/ui/InputMapper.h create mode 100644 src/ui/InputMapper.cpp diff --git a/Vorb.vcxproj b/Vorb.vcxproj index 499b9ed67..02453504a 100644 --- a/Vorb.vcxproj +++ b/Vorb.vcxproj @@ -356,6 +356,7 @@ $(SolutionDir)DepsBuildCopy.bat + @@ -506,6 +507,7 @@ $(SolutionDir)DepsBuildCopy.bat + diff --git a/Vorb.vcxproj.filters b/Vorb.vcxproj.filters index bd1f1a92c..f2b3a8b90 100644 --- a/Vorb.vcxproj.filters +++ b/Vorb.vcxproj.filters @@ -291,6 +291,9 @@ Graphics + + UI\Input + @@ -748,6 +751,9 @@ Voxel + + UI\Input + diff --git a/include/ui/InputMapper.h b/include/ui/InputMapper.h new file mode 100644 index 000000000..9e72c26aa --- /dev/null +++ b/include/ui/InputMapper.h @@ -0,0 +1,142 @@ +/// +/// InputMapper.h +/// Seed of Andromeda +/// +/// Created by Frank McCoy +/// Refactored by Ben Arnold on Mar 25 2015 +/// Copyright 2014 Regrowth Studios +/// All Rights Reserved +/// +/// Summary: +/// Handles mapping of input for keys and buttons. +/// + +#pragma once + +#ifndef Input_Manager_h +#define Input_Manager_h + +#include "../Events.hpp" +#include "InputDispatcher.h" + +#define INPUTMAPPER_DEFAULT_CONFIG_LOCATION "Data/KeyConfig.yml" + +namespace vorb { +namespace ui { + + + /// Handles all the user input through the mouse, keyboard and gamepad. + /// @author Frank McCoy and Ben Arnold + class InputMapper { + public: + typedef Event::Listener Listener; + typedef i32 InputID; + + /// Constructor. + InputMapper(); + /// Destructor. + ~InputMapper(); + + /// The data for a single Input. + class Input { + public: + Input(InputID ID, const nString& nm, VirtualKey defKey, InputMapper* parent) : + id(ID), + name(nm), + defaultKey(defKey), + key(defKey), + upEvent(parent), + downEvent(parent){ + // Empty + } + InputID id; + nString name; ///< The name of the input. + VirtualKey defaultKey; ///< The default key. + VirtualKey key; ///< The actual key. + Event upEvent; ///< The event for when the key is released + Event downEvent; ///< The event for when the key is pressed + }; + typedef std::vector InputList; + typedef std::unordered_map InputMap; + + /// Returns the state of an input + /// @param id: The id of the input which is being looked up. + /// @return The state of the positive key in the input. + bool getInputState(const InputID id); + + /// Creates a single key input. + /// If the input already exists the old ID is returned and no data is modified. + /// @param inputName: The name of the input to create. + /// @param defaultKey: The default key(positive) for the new input. + /// @return The id of the new input. + InputID createInput(const nString& inputName, VirtualKey defaultKey); // Single key + + + /// Get the positive key of the supplied input. + /// If the input does not exist return UINT32_MAX. + /// @param id: The id of the input to look up. + /// @return The id of the positive key of the input. + VirtualKey getKey(const InputID id); + + /// Set the positive key of the supplied input. + /// @param id: The id of the input to look up. + /// @param key: The key to set the keys' positive key to. + void setKey(const InputID id, VirtualKey key); + + /// Resets the axes' positive key to the default. + /// @param id: The input to reset to default. + void setKeyToDefault(const InputID id); + + /// Gets the input ID for the supplied input. + /// If supplied an invalid inputName the function returns -1. + /// @param inputName: The name of the input to look up. + /// @return The id of the supplied input. + InputID getInputID(const nString& inputName) const; + + /// Reads all the axes stored in a given ini file. + /// @param filePath: The local path to the file to load axes from. + void loadInputs(const nString& filePath = INPUTMAPPER_DEFAULT_CONFIG_LOCATION); + + /// Saves currently stored axes to the given file path. + /// @param filePath: The local filePath to the file to save the loaded axes into. + void saveInputs(const nString& filePath = INPUTMAPPER_DEFAULT_CONFIG_LOCATION); + + /// Begins receiving input events from dispatcher + void startInput(); + /// Stops receiving input events from dispatcher + void stopInput(); + + const bool& isRecievingInput() const { return m_receivingInput; } + + /// Gets the input associated with the InputID + Input& get(InputID i) { + return m_inputs[i]; + } + Input& operator[](InputID i) { + return m_inputs[i]; + } + + const InputMap& getInputLookup() const { return m_inputLookup; } + + private: + void onMouseButtonDown(Sender, const vui::MouseButtonEvent& e); + void onMouseButtonUp(Sender, const vui::MouseButtonEvent& e); + void onKeyDown(Sender, const vui::KeyEvent& e); + void onKeyUp(Sender, const vui::KeyEvent& e); + + InputList m_inputs; ///< All the stored axes. + InputMap m_inputLookup; ///< A map of input names to input IDs for quick look up. + std::unordered_map > m_keyCodeMap; ///< Map of keycodes to active input + + /// Assuming vui::MouseButton wont change... + bool m_keyStates[VKEY_HIGHEST_VALUE + (ui32)vui::MouseButton::X2]; ///< The state of the keys and mouse buttons this frame. + + bool m_receivingInput = false; ///< Tracks input reception state + AutoDelegatePool m_inputHooks; ///< Stores input reception function hooks for deallocation + }; + +} +} +namespace vui = vorb::ui; + +#endif //Input_Manager_h diff --git a/src/ui/InputMapper.cpp b/src/ui/InputMapper.cpp new file mode 100644 index 000000000..97102a098 --- /dev/null +++ b/src/ui/InputMapper.cpp @@ -0,0 +1,216 @@ +#include "stdafx.h" +#include "ui/InputMapper.h" + +#include "io/Keg.h" +#include "io/IOManager.h" + +struct InputKegArray { + Array defaultKey; + Array key; +}; +KEG_TYPE_DECL(InputKegArray); +KEG_TYPE_DEF(InputKegArray, InputKegArray, kt) { + using namespace keg; + kt.addValue("defaultKey", Value::array(offsetof(InputKegArray, defaultKey), Value::custom(0, "VirtualKey", true))); + kt.addValue("key", Value::array(offsetof(InputKegArray, key), Value::custom(0, "VirtualKey", true))); +} +vui::InputMapper::InputMapper() { + memset(m_keyStates, 0, sizeof(m_keyStates)); +} + +vui::InputMapper::~InputMapper() { + stopInput(); +} + +bool vui::InputMapper::getInputState(const InputID id) { + // Check Input + if (id < 0 || id >= (int)m_inputs.size()) return false; + return m_keyStates[m_inputs.at(id).key]; +} + +vui::InputMapper::InputID vui::InputMapper::createInput(const nString& inputName, VirtualKey defaultKey) { + InputID id = getInputID(inputName); + if (id >= 0) return id; + id = m_inputs.size(); + m_inputLookup[inputName] = id; + m_inputs.emplace_back(id, inputName, defaultKey, this); + m_keyCodeMap[m_inputs.back().key].push_back(id); + return id; +} + +vui::InputMapper::InputID vui::InputMapper::getInputID(const nString& inputName) const { + auto iter = m_inputLookup.find(inputName); + + if (iter != m_inputLookup.end()) { + return iter->second; + } else { + return -1; + } +} + +void vui::InputMapper::loadInputs(const nString &location /* = INPUTMAPPER_DEFAULT_CONFIG_LOCATION */) { + vio::IOManager iom; //TODO PASS IN + nString data; + + // If the file doesn't exist, just make it with defaults + if (!iom.fileExists(location)) { + saveInputs(location); + return; + } + + iom.readFileToString(location.c_str(), data); + + if (data.length() == 0) { + fprintf(stderr, "Failed to load %s", location.c_str()); + throw 33; + } + + keg::ReadContext context; + context.env = keg::getGlobalEnvironment(); + context.reader.init(data.c_str()); + keg::Node node = context.reader.getFirst(); + if (keg::getType(node) != keg::NodeType::MAP) { + perror(location.c_str()); + context.reader.dispose(); + throw 34; + } + + // Manually parse yml file + auto f = makeFunctor([&] (Sender, const nString& name, keg::Node value) { + InputKegArray kegArray; + + keg::parse((ui8*)&kegArray, value, context, &KEG_GET_TYPE(InputKegArray)); + + // TODO(Ben): Somehow do multikey support + // Right now its only using the first key + InputID id = m_inputs.size(); + m_inputs.emplace_back(id, name, kegArray.defaultKey[0], this); + m_keyCodeMap[m_inputs.back().key].push_back(id); + + if (kegArray.key.size()) { + m_inputs.back().key = kegArray.key[0]; + } else { + m_inputs.back().key = kegArray.defaultKey[0]; + } + + m_inputLookup[name] = id; + + }); + context.reader.forAllInMap(node, f); + delete f; + context.reader.dispose(); +} + +void vui::InputMapper::startInput() { + if (!m_receivingInput) { + vui::InputDispatcher::mouse.onButtonDown += makeDelegate(*this, &vui::InputMapper::onMouseButtonDown); + vui::InputDispatcher::mouse.onButtonUp += makeDelegate(*this, &vui::InputMapper::onMouseButtonDown); + vui::InputDispatcher::key.onKeyDown += makeDelegate(*this, &vui::InputMapper::onKeyDown); + vui::InputDispatcher::key.onKeyUp += makeDelegate(*this, &vui::InputMapper::onKeyUp); + m_receivingInput = true; + } +} +void vui::InputMapper::stopInput() { + if (m_receivingInput) { + vui::InputDispatcher::mouse.onButtonDown -= makeDelegate(*this, &vui::InputMapper::onMouseButtonDown); + vui::InputDispatcher::mouse.onButtonUp -= makeDelegate(*this, &vui::InputMapper::onMouseButtonDown); + vui::InputDispatcher::key.onKeyDown -= makeDelegate(*this, &vui::InputMapper::onKeyDown); + vui::InputDispatcher::key.onKeyUp -= makeDelegate(*this, &vui::InputMapper::onKeyUp); + m_receivingInput = false; + } +} + +void vui::InputMapper::saveInputs(const nString &filePath /* = INPUTMAPPER_DEFAULT_CONFIG_LOCATION */) { + //TODO(Ben): Implement + // vio::IOManager iom; + // Just build the data string manually then write it + + /* bool tmp; + keg::Enum enm; + nString data = ""; + for (auto& input : m_inputs) { + data += input->name + ":\n"; + data += " defaultKey:\n"; + data += " - " + keg::getEnum(tmp, .getValue() + }*/ +} + +VirtualKey vui::InputMapper::getKey(const InputID id) { + if (id < 0 || id >= (int)m_inputs.size()) return VKEY_HIGHEST_VALUE; + return m_inputs.at(id).key; +} + +void vui::InputMapper::setKey(const InputID id, VirtualKey key) { + // Need to remove old key state + VirtualKey oldKey = m_inputs.at(id).key; + auto& it = m_keyCodeMap.find(oldKey); + auto& vec = it->second; + for (size_t i = 0; i < vec.size(); i++) { + // Remove the input from the vector keyed on VirtualKey + if (vec[i] == id) { + vec[i] = vec.back(); + vec.pop_back(); + break; + } + } + // Set new key + m_keyCodeMap[key].push_back(id); + m_inputs[id].key = key; +} + +void vui::InputMapper::setKeyToDefault(const InputID id) { + if (id < 0 || id >= (int)m_inputs.size()) return; + setKey(id, m_inputs.at(id).defaultKey); +} + +void vui::InputMapper::onMouseButtonDown(Sender, const vui::MouseButtonEvent& e) { + ui32 code = VKEY_HIGHEST_VALUE + (ui32)e.button; + if (!m_keyStates[code]) { + m_keyStates[code] = true; + // TODO(Ben): input mapping for mouse + //auto& it = m_keyCodeMap.find((VirtualKey)e.keyCode); + //if (it != m_keyCodeMap.end()) { + // // Call all events mapped to that virtual key + // for (auto& id : it->second) { + // m_inputs[id].downEvent(e.keyCode); + // } + //} + } +} + +void vui::InputMapper::onMouseButtonUp(Sender, const vui::MouseButtonEvent& e) { + ui32 code = VKEY_HIGHEST_VALUE + (ui32)e.button; + m_keyStates[code] = false; + // TODO(Ben): input mapping for mouse + //auto& it = m_keyCodeMap.find((VirtualKey)e.keyCode); + //if (it != m_keyCodeMap.end()) { + // // Call all events mapped to that virtual key + // for (auto& id : it->second) { + // m_inputs[id].upEvent(e.keyCode); + // } + //} +} + +void vui::InputMapper::onKeyDown(Sender, const vui::KeyEvent& e) { + if (!m_keyStates[e.keyCode]) { + m_keyStates[e.keyCode] = true; + auto& it = m_keyCodeMap.find((VirtualKey)e.keyCode); + if (it != m_keyCodeMap.end()) { + // Call all events mapped to that virtual key + for (auto& id : it->second) { + m_inputs[id].downEvent(e.keyCode); + } + } + } +} + +void vui::InputMapper::onKeyUp(Sender, const vui::KeyEvent& e) { + m_keyStates[e.keyCode] = false; + auto& it = m_keyCodeMap.find((VirtualKey)e.keyCode); + if (it != m_keyCodeMap.end()) { + // Call all events mapped to that virtual key + for (auto& id : it->second) { + m_inputs[id].upEvent(e.keyCode); + } + } +} From a4c4dec303db0832930f02c2471d48940c34d2e1 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Sun, 24 Jul 2016 10:14:44 -0700 Subject: [PATCH 25/27] Added test code for determining bug --- include/graphics/ShaderCommon.inl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/graphics/ShaderCommon.inl b/include/graphics/ShaderCommon.inl index cc9beeeee..6588d92aa 100644 --- a/include/graphics/ShaderCommon.inl +++ b/include/graphics/ShaderCommon.inl @@ -50,7 +50,7 @@ uniform sampler2D unSampler; out vec4 pColor; void main() { - pColor = texture(unSampler, fUV); + pColor = vec4(texture(unSampler, fUV).rgb, 1.0); })"; const cString const COLOR_FRAG_SRC = R"( From 7bf0ca3961aa4fce36d7dd3d27117bf07441bb64 Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Sun, 24 Jul 2016 11:19:36 -0700 Subject: [PATCH 26/27] Changed some camera weirdness --- include/graphics/Camera.h | 6 +++--- include/graphics/ShaderCommon.inl | 2 +- src/graphics/Camera.cpp | 25 +++++++++++++------------ 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/include/graphics/Camera.h b/include/graphics/Camera.h index a69da284a..a4739168b 100644 --- a/include/graphics/Camera.h +++ b/include/graphics/Camera.h @@ -82,7 +82,7 @@ namespace graphics { void setFocalPoint (const f64v3& focalPoint) { m_focalPoint = focalPoint; m_viewChanged = true; } void setPosition (const f64v3& position) { m_focalPoint = m_position = position; m_focalLength = 0; m_viewChanged = true; } void setDirection (const f32v3& direction) { m_direction = direction; m_viewChanged = true; } - void setRight (const f32v3& right) { m_right = right; m_viewChanged = true; } + void setLeft (const f32v3& left) { m_left = left; m_viewChanged = true; } void setUp (const f32v3& up) { m_up = up; m_viewChanged = true; } void setClippingPlane(f32 zNear, f32 zFar) { m_zNear = zNear; m_zFar = zFar; m_projectionChanged = true; } void setFieldOfView (f32 fieldOfView) { m_fieldOfView = fieldOfView; m_projectionChanged = true; } @@ -98,7 +98,7 @@ namespace graphics { const f64& getMaxFocalLength() const { return m_maxFocalLength; } const f32v3& getDirection() const { return m_direction; } - const f32v3& getRight () const { return m_right; } + const f32v3& getLeft () const { return m_left; } const f32v3& getUp () const { return m_up; } const f32m4& getProjectionMatrix () const { return m_projectionMatrix; } @@ -126,7 +126,7 @@ namespace graphics { f32 m_aspectRatio = 0.0f; f32v3 m_direction = f32v3(1.0f, 0.0f, 0.0f); - f32v3 m_right = f32v3(0.0f, 0.0f, 1.0f); + f32v3 m_left = f32v3(0.0f, 0.0f, 1.0f); f32v3 m_up = f32v3(0.0f, 1.0f, 0.0f); f32m4 m_projectionMatrix; diff --git a/include/graphics/ShaderCommon.inl b/include/graphics/ShaderCommon.inl index 6588d92aa..7552504c0 100644 --- a/include/graphics/ShaderCommon.inl +++ b/include/graphics/ShaderCommon.inl @@ -50,7 +50,7 @@ uniform sampler2D unSampler; out vec4 pColor; void main() { - pColor = vec4(texture(unSampler, fUV).rgb, 1.0); + pColor = vec4(texture(unSampler, fUV).rgb, 1.0); // *TEMP Force alpha to 1 })"; const cString const COLOR_FRAG_SRC = R"( diff --git a/src/graphics/Camera.cpp b/src/graphics/Camera.cpp index 634d95c67..cdd9481ac 100644 --- a/src/graphics/Camera.cpp +++ b/src/graphics/Camera.cpp @@ -46,33 +46,33 @@ void vg::Camera::offsetPosition(const f64v3& offset) { void vg::Camera::rotate(const f32q& rot) { m_direction = rot * m_direction; - m_right = rot * m_right; - m_up = vmath::normalize(vmath::cross(m_right, m_direction)); + m_left = rot * m_left; + m_up = vmath::normalize(vmath::cross(m_left, m_direction)); m_viewChanged = true; } void vg::Camera::rotate(f32 yaw, f32 pitch) { - f32q upQuat = vmath::angleAxis(pitch, m_right); + f32q upQuat = vmath::angleAxis(pitch, m_left); f32q rightQuat = vmath::angleAxis(yaw, m_up); rotate(upQuat * rightQuat); } void vg::Camera::rotateAbsoluteUp(f32 yaw, f32 pitch, bool clampVerticalRotation /*= false*/) { - f32q upQuat = vmath::angleAxis(pitch, m_right); + f32q upQuat = vmath::angleAxis(pitch, m_left); f32q rightQuat = vmath::angleAxis(yaw, UP_ABSOLUTE); f32v3 previousDirection = m_direction; f32v3 previousUp = m_up; - f32v3 previousRight = m_right; + f32v3 previousRight = m_left; rotate(upQuat * rightQuat); if (clampVerticalRotation && m_up.y < 0) { m_direction = previousDirection; m_up = previousUp; - m_right = previousRight; + m_left = previousRight; rotateAbsoluteUp(yaw, 0.0f); } } @@ -84,22 +84,23 @@ void vg::Camera::roll(f32 roll) { } void vg::Camera::setOrientation(const f32v3& direction, const f32v3& up) { - m_direction = direction; - m_up = up; - m_right = vmath::normalize(vmath::cross(m_up, m_direction)); + m_direction = vmath::normalize(direction); + m_up = vmath::normalize(up); + m_left = vmath::cross(m_up, m_direction); // We calculate up again to guarantee orthogonality - m_up = vmath::normalize(vmath::cross(m_right, m_direction)); + m_up = vmath::cross(m_direction, m_left); } void vg::Camera::setOrientation(const f64q& orientation) { m_direction = orientation * f64v3(0.0, 0.0, 1.0); - m_right = orientation * f64v3(1.0, 0.0, 0.0); + m_left = orientation * f64v3(1.0, 0.0, 0.0); m_up = orientation * f64v3(0.0, 1.0, 0.0); m_viewChanged = true; } void vg::Camera::updateView() { - m_viewMatrix = vmath::lookAt(f32v3(0.0f), m_direction, m_up); + // TODO(Ben): Allow camera relative view matrix. + m_viewMatrix = vmath::lookAt(f32v3(m_position), f32v3(m_position) + m_direction, -m_up); } void vg::Camera::updateProjection() { From e74088cba9f341c812be9c4cc4219db0c6ddcdde Mon Sep 17 00:00:00 2001 From: barnold1953 Date: Sun, 24 Jul 2016 11:55:43 -0700 Subject: [PATCH 27/27] Improved mouse input, fixed camera issues --- include/ui/GameWindow.h | 1 + include/ui/InputMapper.h | 3 +-- include/ui/Keys.inl | 6 ++++++ include/ui/MouseInputDispatcher.h | 2 +- src/graphics/Camera.cpp | 8 +++---- src/ui/GameWindow.cpp | 10 +++++++++ src/ui/InputMapper.cpp | 35 +++++++++++++++---------------- 7 files changed, 40 insertions(+), 25 deletions(-) diff --git a/include/ui/GameWindow.h b/include/ui/GameWindow.h index 9b94bf354..2f4434988 100644 --- a/include/ui/GameWindow.h +++ b/include/ui/GameWindow.h @@ -193,6 +193,7 @@ namespace vorb { void setPosition(int x, int y); void setMaxFPS(f32 fpsLimit); void setTitle(const cString title) const; + void setRelativeMouseMode(bool relative) const; void sync(ui32 frameTime); diff --git a/include/ui/InputMapper.h b/include/ui/InputMapper.h index 9e72c26aa..2a02d8bc9 100644 --- a/include/ui/InputMapper.h +++ b/include/ui/InputMapper.h @@ -128,8 +128,7 @@ namespace ui { InputMap m_inputLookup; ///< A map of input names to input IDs for quick look up. std::unordered_map > m_keyCodeMap; ///< Map of keycodes to active input - /// Assuming vui::MouseButton wont change... - bool m_keyStates[VKEY_HIGHEST_VALUE + (ui32)vui::MouseButton::X2]; ///< The state of the keys and mouse buttons this frame. + bool m_keyStates[VKEY_HIGHEST_VALUE]; ///< The state of the keys and mouse buttons this frame. bool m_receivingInput = false; ///< Tracks input reception state AutoDelegatePool m_inputHooks; ///< Stores input reception function hooks for deallocation diff --git a/include/ui/Keys.inl b/include/ui/Keys.inl index 6dc78b3cf..e197f3f0b 100644 --- a/include/ui/Keys.inl +++ b/include/ui/Keys.inl @@ -241,6 +241,12 @@ enum VirtualKey : ui16 { VKEY_KBDILLUMUP, VKEY_EJECT, VKEY_SLEEP, + VKEY_MOUSE_UNKNOWN, + VKEY_MOUSE_LEFT, + VKEY_MOUSE_RIGHT, + VKEY_MOUSE_MIDDLE, + VKEY_MOUSE_X1, + VKEY_MOUSE_X2, VKEY_HIGHEST_VALUE }; diff --git a/include/ui/MouseInputDispatcher.h b/include/ui/MouseInputDispatcher.h index 3280e44b7..44f90c2f5 100644 --- a/include/ui/MouseInputDispatcher.h +++ b/include/ui/MouseInputDispatcher.h @@ -37,7 +37,7 @@ namespace vorb { /// The known mouse buttons enum class MouseButton { - UNKNOWN, ///< An unknown mouse button + UNKNOWN = 0, ///< An unknown mouse button LEFT, ///< The left mouse button MIDDLE, ///< The middle mouse button RIGHT, ///< The right mouse button diff --git a/src/graphics/Camera.cpp b/src/graphics/Camera.cpp index cdd9481ac..fdf9bb4d5 100644 --- a/src/graphics/Camera.cpp +++ b/src/graphics/Camera.cpp @@ -47,7 +47,7 @@ void vg::Camera::offsetPosition(const f64v3& offset) { void vg::Camera::rotate(const f32q& rot) { m_direction = rot * m_direction; m_left = rot * m_left; - m_up = vmath::normalize(vmath::cross(m_left, m_direction)); + m_up = vmath::normalize(vmath::cross(m_direction, m_left)); m_viewChanged = true; } @@ -100,10 +100,10 @@ void vg::Camera::setOrientation(const f64q& orientation) { void vg::Camera::updateView() { // TODO(Ben): Allow camera relative view matrix. - m_viewMatrix = vmath::lookAt(f32v3(m_position), f32v3(m_position) + m_direction, -m_up); + m_viewMatrix = vmath::lookAt(f32v3(m_position), f32v3(m_position) + m_direction, m_up); } void vg::Camera::updateProjection() { - m_frustum.setCamInternals(m_fieldOfView, m_aspectRatio, m_zNear, m_zFar); - m_projectionMatrix = vmath::perspective(m_fieldOfView, m_aspectRatio, m_zNear, m_zFar); + m_frustum.setCamInternals(vmath::radians(m_fieldOfView), m_aspectRatio, m_zNear, m_zFar); + m_projectionMatrix = vmath::perspective(vmath::radians(m_fieldOfView), m_aspectRatio, m_zNear, m_zFar); } diff --git a/src/ui/GameWindow.cpp b/src/ui/GameWindow.cpp index a6402b9ca..666e809fc 100644 --- a/src/ui/GameWindow.cpp +++ b/src/ui/GameWindow.cpp @@ -444,6 +444,16 @@ void vui::GameWindow::setTitle(const cString title) const { #endif } +void vui::GameWindow::setRelativeMouseMode(bool relative) const { +#if defined(VORB_IMPL_UI_SDL) + SDL_SetRelativeMouseMode(static_cast(relative)); +#elif defined(VORB_IMPL_UI_GLFW) + // TODO +#elif defined(VORB_IMPL_UI_SFML) + // TODO +#endif +} + void vui::GameWindow::setPosition(int x, int y) { #if defined(VORB_IMPL_UI_SDL) SDL_SetWindowPosition((SDL_Window*)m_window, x, y); diff --git a/src/ui/InputMapper.cpp b/src/ui/InputMapper.cpp index 97102a098..0dd15606a 100644 --- a/src/ui/InputMapper.cpp +++ b/src/ui/InputMapper.cpp @@ -164,31 +164,30 @@ void vui::InputMapper::setKeyToDefault(const InputID id) { } void vui::InputMapper::onMouseButtonDown(Sender, const vui::MouseButtonEvent& e) { - ui32 code = VKEY_HIGHEST_VALUE + (ui32)e.button; + // These must map 1:1 + ui32 code = VKEY_MOUSE_UNKNOWN + (ui32)e.button; if (!m_keyStates[code]) { m_keyStates[code] = true; - // TODO(Ben): input mapping for mouse - //auto& it = m_keyCodeMap.find((VirtualKey)e.keyCode); - //if (it != m_keyCodeMap.end()) { - // // Call all events mapped to that virtual key - // for (auto& id : it->second) { - // m_inputs[id].downEvent(e.keyCode); - // } - //} + auto& it = m_keyCodeMap.find((VirtualKey)code); + if (it != m_keyCodeMap.end()) { + // Call all events mapped to that virtual key + for (auto& id : it->second) { + m_inputs[id].downEvent(code); + } + } } } void vui::InputMapper::onMouseButtonUp(Sender, const vui::MouseButtonEvent& e) { - ui32 code = VKEY_HIGHEST_VALUE + (ui32)e.button; + ui32 code = VKEY_MOUSE_UNKNOWN + (ui32)e.button; m_keyStates[code] = false; - // TODO(Ben): input mapping for mouse - //auto& it = m_keyCodeMap.find((VirtualKey)e.keyCode); - //if (it != m_keyCodeMap.end()) { - // // Call all events mapped to that virtual key - // for (auto& id : it->second) { - // m_inputs[id].upEvent(e.keyCode); - // } - //} + auto& it = m_keyCodeMap.find((VirtualKey)code); + if (it != m_keyCodeMap.end()) { + // Call all events mapped to that virtual key + for (auto& id : it->second) { + m_inputs[id].downEvent(code); + } + } } void vui::InputMapper::onKeyDown(Sender, const vui::KeyEvent& e) {