From: Daniel Karbach Date: Mon, 6 Nov 2017 21:08:25 +0000 (+0100) Subject: simple planet render X-Git-Url: https://git.localhorst.tv/?a=commitdiff_plain;h=be413456f57da06e918ae7bf4c4f35e5198ff7ce;p=blobs.git simple planet render --- diff --git a/src/app/Application.hpp b/src/app/Application.hpp index 4ed4b76..040935f 100644 --- a/src/app/Application.hpp +++ b/src/app/Application.hpp @@ -5,14 +5,18 @@ namespace blobs { +namespace graphics { + class Viewport; +} namespace app { class State; +class Window; class Application { public: - Application(); + Application(Window &, graphics::Viewport &); ~Application(); Application(const Application &) = delete; @@ -36,6 +40,8 @@ public: void HandleEvents(); private: + Window &window; + graphics::Viewport &viewport; std::stack states; }; diff --git a/src/app/Assets.hpp b/src/app/Assets.hpp new file mode 100644 index 0000000..ed3c2e2 --- /dev/null +++ b/src/app/Assets.hpp @@ -0,0 +1,35 @@ +#ifndef BLOBS_APP_ASSETS_HPP_ +#define BLOBS_APP_ASSETS_HPP_ + +#include "../graphics/ArrayTexture.hpp" +#include "../graphics/PlanetSurface.hpp" + + +namespace blobs { +namespace app { + +struct Assets { + + struct { + graphics::ArrayTexture tiles; + } textures; + + struct { + graphics::PlanetSurface planet_surface; + } shaders; + + Assets(); + ~Assets(); + + Assets(const Assets &) = delete; + Assets &operator =(const Assets &) = delete; + + Assets(Assets &&) = delete; + Assets &operator =(Assets &&) = delete; + +}; + +} +} + +#endif diff --git a/src/app/MasterState.hpp b/src/app/MasterState.hpp new file mode 100644 index 0000000..fb245cb --- /dev/null +++ b/src/app/MasterState.hpp @@ -0,0 +1,57 @@ +#ifndef BLOBS_APP_MASTERSTATE_HPP_ +#define BLOBS_APP_MASTERSTATE_HPP_ + +#include "State.hpp" + +#include "Assets.hpp" +#include "../graphics/Camera.hpp" + + +namespace blobs { +namespace world { + class Body; + class Simulation; +} +namespace app { + +class MasterState +: public State { + +public: + MasterState(Assets &, world::Simulation &) noexcept; + ~MasterState() noexcept; + + MasterState(const MasterState &) = delete; + MasterState &operator =(const MasterState &) = delete; + + MasterState(MasterState &&) = delete; + MasterState &operator =(MasterState &&) = delete; + +public: + void SetReference(world::Body &r) { reference = &r; } + +private: + void OnResize(int w, int h) override; + + void OnUpdate(int dt) override; + void OnRender(graphics::Viewport &) override; + + void Tick(); + int FrameMS() const noexcept; + +private: + Assets &assets; + world::Simulation ∼ + world::Body *reference; + + graphics::Camera cam; + + int remain; + int thirds; + +}; + +} +} + +#endif diff --git a/src/app/State.hpp b/src/app/State.hpp index 4462904..dc73e21 100644 --- a/src/app/State.hpp +++ b/src/app/State.hpp @@ -5,6 +5,9 @@ namespace blobs { +namespace graphics { + class Viewport; +} namespace app { class Application; @@ -16,7 +19,7 @@ class State { void Handle(const SDL_Event &); void Handle(const SDL_WindowEvent &); void Update(int dt); - void Render(); + void Render(graphics::Viewport &); virtual void OnEnter() { } virtual void OnResume() { } @@ -25,13 +28,26 @@ class State { virtual void OnFocus() { } virtual void OnBlur() { } - virtual void OnResize() { } + virtual void OnResize(int w, int h) { } + + virtual void OnKeyDown(const SDL_KeyboardEvent &) { } + virtual void OnKeyUp(const SDL_KeyboardEvent &) { } + virtual void OnMouseDown(const SDL_MouseButtonEvent &) { } + virtual void OnMouseUp(const SDL_MouseButtonEvent &) { } + virtual void OnMouseMotion(const SDL_MouseMotionEvent &) { } + virtual void OnMouseWheel(const SDL_MouseWheelEvent &) { } + virtual void OnQuit(); - virtual void OnEvent(const SDL_Event &); - virtual void OnUpdate(int dt); - virtual void OnRender(); + virtual void OnUpdate(int dt) { } + virtual void OnRender(graphics::Viewport &) { } int ref_count = 0; + Application *app = nullptr; + +protected: + Application &App() { + return *app; + } }; diff --git a/src/app/app.cpp b/src/app/app.cpp index a7bbf39..064786f 100644 --- a/src/app/app.cpp +++ b/src/app/app.cpp @@ -1,14 +1,20 @@ #include "Application.hpp" +#include "Assets.hpp" #include "State.hpp" +#include "init.hpp" +#include "../graphics/Viewport.hpp" + #include namespace blobs { namespace app { -Application::Application() -: states() { +Application::Application(Window &win, graphics::Viewport &vp) +: window(win) +, viewport(vp) +, states() { } Application::~Application() { @@ -16,6 +22,7 @@ Application::~Application() { void Application::PushState(State *s) { + s->app = this; if (!states.empty()) { states.top()->OnPause(); } @@ -39,6 +46,7 @@ State *Application::PopState() { } State *Application::SwitchState(State *s_new) { + s_new->app = this; State *s_old = states.top(); states.top() = s_new; --s_old->ref_count; @@ -78,23 +86,49 @@ void Application::Loop(int dt) { if (!HasState()) return; GetState().Update(dt); if (!HasState()) return; - GetState().Render(); + viewport.Clear(); + GetState().Render(viewport); + window.Flip(); } void Application::HandleEvents() { SDL_Event event; while (HasState() && SDL_PollEvent(&event)) { + if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_RESIZED) { + viewport.Resize(event.window.data1, event.window.data2); + } GetState().Handle(event); } } void State::Handle(const SDL_Event &event) { switch (event.type) { + case SDL_KEYDOWN: + OnKeyDown(event.key); + break; + case SDL_KEYUP: + OnKeyUp(event.key); + break; + case SDL_MOUSEBUTTONDOWN: + OnMouseDown(event.button); + break; + case SDL_MOUSEBUTTONUP: + OnMouseUp(event.button); + break; + case SDL_MOUSEMOTION: + OnMouseMotion(event.motion); + break; + case SDL_MOUSEWHEEL: + OnMouseWheel(event.wheel); + break; + case SDL_QUIT: + OnQuit(); + break; case SDL_WINDOWEVENT: Handle(event.window); break; default: - OnEvent(event); + // ignore break; } } @@ -108,8 +142,7 @@ void State::Handle(const SDL_WindowEvent &event) { OnBlur(); break; case SDL_WINDOWEVENT_RESIZED: - //env.viewport.Resize(event.data1, event.data2); - OnResize(); + OnResize(event.data1, event.data2); break; default: break; @@ -120,8 +153,32 @@ void State::Update(int dt) { OnUpdate(dt); } -void State::Render() { - OnRender(); +void State::Render(graphics::Viewport &viewport) { + OnRender(viewport); +} + +void State::OnQuit() { + while (App().HasState()) { + App().PopState(); + } +} + + +Assets::Assets() { + graphics::Format format; + textures.tiles.Bind(); + textures.tiles.Reserve(1, 1, 3, format); + std::uint8_t texdata[] = { + 0xFF, 0x00, 0x00, 0xFF, + 0x00, 0xFF, 0x00, 0xFF, + 0x00, 0x00, 0xFF, 0xFF, + }; + textures.tiles.Data(0, format, texdata); + textures.tiles.Data(1, format, texdata + 4); + textures.tiles.Data(2, format, texdata + 8); +} + +Assets::~Assets() { } } diff --git a/src/app/error.cpp b/src/app/error.cpp new file mode 100644 index 0000000..d4fe7ed --- /dev/null +++ b/src/app/error.cpp @@ -0,0 +1,87 @@ +#include "error.hpp" + +#include +#include +#include +#include + +using std::string; +using std::runtime_error; + + +namespace { + +string sdl_error_append(string msg) { + const char *error = SDL_GetError(); + if (*error != '\0') { + msg += ": "; + msg += error; + SDL_ClearError(); + } + return msg; +} + +string net_error_append(string msg) { + const char *error = SDLNet_GetError(); + if (*error != '\0') { + msg += ": "; + msg += error; + } + return msg; +} + +string alut_error_append(ALenum num, string msg) { + const char *error = alutGetErrorString(num); + if (*error != '\0') { + msg += ": "; + msg += error; + } + return msg; +} + +string error_append(string msg, const char *err) { + if (err && *err) { + msg += ": "; + msg += err; + } + return msg; +} + +} + +namespace blobs { +namespace app { + +AlutError::AlutError(ALenum num) +: runtime_error(alutGetErrorString(num)) { +} + +AlutError::AlutError(ALenum num, const string &msg) +: runtime_error(alut_error_append(num, msg)) { +} + + +GLError::GLError(const char *msg) +: runtime_error(error_append(msg, reinterpret_cast(gluErrorString(glGetError())))) { +} + + +NetError::NetError() +: runtime_error(SDLNet_GetError()) { +} + +NetError::NetError(const string &msg) +: runtime_error(net_error_append(msg)) { +} + + +SDLError::SDLError() +: runtime_error(SDL_GetError()) { +} + +SDLError::SDLError(const string &msg) +: runtime_error(sdl_error_append(msg)) { +} + +} +} diff --git a/src/app/error.hpp b/src/app/error.hpp new file mode 100644 index 0000000..d04e30c --- /dev/null +++ b/src/app/error.hpp @@ -0,0 +1,49 @@ +#ifndef BLOBS_APP_ERROR_HPP_ +#define BLOBS_APP_ERROR_HPP_ + +#include +#include + + +namespace blobs { +namespace app { + +class AlutError +: public std::runtime_error { + +public: + explicit AlutError(ALenum); + AlutError(ALenum, const std::string &); + +}; + +class GLError +: public std::runtime_error { + +public: + explicit GLError(const char *msg); + +}; + +class SDLError +: public std::runtime_error { + +public: + SDLError(); + explicit SDLError(const std::string &); + +}; + +class NetError +: public std::runtime_error { + +public: + NetError(); + explicit NetError(const std::string &); + +}; + +} +} + +#endif diff --git a/src/app/init.cpp b/src/app/init.cpp index 721452f..5e9bcba 100644 --- a/src/app/init.cpp +++ b/src/app/init.cpp @@ -1,5 +1,7 @@ #include "init.hpp" +#include "error.hpp" + #include #include #include @@ -10,74 +12,10 @@ #include -namespace { - -std::string sdl_error_append(std::string msg) { - const char *error = SDL_GetError(); - if (*error != '\0') { - msg += ": "; - msg += error; - SDL_ClearError(); - } - return msg; -} - -std::string net_error_append(std::string msg) { - const char *error = SDLNet_GetError(); - if (*error != '\0') { - msg += ": "; - msg += error; - } - return msg; -} - -std::string alut_error_append(ALenum num, std::string msg) { - const char *error = alutGetErrorString(num); - if (*error != '\0') { - msg += ": "; - msg += error; - } - return msg; -} - -} namespace blobs { namespace app { -AlutError::AlutError(ALenum num) -: std::runtime_error(alutGetErrorString(num)) { - -} - -AlutError::AlutError(ALenum num, const std::string &msg) -: std::runtime_error(alut_error_append(num, msg)) { - -} - - -NetError::NetError() -: std::runtime_error(SDLNet_GetError()) { - -} - -NetError::NetError(const std::string &msg) -: std::runtime_error(net_error_append(msg)) { - -} - - -SDLError::SDLError() -: std::runtime_error(SDL_GetError()) { - -} - -SDLError::SDLError(const std::string &msg) -: std::runtime_error(sdl_error_append(msg)) { - -} - - InitSDL::InitSDL() { if (SDL_Init(SDL_INIT_EVENTS) != 0) { throw SDLError("SDL_Init(SDL_INIT_EVENTS)"); @@ -177,11 +115,11 @@ InitGL::InitGL(bool double_buffer, int sample_size) { } -Window::Window() +Window::Window(int w, int h) : handle(SDL_CreateWindow( "blobs", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, - 960, 600, + w, h, SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE )) { if (!handle) { @@ -246,20 +184,15 @@ InitGLEW::InitGLEW() { } -InitHeadless::InitHeadless() -: init_sdl() -, init_net() { - -} - Init::Init(bool double_buffer, int sample_size) : init_video() , init_img() , init_ttf() , init_gl(double_buffer, sample_size) -, window() +, window(960, 540) , ctx(window.Handle()) -, init_glew() { +, init_glew() +, viewport(960, 540) { } diff --git a/src/app/init.hpp b/src/app/init.hpp index 63334e8..f1378e0 100644 --- a/src/app/init.hpp +++ b/src/app/init.hpp @@ -1,43 +1,16 @@ #ifndef BLOBS_APP_INIT_HPP_ #define BLOBS_APP_INIT_HPP_ +#include "../graphics/Viewport.hpp" + #include #include -#include #include namespace blobs { namespace app { -class AlutError -: public std::runtime_error { - -public: - explicit AlutError(ALenum); - AlutError(ALenum, const std::string &); - -}; - -class SDLError -: public std::runtime_error { - -public: - SDLError(); - explicit SDLError(const std::string &); - -}; - -class NetError -: public std::runtime_error { - -public: - NetError(); - explicit NetError(const std::string &); - -}; - - class InitSDL { public: @@ -124,7 +97,7 @@ public: class Window { public: - Window(); + Window(int w, int h); ~Window(); Window(const Window &) = delete; @@ -172,15 +145,6 @@ public: }; -struct InitHeadless { - - InitHeadless(); - - InitSDL init_sdl; - InitNet init_net; - -}; - struct Init { Init(bool double_buffer = true, int sample_size = 1); @@ -193,6 +157,7 @@ struct Init { Window window; GLContext ctx; InitGLEW init_glew; + graphics::Viewport viewport; }; diff --git a/src/app/states.cpp b/src/app/states.cpp new file mode 100644 index 0000000..0298e0c --- /dev/null +++ b/src/app/states.cpp @@ -0,0 +1,57 @@ +#include "MasterState.hpp" + +#include "../world/Body.hpp" +#include "../world/Simulation.hpp" + +#include + + +namespace blobs { +namespace app { + +MasterState::MasterState(Assets &assets, world::Simulation &sim) noexcept +: State() +, assets(assets) +, sim(sim) +, reference(&sim.Root()) +, cam() +, remain(0) +, thirds(0) { + cam.View(glm::translate(glm::vec3(-3.0f, -2.0f, -10.0f))); +} + +MasterState::~MasterState() noexcept { +} + + +void MasterState::OnResize(int w, int h) { + cam.Aspect(float(w), float(h)); +} + +void MasterState::OnUpdate(int dt) { + remain += dt; + while (remain >= FrameMS()) { + Tick(); + } +} + +void MasterState::Tick() { + sim.Tick(); + remain -= FrameMS(); + thirds = (thirds + 1) % 3; +} + +int MasterState::FrameMS() const noexcept { + return thirds == 0 ? 16 : 17; +} + + +void MasterState::OnRender(graphics::Viewport &viewport) { + assets.shaders.planet_surface.Activate(); + assets.shaders.planet_surface.SetMVP(glm::mat4(1.0f), cam.View(), cam.Projection()); + assets.shaders.planet_surface.SetTexture(assets.textures.tiles); + reference->Draw(assets, viewport); +} + +} +} diff --git a/src/blobs.cpp b/src/blobs.cpp index 6aa0aa4..8577e32 100644 --- a/src/blobs.cpp +++ b/src/blobs.cpp @@ -1,16 +1,33 @@ #include "app/Application.hpp" +#include "app/Assets.hpp" #include "app/init.hpp" +#include "app/MasterState.hpp" #include "world/Planet.hpp" +#include "world/Simulation.hpp" +#include "world/Sun.hpp" -#include -#include +#include using namespace blobs; int main(int argc, char *argv[]) { app::Init init; - world::Planet planet(1); // r=1 should be a 3³ + app::Assets assets; - app::Application app; + world::Sun sun; + world::Simulation sim(sun); + world::Planet planet(3); + world::GenerateTest(planet); + planet.SetParent(sun); + + app::MasterState state(assets, sim); + state.SetReference(planet); + planet.BuildVAOs(); + + app::Application app(init.window, init.viewport); + app.PushState(&state); + app.Run(); + + return 0; } diff --git a/src/graphics/ArrayTexture.hpp b/src/graphics/ArrayTexture.hpp new file mode 100644 index 0000000..26d7b5f --- /dev/null +++ b/src/graphics/ArrayTexture.hpp @@ -0,0 +1,47 @@ +#ifndef BLOBS_GRAPHICS_ARRAYTEXTURE_HPP_ +#define BLOBS_GRAPHICS_ARRAYTEXTURE_HPP_ + +#include "Format.hpp" +#include "TextureBase.hpp" + +#include + +struct SDL_Surface; + + +namespace blobs { +namespace graphics { + +class ArrayTexture +: public TextureBase { + +public: + ArrayTexture(); + ~ArrayTexture(); + + ArrayTexture(ArrayTexture &&) noexcept; + ArrayTexture &operator =(ArrayTexture &&) noexcept; + + ArrayTexture(const ArrayTexture &) = delete; + ArrayTexture &operator =(const ArrayTexture &) = delete; + +public: + GLsizei Width() const noexcept { return width; } + GLsizei Height() const noexcept { return height; } + GLsizei Depth() const noexcept { return depth; } + + void Reserve(GLsizei w, GLsizei h, GLsizei d, const Format &) noexcept; + void Data(GLsizei l, const SDL_Surface &); + void Data(GLsizei l, const Format &, GLvoid *data) noexcept; + +private: + GLsizei width, height, depth; + + Format format; + +}; + +} +} + +#endif diff --git a/src/graphics/Camera.hpp b/src/graphics/Camera.hpp new file mode 100644 index 0000000..7cc141e --- /dev/null +++ b/src/graphics/Camera.hpp @@ -0,0 +1,49 @@ +#ifndef BLOBS_GRAPHICS_CAMERA_HPP_ +#define BLOBS_GRAPHICS_CAMERA_HPP_ + +#include "glm.hpp" + + +namespace blobs { +namespace graphics { + +class Camera { + +public: + Camera() noexcept; + ~Camera() noexcept; + + Camera(const Camera &) = delete; + Camera &operator =(const Camera &) = delete; + + Camera(Camera &&) = delete; + Camera &operator =(Camera &&) = delete; + +public: + void FOV(float f) noexcept; + void Aspect(float r) noexcept; + void Aspect(float w, float h) noexcept; + void Clip(float near, float far) noexcept; + + const glm::mat4 &Projection() const noexcept { return projection; } + const glm::mat4 &View() const noexcept { return view; } + void View(const glm::mat4 &v) noexcept; + +private: + void UpdateProjection() noexcept; + +private: + float fov; + float aspect; + float near; + float far; + + glm::mat4 projection; + glm::mat4 view; + +}; + +} +} + +#endif diff --git a/src/graphics/CubeMap.hpp b/src/graphics/CubeMap.hpp new file mode 100644 index 0000000..0b426d6 --- /dev/null +++ b/src/graphics/CubeMap.hpp @@ -0,0 +1,47 @@ +#ifndef BLOBS_GRAPHICS_CUBEMAP_HPP_ +#define BLOBS_GRAPHICS_CUBEMAP_HPP_ + +#include "Format.hpp" +#include "TextureBase.hpp" + +#include + +struct SDL_Surface; + + +namespace blobs { +namespace graphics { + +class CubeMap +: public TextureBase { + +public: + enum Face { + RIGHT = GL_TEXTURE_CUBE_MAP_POSITIVE_X, + LEFT = GL_TEXTURE_CUBE_MAP_NEGATIVE_X, + TOP = GL_TEXTURE_CUBE_MAP_POSITIVE_Y, + BOTTOM = GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, + BACK = GL_TEXTURE_CUBE_MAP_POSITIVE_Z, + FRONT = GL_TEXTURE_CUBE_MAP_NEGATIVE_Z, + }; + +public: + CubeMap(); + ~CubeMap(); + + CubeMap(CubeMap &&) noexcept; + CubeMap &operator =(CubeMap &&) noexcept; + + CubeMap(const CubeMap &) = delete; + CubeMap &operator =(const CubeMap &) = delete; + +public: + void Data(Face, const SDL_Surface &); + void Data(Face, GLsizei w, GLsizei h, const Format &, GLvoid *data) noexcept; + +}; + +} +} + +#endif diff --git a/src/graphics/Font.hpp b/src/graphics/Font.hpp new file mode 100644 index 0000000..ce9cfd9 --- /dev/null +++ b/src/graphics/Font.hpp @@ -0,0 +1,75 @@ +#ifndef BLOBS_GRAPHICS_FONT_HPP_ +#define BLOBS_GRAPHICS_FONT_HPP_ + +#include "glm.hpp" + +#include + + +namespace blobs { +namespace graphics { + +class Texture; + +class Font { + +public: + enum FontStyle { + STYLE_NORMAL = TTF_STYLE_NORMAL, + STYLE_BOLD = TTF_STYLE_BOLD, + STYLE_ITALIC = TTF_STYLE_ITALIC, + STYLE_UNDERLINE = TTF_STYLE_UNDERLINE, + STYLE_STRIKE = TTF_STYLE_STRIKETHROUGH, + }; + enum FontHinting { + HINT_NORMAL = TTF_HINTING_NORMAL, + HINT_LIGHT = TTF_HINTING_LIGHT, + HINT_MONO = TTF_HINTING_MONO, + HINT_NONE = TTF_HINTING_NONE, + }; + +public: + Font(const char *src, int size, long index = 0); + ~Font(); + + Font(Font &&) noexcept; + Font &operator =(Font &&) noexcept; + + Font(const Font &) = delete; + Font &operator =(const Font &) = delete; + +public: + int Style() const noexcept; + void Style(int) const noexcept; + int Outline() const noexcept; + void Outline(int) noexcept; + + int Hinting() const noexcept; + void Hinting(int) const noexcept; + bool Kerning() const noexcept; + void Kerning(bool) noexcept; + + int Height() const noexcept; + int Ascent() const noexcept; + int Descent() const noexcept; + int LineSkip() const noexcept; + + const char *FamilyName() const noexcept; + const char *StyleName() const noexcept; + + bool HasGlyph(Uint16) const noexcept; + + glm::ivec2 TextSize(const char *) const; + + Texture Render(const char *) const; + void Render(const char *, Texture &) const; + +private: + TTF_Font *handle; + +}; + +} +} + +#endif diff --git a/src/graphics/Format.hpp b/src/graphics/Format.hpp new file mode 100644 index 0000000..52942d8 --- /dev/null +++ b/src/graphics/Format.hpp @@ -0,0 +1,29 @@ +#ifndef BLOBS_GRAPHICS_FORMAT_HPP_ +#define BLOBS_GRAPHICS_FORMAT_HPP_ + +#include +#include + + +namespace blobs { +namespace graphics { + +struct Format { + + GLenum format; + GLenum type; + GLenum internal; + + SDL_PixelFormat sdl_format; + + Format() noexcept; + explicit Format(const SDL_PixelFormat &) noexcept; + + bool Compatible(const Format &other) const noexcept; + +}; + +} +} + +#endif diff --git a/src/graphics/PlanetSurface.hpp b/src/graphics/PlanetSurface.hpp new file mode 100644 index 0000000..2e73fbc --- /dev/null +++ b/src/graphics/PlanetSurface.hpp @@ -0,0 +1,45 @@ +#ifndef BLOBS_GRAPHICS_PLANETSURFACE_HPP_ +#define BLOBS_GRAPHICS_PLANETSURFACE_HPP_ + +#include "Program.hpp" + + +namespace blobs { +namespace graphics { + +class ArrayTexture; + +class PlanetSurface { + +public: + PlanetSurface(); + ~PlanetSurface(); + + PlanetSurface(const PlanetSurface &) = delete; + PlanetSurface &operator =(const PlanetSurface &) = delete; + + PlanetSurface(PlanetSurface &&) = delete; + PlanetSurface &operator =(PlanetSurface &&) = delete; + +public: + void Activate() noexcept; + + void SetMVP(const glm::mat4 &m, const glm::mat4 &v, const glm::mat4 &p) noexcept; + void SetNormal(const glm::vec3 &) noexcept; + void SetTexture(ArrayTexture &) noexcept; + +private: + Program prog; + + GLuint m_handle; + GLuint mv_handle; + GLuint mvp_handle; + GLuint sampler_handle; + GLuint normal_handle; + +}; + +} +} + +#endif diff --git a/src/graphics/Program.hpp b/src/graphics/Program.hpp new file mode 100644 index 0000000..7e0e93b --- /dev/null +++ b/src/graphics/Program.hpp @@ -0,0 +1,51 @@ +#ifndef BLOBS_GRAPHICS_PROGRAM_HPP_ +#define BLOBS_GRAPHICS_PROGRAM_HPP_ + +#include "glm.hpp" + +#include +#include +#include + + +namespace blobs { +namespace graphics { + +class Shader; + +class Program { + +public: + Program(); + ~Program(); + + Program(const Program &) = delete; + Program &operator =(const Program &) = delete; + + const Shader &LoadShader(GLenum type, const GLchar *src); + void Attach(Shader &) noexcept; + void Link() noexcept; + bool Linked() const noexcept; + void Log(std::ostream &) const; + + GLint AttributeLocation(const GLchar *name) const noexcept; + GLint UniformLocation(const GLchar *name) const noexcept; + + void Uniform(GLint, GLint) noexcept; + void Uniform(GLint, float) noexcept; + void Uniform(GLint, const glm::vec3 &) noexcept; + void Uniform(GLint, const glm::vec4 &) noexcept; + void Uniform(GLint, const glm::mat4 &) noexcept; + + void Use() const noexcept { glUseProgram(handle); } + +private: + GLuint handle; + std::list shaders; + +}; + +} +} + +#endif diff --git a/src/graphics/Shader.hpp b/src/graphics/Shader.hpp new file mode 100644 index 0000000..8c72da1 --- /dev/null +++ b/src/graphics/Shader.hpp @@ -0,0 +1,38 @@ +#ifndef BLOBS_GRAPHICS_SHADER_HPP_ +#define BLOBS_GRAPHICS_SHADER_HPP_ + +#include +#include + + +namespace blobs { +namespace graphics { + +class Shader { + +public: + explicit Shader(GLenum type); + ~Shader(); + + Shader(Shader &&) noexcept; + Shader &operator =(Shader &&) noexcept; + + Shader(const Shader &) = delete; + Shader &operator =(const Shader &) = delete; + + void Source(const GLchar *src) noexcept; + void Compile() noexcept; + bool Compiled() const noexcept; + void Log(std::ostream &) const; + + void AttachToProgram(GLuint id) const noexcept; + +private: + GLuint handle; + +}; + +} +} + +#endif diff --git a/src/graphics/SimpleVAO.hpp b/src/graphics/SimpleVAO.hpp new file mode 100644 index 0000000..963c015 --- /dev/null +++ b/src/graphics/SimpleVAO.hpp @@ -0,0 +1,88 @@ +#ifndef BLOBS_GRAPHICS_VAO_HPP_ +#define BLOBS_GRAPHICS_VAO_HPP_ + +#include "buffer.hpp" +#include "gl_traits.hpp" + +#include + + +namespace blobs { +namespace graphics { + +/// Simple vertex array object based on indexed draw calls with attributes +/// interleaved in a single buffer. +template +class SimpleVAO { + +public: + SimpleVAO() + : vao(0) + , buffers{0} { + glGenVertexArrays(1, &vao); + glGenBuffers(2, buffers); + } + ~SimpleVAO() noexcept { + glDeleteBuffers(2, buffers); + glDeleteVertexArrays(1, &vao); + } + + SimpleVAO(const SimpleVAO &) = delete; + SimpleVAO &operator =(const SimpleVAO &) = delete; + +public: + void Bind() const noexcept { + glBindVertexArray(vao); + } + void Unbind() const noexcept { + glBindVertexArray(0); + } + void BindAttributes() const noexcept { + glBindBuffer(GL_ARRAY_BUFFER, buffers[0]); + } + void BindElements() const noexcept { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffers[1]); + } + void EnableAttribute(GLuint index) noexcept { + glEnableVertexAttribArray(index); + } + void DisableAttribute(GLuint index) noexcept { + glDisableVertexAttribArray(index); + } + template + void AttributePointer(GLuint index, bool normalized, std::size_t offset) noexcept { + glVertexAttribPointer( + index, + gl_traits::size, + gl_traits::type, + normalized, + sizeof(Attributes), + reinterpret_cast(offset) + ); + } + void ReserveAttributes(std::size_t size, GLenum usage) noexcept { + glBufferData(GL_ARRAY_BUFFER, size * sizeof(Attributes), nullptr, usage); + } + MappedBuffer MapAttributes(GLenum access) { + return MappedBuffer(GL_ARRAY_BUFFER, access); + } + void ReserveElements(std::size_t size, GLenum usage) noexcept { + glBufferData(GL_ELEMENT_ARRAY_BUFFER, size * sizeof(Element), nullptr, usage); + } + MappedBuffer MapElements(GLenum access) { + return MappedBuffer(GL_ELEMENT_ARRAY_BUFFER, access); + } + void DrawTriangles(std::size_t size, std::size_t offset = 0) const noexcept { + glDrawElements(GL_TRIANGLES, size, gl_traits::type, ((Element *) nullptr) + offset); + } + +private: + GLuint vao; + GLuint buffers[2]; + +}; + +} +} + +#endif diff --git a/src/graphics/Texture.hpp b/src/graphics/Texture.hpp new file mode 100644 index 0000000..6ad7eb9 --- /dev/null +++ b/src/graphics/Texture.hpp @@ -0,0 +1,48 @@ +#ifndef BLOBS_GRAPHICS_TEXTURE_HPP_ +#define BLOBS_GRAPHICS_TEXTURE_HPP_ + +#include "TextureBase.hpp" + +#include + +struct SDL_Surface; + + +namespace blobs { +namespace graphics { + +struct Format; + +class Texture +: public TextureBase { + +public: + Texture(); + ~Texture(); + + Texture(Texture &&) noexcept; + Texture &operator =(Texture &&) noexcept; + + Texture(const Texture &) = delete; + Texture &operator =(const Texture &) = delete; + +public: + GLsizei Width() const noexcept { return width; } + GLsizei Height() const noexcept { return height; } + + void Data(const SDL_Surface &, bool pad2 = true) noexcept; + void Data(GLsizei w, GLsizei h, const Format &, GLvoid *data) noexcept; + + static void UnpackAlignment(GLint) noexcept; + static int UnpackAlignmentFromPitch(int) noexcept; + static void UnpackRowLength(GLint) noexcept; + +private: + GLsizei width, height; + +}; + +} +} + +#endif diff --git a/src/graphics/TextureBase.hpp b/src/graphics/TextureBase.hpp new file mode 100644 index 0000000..9129441 --- /dev/null +++ b/src/graphics/TextureBase.hpp @@ -0,0 +1,71 @@ +#ifndef BLOBS_GRAPHICS_TEXTUREBASE_HPP_ +#define BLOBS_GRAPHICS_TEXTUREBASE_HPP_ + +#include + + +namespace blobs { +namespace graphics { + +template +class TextureBase { + +public: + TextureBase(); + ~TextureBase(); + + TextureBase(TextureBase &&other) noexcept; + TextureBase &operator =(TextureBase &&) noexcept; + + TextureBase(const TextureBase &) = delete; + TextureBase &operator =(const TextureBase &) = delete; + +public: + void Bind(GLsizei which = 0) noexcept { + glBindTexture(TARGET, handle[which]); + } + + void FilterNearest() noexcept { + glTexParameteri(TARGET, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(TARGET, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } + void FilterLinear() noexcept { + glTexParameteri(TARGET, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(TARGET, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + void FilterTrilinear() noexcept { + glTexParameteri(TARGET, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameteri(TARGET, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glGenerateMipmap(TARGET); + } + + void WrapEdge() noexcept { + glTexParameteri(TARGET, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); + glTexParameteri(TARGET, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); + glTexParameteri(TARGET, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); + } + void WrapBorder() noexcept { + glTexParameteri(TARGET, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_BORDER); + glTexParameteri(TARGET, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(TARGET, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + } + void WrapRepeat() noexcept { + glTexParameteri(TARGET, GL_TEXTURE_WRAP_R, GL_REPEAT); + glTexParameteri(TARGET, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(TARGET, GL_TEXTURE_WRAP_T, GL_REPEAT); + } + void WrapMirror() noexcept { + glTexParameteri(TARGET, GL_TEXTURE_WRAP_R, GL_MIRRORED_REPEAT); + glTexParameteri(TARGET, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT); + glTexParameteri(TARGET, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT); + } + +private: + GLuint handle[COUNT]; + +}; + +} +} + +#endif diff --git a/src/graphics/Viewport.hpp b/src/graphics/Viewport.hpp new file mode 100644 index 0000000..78178a6 --- /dev/null +++ b/src/graphics/Viewport.hpp @@ -0,0 +1,40 @@ +#ifndef BLOBS_GRAPHICS_VIEWPORT_HPP_ +#define BLOBS_GRAPHICS_VIEWPORT_HPP_ + + +namespace blobs { +namespace graphics { + +class Viewport { + +public: + Viewport(int width, int height); + ~Viewport(); + + Viewport(const Viewport &) = delete; + Viewport &operator =(const Viewport &) = delete; + + Viewport(Viewport &&) = delete; + Viewport &operator =(Viewport &&) = delete; + +public: + int Width() const { + return width; + } + int Height() const { + return height; + } + void Resize(int w, int h); + + void Clear(); + +private: + int width; + int height; + +}; + +} +} + +#endif diff --git a/src/graphics/buffer.hpp b/src/graphics/buffer.hpp new file mode 100644 index 0000000..0d93920 --- /dev/null +++ b/src/graphics/buffer.hpp @@ -0,0 +1,62 @@ +#ifndef BLOBS_GRAPHICS_BUFFER_HPP_ +#define BLOBS_GRAPHICS_BUFFER_HPP_ + +#include "../app/error.hpp" + +#include +#include + + +namespace blobs { +namespace graphics { + +template +class MappedBuffer { + +public: + MappedBuffer(GLenum target, GLenum access) + : buf(reinterpret_cast(glMapBuffer(target, access))) + , target(target) { + if (!buf) { + throw app::GLError("failed to map buffer"); + } + } + MappedBuffer() + : buf(nullptr) + , target(0) { + } + ~MappedBuffer() noexcept { + if (buf) { + glUnmapBuffer(target); + } + } + + MappedBuffer(MappedBuffer &&other) noexcept + : buf(other.buf) + , target(other.target) { + other.buf = nullptr; + } + MappedBuffer &operator =(MappedBuffer &&other) noexcept { + std::swap(buf, other.buf); + std::swap(target, other.target); + } + + MappedBuffer(const MappedBuffer &) = delete; + MappedBuffer &operator =(const MappedBuffer &) = delete; + + explicit operator bool() const noexcept { return buf; } + +public: + T &operator [](std::size_t i) noexcept { return buf[i]; } + const T &operator [](std::size_t i) const noexcept { return buf[i]; } + +private: + T *buf; + GLenum target; + +}; + +} +} + +#endif diff --git a/src/graphics/const.hpp b/src/graphics/const.hpp new file mode 100644 index 0000000..a65dc10 --- /dev/null +++ b/src/graphics/const.hpp @@ -0,0 +1,20 @@ +#ifndef BLOBS_GRAPHICS_CONST_HPP_ +#define BLOBS_GRAPHICS_CONST_HPP_ + + +namespace blobs { +namespace graphics { + +constexpr double PI = 3.141592653589793238462643383279502884; +constexpr double PI_0p25 = PI * 0.25; +constexpr double PI_0p5 = PI * 0.5; +constexpr double PI_1p5 = PI * 1.5; +constexpr double PI_2p0 = PI * 2.0; + +constexpr double PI_inv = 1.0 / PI; +constexpr double PI_0p5_inv = 1.0 / PI_0p5; + +} +} + +#endif diff --git a/src/graphics/gl_traits.cpp b/src/graphics/gl_traits.cpp new file mode 100644 index 0000000..b99e62f --- /dev/null +++ b/src/graphics/gl_traits.cpp @@ -0,0 +1,32 @@ +#include "gl_traits.hpp" + + +namespace blobs { +namespace graphics { + +constexpr GLint gl_traits::size; +constexpr GLenum gl_traits::type; + +constexpr GLint gl_traits::size; +constexpr GLenum gl_traits::type; + +constexpr GLint gl_traits::size; +constexpr GLenum gl_traits::type; + +constexpr GLint gl_traits::size; +constexpr GLenum gl_traits::type; + +constexpr GLint gl_traits::size; +constexpr GLenum gl_traits::type; + +constexpr GLint gl_traits::size; +constexpr GLenum gl_traits::type; + +constexpr GLint gl_traits::size; +constexpr GLenum gl_traits::type; + +constexpr GLint gl_traits::size; +constexpr GLenum gl_traits::type; + +} +} diff --git a/src/graphics/gl_traits.hpp b/src/graphics/gl_traits.hpp new file mode 100644 index 0000000..a399893 --- /dev/null +++ b/src/graphics/gl_traits.hpp @@ -0,0 +1,122 @@ +#ifndef BLOBS_GRAPHICS_GL_TRAITS_HPP_ +#define BLOBS_GRAPHICS_GL_TRAITS_HPP_ + +#include "glm.hpp" + +#include + + +namespace blobs { +namespace graphics { + +template +struct gl_traits { + + /// number of components per generic attribute + /// must be 1, 2, 3, 4 + // static constexpr GLint size; + + /// component type + /// accepted values are: + /// GL_BYTE, GL_UNSIGNED_BYTE, + /// GL_SHORT, GL_UNSIGNED_SHORT, + /// GL_INT, GL_UNSIGNED_INT, + /// GL_HALF_FLOAT, GL_FLOAT, GL_DOUBLE, + /// GL_FIXED, GL_INT_2_10_10_10_REV, GL_UNSIGNED_INT_2_10_10_10_REV + // static constexpr GLenum type; + +}; + + +// basic types + +template<> struct gl_traits { + static constexpr GLint size = 1; + static constexpr GLenum type = GL_BYTE; +}; + +template<> struct gl_traits { + static constexpr GLint size = 1; + static constexpr GLenum type = GL_UNSIGNED_BYTE; +}; + +template<> struct gl_traits { + static constexpr GLint size = 1; + static constexpr GLenum type = GL_SHORT; +}; + +template<> struct gl_traits { + static constexpr GLint size = 1; + static constexpr GLenum type = GL_UNSIGNED_SHORT; +}; + +template<> struct gl_traits { + static constexpr GLint size = 1; + static constexpr GLenum type = GL_INT; +}; + +template<> struct gl_traits { + static constexpr GLint size = 1; + static constexpr GLenum type = GL_UNSIGNED_INT; +}; + +template<> struct gl_traits { + static constexpr GLint size = 1; + static constexpr GLenum type = GL_FLOAT; +}; + +template<> struct gl_traits { + static constexpr GLint size = 1; + static constexpr GLenum type = GL_DOUBLE; +}; + +// composite types + +template<> +template +struct gl_traits> { + static constexpr GLint size = 1; + static constexpr GLenum type = gl_traits::type; +}; +template +constexpr GLint gl_traits>::size; +template +constexpr GLenum gl_traits>::type; + +template<> +template +struct gl_traits> { + static constexpr GLint size = 2; + static constexpr GLenum type = gl_traits::type; +}; +template +constexpr GLint gl_traits>::size; +template +constexpr GLenum gl_traits>::type; + +template<> +template +struct gl_traits> { + static constexpr GLint size = 3; + static constexpr GLenum type = gl_traits::type; +}; +template +constexpr GLint gl_traits>::size; +template +constexpr GLenum gl_traits>::type; + +template<> +template +struct gl_traits> { + static constexpr GLint size = 4; + static constexpr GLenum type = gl_traits::type; +}; +template +constexpr GLint gl_traits>::size; +template +constexpr GLenum gl_traits>::type; + +} +} + +#endif diff --git a/src/graphics/glm.hpp b/src/graphics/glm.hpp new file mode 100644 index 0000000..1c39ba5 --- /dev/null +++ b/src/graphics/glm.hpp @@ -0,0 +1,23 @@ +#ifndef BLOBS_GRAPHICS_GLM_HPP_ +#define BLOBS_GRAPHICS_GLM_HPP_ + +#ifndef GLM_FORCE_RADIANS +# define GLM_FORCE_RADIANS 1 +#endif + +#include + +// GLM moved tvec[1234] from glm::detail to glm in 0.9.6 + +#if GLM_VERSION < 96 + +namespace glm { + using tvec1 = detail::tvec1; + using tvec2 = detail::tvec2; + using tvec3 = detail::tvec3; + using tvec4 = detail::tvec4; +} + +#endif + +#endif diff --git a/src/graphics/render.cpp b/src/graphics/render.cpp new file mode 100644 index 0000000..5a0e492 --- /dev/null +++ b/src/graphics/render.cpp @@ -0,0 +1,469 @@ +#include "ArrayTexture.hpp" +#include "CubeMap.hpp" +#include "Font.hpp" +#include "Format.hpp" +#include "Texture.hpp" +#include "TextureBase.hpp" +#include "Viewport.hpp" + +#include "../app/error.hpp" + +#include +#include +#include +#include +#include + + +namespace blobs { +namespace graphics { + +Font::Font(const char *src, int size, long index) +: handle(TTF_OpenFontIndex(src, size, index)) { + if (!handle) { + throw std::runtime_error(TTF_GetError()); + } +} + +Font::~Font() { + if (handle) { + TTF_CloseFont(handle); + } +} + +Font::Font(Font &&other) noexcept +: handle(other.handle) { + other.handle = nullptr; +} + +Font &Font::operator =(Font &&other) noexcept { + std::swap(handle, other.handle); + return *this; +} + + +int Font::Style() const noexcept { + return TTF_GetFontStyle(handle); +} + +void Font::Style(int s) const noexcept { + TTF_SetFontStyle(handle, s); +} + +int Font::Outline() const noexcept { + return TTF_GetFontOutline(handle); +} + +void Font::Outline(int px) noexcept { + TTF_SetFontOutline(handle, px); +} + + +int Font::Hinting() const noexcept { + return TTF_GetFontHinting(handle); +} + +void Font::Hinting(int h) const noexcept { + TTF_SetFontHinting(handle, h); +} + +bool Font::Kerning() const noexcept { + return TTF_GetFontKerning(handle); +} + +void Font::Kerning(bool b) noexcept { + TTF_SetFontKerning(handle, b); +} + + +int Font::Height() const noexcept { + return TTF_FontHeight(handle); +} + +int Font::Ascent() const noexcept { + return TTF_FontAscent(handle); +} + +int Font::Descent() const noexcept { + return TTF_FontDescent(handle); +} + +int Font::LineSkip() const noexcept { + return TTF_FontLineSkip(handle); +} + + +const char *Font::FamilyName() const noexcept { + return TTF_FontFaceFamilyName(handle); +} + +const char *Font::StyleName() const noexcept { + return TTF_FontFaceStyleName(handle); +} + + +bool Font::HasGlyph(Uint16 c) const noexcept { + return TTF_GlyphIsProvided(handle, c); +} + + +glm::ivec2 Font::TextSize(const char *text) const { + glm::ivec2 size; + if (TTF_SizeUTF8(handle, text, &size.x, &size.y) != 0) { + throw std::runtime_error(TTF_GetError()); + } + return size; +} + +Texture Font::Render(const char *text) const { + Texture tex; + Render(text, tex); + return tex; +} + +void Font::Render(const char *text, Texture &tex) const { + SDL_Surface *srf = TTF_RenderUTF8_Blended(handle, text, { 0xFF, 0xFF, 0xFF, 0xFF }); + if (!srf) { + throw std::runtime_error(TTF_GetError()); + } + tex.Bind(); + tex.Data(*srf, false); + tex.FilterLinear(); + SDL_FreeSurface(srf); +} + +Format::Format() noexcept +: format(GL_BGRA) +, type(GL_UNSIGNED_INT_8_8_8_8_REV) +, internal(GL_RGBA8) { + sdl_format.format = SDL_PIXELFORMAT_ARGB8888; + sdl_format.palette = nullptr; + sdl_format.BitsPerPixel = 32; + sdl_format.BytesPerPixel = 4; + sdl_format.Rmask = 0x00FF0000; + sdl_format.Gmask = 0x0000FF00; + sdl_format.Bmask = 0x000000FF; + sdl_format.Amask = 0xFF000000; + sdl_format.Rloss = 0; + sdl_format.Gloss = 0; + sdl_format.Bloss = 0; + sdl_format.Aloss = 0; + sdl_format.Rshift = 16; + sdl_format.Gshift = 8; + sdl_format.Bshift = 0; + sdl_format.Ashift = 24; + sdl_format.refcount = 1; + sdl_format.next = nullptr; +} + +Format::Format(const SDL_PixelFormat &fmt) noexcept +: sdl_format(fmt) { + if (fmt.BytesPerPixel == 4) { + if (fmt.Amask == 0xFF) { + if (fmt.Rmask == 0xFF00) { + format = GL_BGRA; + } else { + format = GL_RGBA; + } + type = GL_UNSIGNED_INT_8_8_8_8; + } else { + if (fmt.Rmask == 0xFF) { + format = GL_RGBA; + } else { + format = GL_BGRA; + } + type = GL_UNSIGNED_INT_8_8_8_8_REV; + } + internal = GL_RGBA8; + } else { + if (fmt.Rmask == 0xFF) { + format = GL_RGB; + } else { + format = GL_BGR; + } + type = GL_UNSIGNED_BYTE; + internal = GL_RGB8; + } +} + +bool Format::Compatible(const Format &other) const noexcept { + return format == other.format && type == other.type && internal == other.internal; +} + + +template +TextureBase::TextureBase() { + glGenTextures(COUNT, handle); +} + +template +TextureBase::~TextureBase() { + glDeleteTextures(COUNT, handle); +} + +template +TextureBase::TextureBase(TextureBase &&other) noexcept { + std::memcpy(handle, other.handle, sizeof(handle)); + std::memset(other.handle, 0, sizeof(handle)); +} + +template +TextureBase &TextureBase::operator =(TextureBase &&other) noexcept { + std::swap(handle, other.handle); + return *this; +} + + +Texture::Texture() +: TextureBase() +, width(0) +, height(0) { + +} + +Texture::~Texture() { + +} + +Texture::Texture(Texture &&other) noexcept +: TextureBase(std::move(other)) { + width = other.width; + height = other.height; +} + +Texture &Texture::operator =(Texture &&other) noexcept { + TextureBase::operator =(std::move(other)); + width = other.width; + height = other.height; + return *this; +} + + +namespace { + bool ispow2(unsigned int i) { + // don't care about i == 0 here + return !(i & (i - 1)); + } +} + +void Texture::Data(const SDL_Surface &srf, bool pad2) noexcept { + Format format(*srf.format); + + if (!pad2 || (ispow2(srf.w) && ispow2(srf.h))) { + int align = UnpackAlignmentFromPitch(srf.pitch); + + int pitch = (srf.w * srf.format->BytesPerPixel + align - 1) / align * align; + if (srf.pitch - pitch >= align) { + UnpackRowLength(srf.pitch / srf.format->BytesPerPixel); + } else { + UnpackRowLength(0); + } + + Data(srf.w, srf.h, format, srf.pixels); + + UnpackRowLength(0); + } else if (srf.w > (1 << 30) || srf.h > (1 << 30)) { + // That's at least one gigapixel in either or both dimensions. + // If this is not an error, that's an insanely large or high + // resolution texture. +#ifndef NDEBUG + std::cerr << "texture size exceeds 2^30, aborting data import" << std::endl; +#endif + } else { + GLsizei width = 1, height = 1; + while (width < srf.w) { + width <<= 1; + } + while (height < srf.h) { + height <<= 1; + } + size_t pitch = width * srf.format->BytesPerPixel; + size_t size = pitch * height; + size_t row_pad = pitch - srf.pitch; + std::unique_ptr data(new unsigned char[size]); + unsigned char *src = reinterpret_cast(srf.pixels); + unsigned char *dst = data.get(); + for (int row = 0; row < srf.h; ++row) { + std::memcpy(dst, src, srf.pitch); + src += srf.pitch; + dst += srf.pitch; + std::memset(dst, 0, row_pad); + dst += row_pad; + } + std::memset(dst, 0, (height - srf.h) * pitch); + UnpackAlignmentFromPitch(pitch); + Data(width, height, format, data.get()); + } + + UnpackAlignment(4); +} + +void Texture::Data(GLsizei w, GLsizei h, const Format &format, GLvoid *data) noexcept { + glTexImage2D( + GL_TEXTURE_2D, + 0, format.internal, + w, h, + 0, + format.format, format.type, + data + ); + width = w; + height = h; +} + + +void Texture::UnpackAlignment(GLint i) noexcept { + glPixelStorei(GL_UNPACK_ALIGNMENT, i); +} + +int Texture::UnpackAlignmentFromPitch(int pitch) noexcept { + int align = 8; + while (pitch % align) { + align >>= 1; + } + UnpackAlignment(align); + return align; +} + +void Texture::UnpackRowLength(GLint i) noexcept { + glPixelStorei(GL_UNPACK_ROW_LENGTH, i); +} + + +ArrayTexture::ArrayTexture() +: TextureBase() +, width(0) +, height(0) +, depth(0) { + +} + +ArrayTexture::~ArrayTexture() { + +} + +ArrayTexture::ArrayTexture(ArrayTexture &&other) noexcept +: TextureBase(std::move(other)) { + width = other.width; + height = other.height; + depth = other.depth; +} + +ArrayTexture &ArrayTexture::operator =(ArrayTexture &&other) noexcept { + TextureBase::operator =(std::move(other)); + width = other.width; + height = other.height; + depth = other.depth; + return *this; +} + + +void ArrayTexture::Reserve(GLsizei w, GLsizei h, GLsizei d, const Format &f) noexcept { + glTexStorage3D( + GL_TEXTURE_2D_ARRAY, // which + 1, // mipmap count + f.internal, // format + w, h, // dimensions + d // layer count + ); + width = w; + height = h; + depth = d; + format = f; +} + +void ArrayTexture::Data(GLsizei l, const SDL_Surface &srf) { + Format fmt(*srf.format); + if (format.Compatible(fmt)) { + Data(l, fmt, srf.pixels); + } else { + SDL_Surface *converted = SDL_ConvertSurface( + const_cast(&srf), + &format.sdl_format, + 0 + ); + if (!converted) { + throw app::SDLError("SDL_ConvertSurface"); + } + Format new_fmt(*converted->format); + if (!format.Compatible(new_fmt)) { + SDL_FreeSurface(converted); + throw std::runtime_error("unable to convert texture input"); + } + Data(l, new_fmt, converted->pixels); + SDL_FreeSurface(converted); + } +} + +void ArrayTexture::Data(GLsizei l, const Format &f, GLvoid *data) noexcept { + glTexSubImage3D( + GL_TEXTURE_2D_ARRAY, // which + 0, // mipmap lavel + 0, 0, // dest X and Y offset + l, // layer offset + width, height, + 1, // layer count + f.format, f.type, + data + ); +} + + +CubeMap::CubeMap() +: TextureBase() { + +} + +CubeMap::~CubeMap() { + +} + +CubeMap::CubeMap(CubeMap &&other) noexcept +: TextureBase(std::move(other)) { + +} + +CubeMap &CubeMap::operator =(CubeMap &&other) noexcept { + TextureBase::operator =(std::move(other)); + return *this; +} + + +void CubeMap::Data(Face f, const SDL_Surface &srf) { + Format format; + Format fmt(*srf.format); + if (format.Compatible(fmt)) { + Data(f, srf.w, srf.h, fmt, srf.pixels); + } else { + SDL_Surface *converted = SDL_ConvertSurface( + const_cast(&srf), + &format.sdl_format, + 0 + ); + if (!converted) { + throw app::SDLError("SDL_ConvertSurface"); + } + Format new_fmt(*converted->format); + if (!format.Compatible(new_fmt)) { + SDL_FreeSurface(converted); + throw std::runtime_error("unable to convert texture input"); + } + Data(f, converted->w, converted->h, new_fmt, converted->pixels); + SDL_FreeSurface(converted); + } +} + +void CubeMap::Data(Face face, GLsizei w, GLsizei h, const Format &f, GLvoid *data) noexcept { + glTexImage2D( + face, // which + 0, // mipmap level + f.internal, // internal format + w, h, // size + 0, // border + f.format, f.type, // pixel format + data // pixel data + ); +} + +} +} diff --git a/src/graphics/shader.cpp b/src/graphics/shader.cpp new file mode 100644 index 0000000..ca74766 --- /dev/null +++ b/src/graphics/shader.cpp @@ -0,0 +1,251 @@ +#include "PlanetSurface.hpp" +#include "Program.hpp" +#include "Shader.hpp" + +#include "ArrayTexture.hpp" +#include "../app/init.hpp" + +#include +#include +#include +#include +#include +#include +#include + + +namespace { + +void gl_error(std::string msg) { + const GLubyte *errBegin = gluErrorString(glGetError()); + if (errBegin && *errBegin != '\0') { + const GLubyte *errEnd = errBegin; + while (*errEnd != '\0') { + ++errEnd; + } + msg += ": "; + msg.append(errBegin, errEnd); + } + throw std::runtime_error(msg); +} + +} + +namespace blobs { +namespace graphics { + +Shader::Shader(GLenum type) +: handle(glCreateShader(type)) { + if (handle == 0) { + gl_error("glCreateShader"); + } +} + +Shader::~Shader() { + if (handle != 0) { + glDeleteShader(handle); + } +} + +Shader::Shader(Shader &&other) noexcept +: handle(other.handle) { + other.handle = 0; +} + +Shader &Shader::operator =(Shader &&other) noexcept { + std::swap(handle, other.handle); + return *this; +} + + +void Shader::Source(const GLchar *src) noexcept { + const GLchar* src_arr[] = { src }; + glShaderSource(handle, 1, src_arr, nullptr); +} + +void Shader::Compile() noexcept { + glCompileShader(handle); +} + +bool Shader::Compiled() const noexcept { + GLint compiled = GL_FALSE; + glGetShaderiv(handle, GL_COMPILE_STATUS, &compiled); + return compiled == GL_TRUE; +} + +void Shader::Log(std::ostream &out) const { + int log_len = 0, max_len = 0; + glGetShaderiv(handle, GL_INFO_LOG_LENGTH, &max_len); + std::unique_ptr log(new char[max_len]); + glGetShaderInfoLog(handle, max_len, &log_len, log.get()); + out.write(log.get(), log_len); +} + + +void Shader::AttachToProgram(GLuint id) const noexcept { + glAttachShader(id, handle); +} + + +Program::Program() +: handle(glCreateProgram()) { + if (handle == 0) { + gl_error("glCreateProgram"); + } +} + +Program::~Program() { + if (handle != 0) { + glDeleteProgram(handle); + } +} + + +const Shader &Program::LoadShader(GLenum type, const GLchar *src) { + shaders.emplace_back(type); + Shader &shader = shaders.back(); + shader.Source(src); + shader.Compile(); + if (!shader.Compiled()) { + shader.Log(std::cerr); + throw std::runtime_error("compile shader"); + } + Attach(shader); + return shader; +} + +void Program::Attach(Shader &shader) noexcept { + shader.AttachToProgram(handle); +} + +void Program::Link() noexcept { + glLinkProgram(handle); +} + +bool Program::Linked() const noexcept { + GLint linked = GL_FALSE; + glGetProgramiv(handle, GL_LINK_STATUS, &linked); + return linked == GL_TRUE; +} + +void Program::Log(std::ostream &out) const { + int log_len = 0, max_len = 0; + glGetProgramiv(handle, GL_INFO_LOG_LENGTH, &max_len); + std::unique_ptr log(new char[max_len]); + glGetProgramInfoLog(handle, max_len, &log_len, log.get()); + out.write(log.get(), log_len); +} + + +GLint Program::AttributeLocation(const GLchar *name) const noexcept { + return glGetAttribLocation(handle, name); +} + +GLint Program::UniformLocation(const GLchar *name) const noexcept { + return glGetUniformLocation(handle, name); +} + + +void Program::Uniform(GLint loc, GLint val) noexcept { + glUniform1i(loc, val); +} + +void Program::Uniform(GLint loc, float val) noexcept { + glUniform1f(loc, val); +} + +void Program::Uniform(GLint loc, const glm::vec3 &val) noexcept { + glUniform3fv(loc, 1, glm::value_ptr(val)); +} + +void Program::Uniform(GLint loc, const glm::vec4 &val) noexcept { + glUniform4fv(loc, 1, glm::value_ptr(val)); +} + +void Program::Uniform(GLint loc, const glm::mat4 &val) noexcept { + glUniformMatrix4fv(loc, 1, GL_FALSE, glm::value_ptr(val)); +} + + +PlanetSurface::PlanetSurface() +: prog() { + prog.LoadShader( + GL_VERTEX_SHADER, + "#version 330 core\n" + + "layout(location = 0) in vec3 vtx_position;\n" + "layout(location = 1) in vec3 vtx_tex_uv;\n" + + "uniform mat4 M;\n" + "uniform mat4 MV;\n" + "uniform mat4 MVP;\n" + + "out vec3 frag_tex_uv;\n" + "out vec3 vtx_viewspace;\n" + + "void main() {\n" + "gl_Position = MVP * vec4(vtx_position, 1);\n" + "vtx_viewspace = (MV * vec4(vtx_position, 1)).xyz;\n" + "frag_tex_uv = vtx_tex_uv;\n" + "}\n" + ); + prog.LoadShader( + GL_FRAGMENT_SHADER, + "#version 330 core\n" + + "in vec3 vtx_viewspace;\n" + "in vec3 frag_tex_uv;\n" + + "uniform sampler2DArray tex_sampler;\n" + "uniform vec3 normal;\n" + + "out vec3 color;\n" + + "void main() {\n" + "vec3 tex_color = texture(tex_sampler, frag_tex_uv).rgb;\n" + // TODO: lighting + "color = tex_color;\n" + "}\n" + ); + prog.Link(); + if (!prog.Linked()) { + prog.Log(std::cerr); + throw std::runtime_error("link program"); + } + m_handle = prog.UniformLocation("M"); + mv_handle = prog.UniformLocation("MV"); + mvp_handle = prog.UniformLocation("MVP"); + sampler_handle = prog.UniformLocation("tex_sampler"); + normal_handle = prog.UniformLocation("normal"); +} + +PlanetSurface::~PlanetSurface() { +} + +void PlanetSurface::Activate() noexcept { + prog.Use(); + glEnable(GL_DEPTH_TEST); + glDepthFunc(GL_LESS); + glDisable(GL_CULL_FACE); + glDisable(GL_BLEND); +} + +void PlanetSurface::SetMVP(const glm::mat4 &m, const glm::mat4 &v, const glm::mat4 &p) noexcept { + prog.Uniform(m_handle, m); + glm::mat4 mv(v * m); + prog.Uniform(mv_handle, mv); + prog.Uniform(mvp_handle, p * mv); +} + +void PlanetSurface::SetNormal(const glm::vec3 &n) noexcept { + prog.Uniform(normal_handle, n); +} + +void PlanetSurface::SetTexture(ArrayTexture &tex) noexcept { + glActiveTexture(GL_TEXTURE0); + tex.Bind(); + prog.Uniform(sampler_handle, GLint(0)); +} + +} +} diff --git a/src/graphics/viewport.cpp b/src/graphics/viewport.cpp new file mode 100644 index 0000000..499a227 --- /dev/null +++ b/src/graphics/viewport.cpp @@ -0,0 +1,77 @@ +#include "Camera.hpp" +#include "Viewport.hpp" + +#include "const.hpp" + +#include +#include + + +namespace blobs { +namespace graphics { + +Camera::Camera() noexcept +: fov(PI_0p25) +, aspect(1.0f) +, near(0.1f) +, far(256.0f) +, projection(glm::perspective(fov, aspect, near, far)) +, view(1.0f) { + +} + +Camera::~Camera() noexcept { +} + +void Camera::FOV(float f) noexcept { + fov = f; + UpdateProjection(); +} + +void Camera::Aspect(float r) noexcept { + aspect = r; + UpdateProjection(); +} + +void Camera::Aspect(float w, float h) noexcept { + Aspect(w / h); +} + +void Camera::Clip(float n, float f) noexcept { + near = n; + far = f; + UpdateProjection(); +} + +void Camera::View(const glm::mat4 &v) noexcept { + view = v; +} + +void Camera::UpdateProjection() noexcept { + projection = glm::perspective(fov, aspect, near, far); +} + + +Viewport::Viewport(int w, int h) +: width(w) +, height(h) { + Resize(w, h); + glClearColor(0.0, 0.0, 0.0, 1.0); +} + +Viewport::~Viewport() { +} + + +void Viewport::Resize(int w, int h) { + width = w; + height = h; + glViewport(0, 0, w, h); +} + +void Viewport::Clear() { + glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); +} + +} +} diff --git a/src/world/Body.hpp b/src/world/Body.hpp new file mode 100644 index 0000000..e3fd8de --- /dev/null +++ b/src/world/Body.hpp @@ -0,0 +1,52 @@ +#ifndef BLOBS_WORLD_BODY_HPP_ +#define BLOBS_WORLD_BODY_HPP_ + +#include + + +namespace blobs { +namespace app { + class Assets; +} +namespace graphics { + class Viewport; +} +namespace world { + +class Body { + +public: + Body(); + ~Body(); + + Body(const Body &) = delete; + Body &operator =(const Body &) = delete; + + Body(Body &&) = delete; + Body &operator =(Body &&) = delete; + +public: + bool HasParent() const { return parent; } + Body &Parent() { return *parent; } + const Body &Parent() const { return *parent; } + void SetParent(Body &); + void UnsetParent(); + + virtual void Draw(app::Assets &, graphics::Viewport &) { } + +private: + void AddChild(Body &); + void RemoveChild(Body &); + +private: + Body *parent; + std::vector children; + double mass; + double radius; + +}; + +} +} + +#endif diff --git a/src/world/Planet.hpp b/src/world/Planet.hpp index 6106ccf..0c468f5 100644 --- a/src/world/Planet.hpp +++ b/src/world/Planet.hpp @@ -1,10 +1,15 @@ #ifndef BLOBS_WORLD_PLANET_HPP_ #define BLOBS_WORLD_PLANET_HPP_ +#include "Body.hpp" + #include "Tile.hpp" +#include "../graphics/glm.hpp" +#include "../graphics/SimpleVAO.hpp" #include #include +#include namespace blobs { @@ -12,20 +17,21 @@ namespace world { struct Tile; -/// A planet has six surfaces, numbered 0 to 5, each with tiles from -/// +radius to -radius. -class Planet { +/// A planet has six surfaces, numbered 0 to 5, each filled with +/// sidelength² tiles. +class Planet +: public Body { public: - explicit Planet(int radius); + explicit Planet(int sidelength); ~Planet(); - Planet(Planet &&); - Planet &operator =(Planet &&); - Planet(const Planet &) = delete; Planet &operator =(const Planet &) = delete; + Planet(Planet &&) = delete; + Planet &operator =(Planet &&) = delete; + public: /// Get the tile at given surface and coordinates. Tile &TileAt(int surface, int x, int y) { @@ -38,35 +44,36 @@ public: /// Convert coordinates into a tile index. int IndexOf(int surface, int x, int y) const { assert(0 <= surface && surface <= 5); - assert(-radius <= x && x <= radius); - assert(-radius <= y && y <= radius); - return surface * SurfaceArea() + ToOffset(y) * SideLength() + ToOffset(x); - } - /// Convert coordinate into offset - int ToOffset(int c) const { - return c + radius; - } - /// The "radius" of the planet. - int Radius() const { - return radius; + assert(0 <= x && x <= sidelength); + assert(0 <= y && y <= sidelength); + return surface * TilesPerSurface() + y * SideLength() + x; } /// The length of the side of each surface. int SideLength() const { - return 2 * radius + 1; + return sidelength; } - /// The area (or number of tiles) of one surface - int SurfaceArea() const { + /// The number of tiles of one surface. + int TilesPerSurface() const { return SideLength() * SideLength(); } - /// Total area of all surfaces combined. - int TotalArea() const { - return 6 * SurfaceArea(); + /// Total number of tiles of all surfaces combined. + int TilesTotal() const { + return 6 * TilesPerSurface(); } + void BuildVAOs(); + void Draw(app::Assets &, graphics::Viewport &) override; + private: - int radius; + int sidelength; std::unique_ptr tiles; + struct Attributes { + glm::vec3 position; + glm::vec3 tex_coord; + }; + graphics::SimpleVAO vao; + }; void GenerateTest(Planet &); diff --git a/src/world/Simulation.hpp b/src/world/Simulation.hpp new file mode 100644 index 0000000..2acf549 --- /dev/null +++ b/src/world/Simulation.hpp @@ -0,0 +1,36 @@ +#ifndef BLOBS_WORLD_SIMULATION_HPP_ +#define BLOBS_WORLD_SIMULATION_HPP_ + + +namespace blobs { +namespace world { + +class Body; + +class Simulation { + +public: + explicit Simulation(Body &root); + ~Simulation(); + + Simulation(const Simulation &) = delete; + Simulation &operator =(const Simulation &) = delete; + + Simulation(Simulation &&) = delete; + Simulation &operator =(Simulation &&) = delete; + +public: + void Tick(); + + Body &Root() { return root; } + const Body &Root() const { return root; } + +private: + Body &root; + +}; + +} +} + +#endif diff --git a/src/world/Sun.hpp b/src/world/Sun.hpp new file mode 100644 index 0000000..a47c039 --- /dev/null +++ b/src/world/Sun.hpp @@ -0,0 +1,28 @@ +#ifndef BLOBS_WORLD_SUN_HPP_ +#define BLOBS_WORLD_SUN_HPP_ + +#include "Body.hpp" + + +namespace blobs { +namespace world { + +class Sun +: public Body { + +public: + Sun(); + ~Sun(); + + Sun(const Sun &) = delete; + Sun &operator =(const Sun &) = delete; + + Sun(Sun &&) = delete; + Sun &operator =(Sun &&) = delete; + +}; + +} +} + +#endif diff --git a/src/world/sim.cpp b/src/world/sim.cpp new file mode 100644 index 0000000..0e0b466 --- /dev/null +++ b/src/world/sim.cpp @@ -0,0 +1,19 @@ +#include "Simulation.hpp" + + +namespace blobs { +namespace world { + +Simulation::Simulation(Body &r) +: root(r) { +} + +Simulation::~Simulation() { +} + + +void Simulation::Tick() { +} + +} +} diff --git a/src/world/world.cpp b/src/world/world.cpp index cd18fb2..7029667 100644 --- a/src/world/world.cpp +++ b/src/world/world.cpp @@ -1,41 +1,165 @@ +#include "Body.hpp" #include "Planet.hpp" +#include "Sun.hpp" #include "Tile.hpp" +#include "../app/Assets.hpp" +#include "../graphics/Viewport.hpp" + #include namespace blobs { namespace world { -Planet::Planet(int radius) -: radius(radius) -, tiles(new Tile[TotalArea()]) { +Body::Body() +: parent(nullptr) +, children() +, mass(1.0) +, radius(1.0) { +} + +Body::~Body() { +} + +void Body::SetParent(Body &p) { + if (HasParent()) { + UnsetParent(); + } + parent = &p; + parent->AddChild(*this); +} + +void Body::UnsetParent() { + if (!HasParent()) return; + parent->RemoveChild(*this); + parent = nullptr; +} + +void Body::AddChild(Body &c) { + children.push_back(&c); +} + +void Body::RemoveChild(Body &c) { + auto entry = std::find(children.begin(), children.end(), &c); + if (entry != children.end()) { + children.erase(entry); + } +} + +Planet::Planet(int sidelength) +: Body() +, sidelength(sidelength) +, tiles(new Tile[TilesTotal()]) +, vao() { } Planet::~Planet() { } -Planet::Planet(Planet &&other) -: radius(other.radius) -, tiles(other.tiles.release()) { +void Planet::BuildVAOs() { + vao.Bind(); + vao.BindAttributes(); + vao.EnableAttribute(0); + vao.EnableAttribute(1); + vao.AttributePointer(0, false, offsetof(Attributes, position)); + vao.AttributePointer(1, false, offsetof(Attributes, tex_coord)); + vao.ReserveAttributes(TilesTotal() * 4, GL_STATIC_DRAW); + { + auto attrib = vao.MapAttributes(GL_WRITE_ONLY); + float offset = sidelength * 0.5f; + + for (int index = 0, surface = 0; surface < 6; ++surface) { + for (int y = 0; y < sidelength; ++y) { + for (int x = 0; x < sidelength; ++x, ++index) { + float tex = TileAt(surface, x, y).type; + attrib[4 * index + 0].position[(surface + 0) % 3] = x + 0 - offset; + attrib[4 * index + 0].position[(surface + 1) % 3] = y + 0 - offset; + attrib[4 * index + 0].position[(surface + 2) % 3] = surface < 3 ? offset : -offset; + attrib[4 * index + 0].tex_coord[0] = 0.0f; + attrib[4 * index + 0].tex_coord[1] = 0.0f; + attrib[4 * index + 0].tex_coord[2] = tex; + + attrib[4 * index + 1].position[(surface + 0) % 3] = x + 0 - offset; + attrib[4 * index + 1].position[(surface + 1) % 3] = y + 1 - offset; + attrib[4 * index + 1].position[(surface + 2) % 3] = surface < 3 ? offset : -offset; + attrib[4 * index + 1].tex_coord[0] = 0.0f; + attrib[4 * index + 1].tex_coord[1] = 1.0f; + attrib[4 * index + 1].tex_coord[2] = tex; + + attrib[4 * index + 2].position[(surface + 0) % 3] = x + 1 - offset; + attrib[4 * index + 2].position[(surface + 1) % 3] = y + 0 - offset; + attrib[4 * index + 2].position[(surface + 2) % 3] = surface < 3 ? offset : -offset; + attrib[4 * index + 2].tex_coord[0] = 1.0f; + attrib[4 * index + 2].tex_coord[1] = 0.0f; + attrib[4 * index + 2].tex_coord[2] = tex; + + attrib[4 * index + 3].position[(surface + 0) % 3] = x + 1 - offset; + attrib[4 * index + 3].position[(surface + 1) % 3] = y + 1 - offset; + attrib[4 * index + 3].position[(surface + 2) % 3] = surface < 3 ? offset : -offset; + attrib[4 * index + 3].tex_coord[0] = 1.0f; + attrib[4 * index + 3].tex_coord[1] = 1.0f; + attrib[4 * index + 3].tex_coord[2] = tex; + } + } + } + } + vao.BindElements(); + vao.ReserveElements(TilesTotal() * 6, GL_STATIC_DRAW); + { + auto element = vao.MapElements(GL_WRITE_ONLY); + for (int index = 0, surface = 0; surface < 6; ++surface) { + for (int y = 0; y < sidelength; ++y) { + for (int x = 0; x < sidelength; ++x, ++index) { + element[6 * index + 0] = 4 * index + 0; + element[6 * index + 1] = 4 * index + 1; + element[6 * index + 2] = 4 * index + 2; + element[6 * index + 3] = 4 * index + 2; + element[6 * index + 4] = 4 * index + 1; + element[6 * index + 5] = 4 * index + 3; + } + } + } + } + vao.Unbind(); } -Planet &Planet::operator =(Planet &&other) { - radius = other.radius; - std::swap(tiles, other.tiles); - return *this; +void Planet::Draw(app::Assets &assets, graphics::Viewport &viewport) { + vao.Bind(); + // TODO: premultiply normal with model matrix (i.e. just take it from M) + assets.shaders.planet_surface.SetNormal(glm::vec3(0.0f, 0.0f, 1.0f)); + vao.DrawTriangles(TilesTotal() * 4, TilesTotal() * 4 * 0); + assets.shaders.planet_surface.SetNormal(glm::vec3(1.0f, 0.0f, 0.0f)); + vao.DrawTriangles(TilesTotal() * 4, TilesTotal() * 4 * 1); + assets.shaders.planet_surface.SetNormal(glm::vec3(0.0f, 1.0f, 0.0f)); + vao.DrawTriangles(TilesTotal() * 4, TilesTotal() * 4 * 2); + assets.shaders.planet_surface.SetNormal(glm::vec3(0.0f, 0.0f, -1.0f)); + vao.DrawTriangles(TilesTotal() * 4, TilesTotal() * 4 * 3); + assets.shaders.planet_surface.SetNormal(glm::vec3(-1.0f, 0.0f, 0.0f)); + vao.DrawTriangles(TilesTotal() * 4, TilesTotal() * 4 * 4); + assets.shaders.planet_surface.SetNormal(glm::vec3(0.0f, -1.0f, 0.0f)); + vao.DrawTriangles(TilesTotal() * 4, TilesTotal() * 4 * 5); } void GenerateTest(Planet &p) { for (int surface = 0; surface <= 5; ++surface) { - for (int y = -p.Radius(); y <= p.Radius(); ++y) { - for (int x = -p.Radius(); x <= p.Radius(); ++x) { - p.TileAt(surface, x, y).type = (x == 0) + (y == 0); + for (int y = 0; y < p.SideLength(); ++y) { + for (int x = 0; x < p.SideLength(); ++x) { + p.TileAt(surface, x, y).type = (x == p.SideLength()/2) + (y == p.SideLength()/2); } } } + p.BuildVAOs(); +} + + +Sun::Sun() +: Body() { +} + +Sun::~Sun() { } }