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;
void HandleEvents();
private:
+ Window &window;
+ graphics::Viewport &viewport;
std::stack<State *> states;
};
--- /dev/null
+#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
--- /dev/null
+#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
namespace blobs {
+namespace graphics {
+ class Viewport;
+}
namespace app {
class Application;
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() { }
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;
+ }
};
#include "Application.hpp"
+#include "Assets.hpp"
#include "State.hpp"
+#include "init.hpp"
+#include "../graphics/Viewport.hpp"
+
#include <SDL.h>
namespace blobs {
namespace app {
-Application::Application()
-: states() {
+Application::Application(Window &win, graphics::Viewport &vp)
+: window(win)
+, viewport(vp)
+, states() {
}
Application::~Application() {
void Application::PushState(State *s) {
+ s->app = this;
if (!states.empty()) {
states.top()->OnPause();
}
}
State *Application::SwitchState(State *s_new) {
+ s_new->app = this;
State *s_old = states.top();
states.top() = s_new;
--s_old->ref_count;
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;
}
}
OnBlur();
break;
case SDL_WINDOWEVENT_RESIZED:
- //env.viewport.Resize(event.data1, event.data2);
- OnResize();
+ OnResize(event.data1, event.data2);
break;
default:
break;
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() {
}
}
--- /dev/null
+#include "error.hpp"
+
+#include <alut.h>
+#include <SDL.h>
+#include <SDL_net.h>
+#include <GL/glew.h>
+
+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<const char *>(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)) {
+}
+
+}
+}
--- /dev/null
+#ifndef BLOBS_APP_ERROR_HPP_
+#define BLOBS_APP_ERROR_HPP_
+
+#include <al.h>
+#include <stdexcept>
+
+
+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
#include "init.hpp"
+#include "error.hpp"
+
#include <algorithm>
#include <iostream>
#include <alut.h>
#include <GL/glew.h>
-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)");
}
-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) {
}
-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) {
}
#ifndef BLOBS_APP_INIT_HPP_
#define BLOBS_APP_INIT_HPP_
+#include "../graphics/Viewport.hpp"
+
#include <al.h>
#include <SDL.h>
-#include <stdexcept>
#include <string>
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:
class Window {
public:
- Window();
+ Window(int w, int h);
~Window();
Window(const Window &) = delete;
};
-struct InitHeadless {
-
- InitHeadless();
-
- InitSDL init_sdl;
- InitNet init_net;
-
-};
-
struct Init {
Init(bool double_buffer = true, int sample_size = 1);
Window window;
GLContext ctx;
InitGLEW init_glew;
+ graphics::Viewport viewport;
};
--- /dev/null
+#include "MasterState.hpp"
+
+#include "../world/Body.hpp"
+#include "../world/Simulation.hpp"
+
+#include <glm/gtx/transform.hpp>
+
+
+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);
+}
+
+}
+}
#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 <exception>
-#include <iostream>
+#include <cstdint>
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;
}
--- /dev/null
+#ifndef BLOBS_GRAPHICS_ARRAYTEXTURE_HPP_
+#define BLOBS_GRAPHICS_ARRAYTEXTURE_HPP_
+
+#include "Format.hpp"
+#include "TextureBase.hpp"
+
+#include <GL/glew.h>
+
+struct SDL_Surface;
+
+
+namespace blobs {
+namespace graphics {
+
+class ArrayTexture
+: public TextureBase<GL_TEXTURE_2D_ARRAY> {
+
+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
--- /dev/null
+#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
--- /dev/null
+#ifndef BLOBS_GRAPHICS_CUBEMAP_HPP_
+#define BLOBS_GRAPHICS_CUBEMAP_HPP_
+
+#include "Format.hpp"
+#include "TextureBase.hpp"
+
+#include <GL/glew.h>
+
+struct SDL_Surface;
+
+
+namespace blobs {
+namespace graphics {
+
+class CubeMap
+: public TextureBase<GL_TEXTURE_CUBE_MAP> {
+
+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
--- /dev/null
+#ifndef BLOBS_GRAPHICS_FONT_HPP_
+#define BLOBS_GRAPHICS_FONT_HPP_
+
+#include "glm.hpp"
+
+#include <SDL_ttf.h>
+
+
+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
--- /dev/null
+#ifndef BLOBS_GRAPHICS_FORMAT_HPP_
+#define BLOBS_GRAPHICS_FORMAT_HPP_
+
+#include <SDL.h>
+#include <GL/glew.h>
+
+
+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
--- /dev/null
+#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
--- /dev/null
+#ifndef BLOBS_GRAPHICS_PROGRAM_HPP_
+#define BLOBS_GRAPHICS_PROGRAM_HPP_
+
+#include "glm.hpp"
+
+#include <iosfwd>
+#include <list>
+#include <GL/glew.h>
+
+
+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<Shader> shaders;
+
+};
+
+}
+}
+
+#endif
--- /dev/null
+#ifndef BLOBS_GRAPHICS_SHADER_HPP_
+#define BLOBS_GRAPHICS_SHADER_HPP_
+
+#include <iosfwd>
+#include <GL/glew.h>
+
+
+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
--- /dev/null
+#ifndef BLOBS_GRAPHICS_VAO_HPP_
+#define BLOBS_GRAPHICS_VAO_HPP_
+
+#include "buffer.hpp"
+#include "gl_traits.hpp"
+
+#include <GL/glew.h>
+
+
+namespace blobs {
+namespace graphics {
+
+/// Simple vertex array object based on indexed draw calls with attributes
+/// interleaved in a single buffer.
+template<class Attributes, class Element>
+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<class Attribute>
+ void AttributePointer(GLuint index, bool normalized, std::size_t offset) noexcept {
+ glVertexAttribPointer(
+ index,
+ gl_traits<Attribute>::size,
+ gl_traits<Attribute>::type,
+ normalized,
+ sizeof(Attributes),
+ reinterpret_cast<const void *>(offset)
+ );
+ }
+ void ReserveAttributes(std::size_t size, GLenum usage) noexcept {
+ glBufferData(GL_ARRAY_BUFFER, size * sizeof(Attributes), nullptr, usage);
+ }
+ MappedBuffer<Attributes> MapAttributes(GLenum access) {
+ return MappedBuffer<Attributes>(GL_ARRAY_BUFFER, access);
+ }
+ void ReserveElements(std::size_t size, GLenum usage) noexcept {
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, size * sizeof(Element), nullptr, usage);
+ }
+ MappedBuffer<Element> MapElements(GLenum access) {
+ return MappedBuffer<Element>(GL_ELEMENT_ARRAY_BUFFER, access);
+ }
+ void DrawTriangles(std::size_t size, std::size_t offset = 0) const noexcept {
+ glDrawElements(GL_TRIANGLES, size, gl_traits<Element>::type, ((Element *) nullptr) + offset);
+ }
+
+private:
+ GLuint vao;
+ GLuint buffers[2];
+
+};
+
+}
+}
+
+#endif
--- /dev/null
+#ifndef BLOBS_GRAPHICS_TEXTURE_HPP_
+#define BLOBS_GRAPHICS_TEXTURE_HPP_
+
+#include "TextureBase.hpp"
+
+#include <GL/glew.h>
+
+struct SDL_Surface;
+
+
+namespace blobs {
+namespace graphics {
+
+struct Format;
+
+class Texture
+: public TextureBase<GL_TEXTURE_2D> {
+
+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
--- /dev/null
+#ifndef BLOBS_GRAPHICS_TEXTUREBASE_HPP_
+#define BLOBS_GRAPHICS_TEXTUREBASE_HPP_
+
+#include <GL/glew.h>
+
+
+namespace blobs {
+namespace graphics {
+
+template<GLenum TARGET, GLsizei COUNT = 1>
+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
--- /dev/null
+#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
--- /dev/null
+#ifndef BLOBS_GRAPHICS_BUFFER_HPP_
+#define BLOBS_GRAPHICS_BUFFER_HPP_
+
+#include "../app/error.hpp"
+
+#include <algorithm>
+#include <GL/glew.h>
+
+
+namespace blobs {
+namespace graphics {
+
+template<class T>
+class MappedBuffer {
+
+public:
+ MappedBuffer(GLenum target, GLenum access)
+ : buf(reinterpret_cast<T *>(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<T> &&other) noexcept
+ : buf(other.buf)
+ , target(other.target) {
+ other.buf = nullptr;
+ }
+ MappedBuffer<T> &operator =(MappedBuffer<T> &&other) noexcept {
+ std::swap(buf, other.buf);
+ std::swap(target, other.target);
+ }
+
+ MappedBuffer(const MappedBuffer<T> &) = delete;
+ MappedBuffer<T> &operator =(const MappedBuffer<T> &) = 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
--- /dev/null
+#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
--- /dev/null
+#include "gl_traits.hpp"
+
+
+namespace blobs {
+namespace graphics {
+
+constexpr GLint gl_traits<signed char>::size;
+constexpr GLenum gl_traits<signed char>::type;
+
+constexpr GLint gl_traits<unsigned char>::size;
+constexpr GLenum gl_traits<unsigned char>::type;
+
+constexpr GLint gl_traits<short>::size;
+constexpr GLenum gl_traits<short>::type;
+
+constexpr GLint gl_traits<unsigned short>::size;
+constexpr GLenum gl_traits<unsigned short>::type;
+
+constexpr GLint gl_traits<int>::size;
+constexpr GLenum gl_traits<int>::type;
+
+constexpr GLint gl_traits<unsigned int>::size;
+constexpr GLenum gl_traits<unsigned int>::type;
+
+constexpr GLint gl_traits<float>::size;
+constexpr GLenum gl_traits<float>::type;
+
+constexpr GLint gl_traits<double>::size;
+constexpr GLenum gl_traits<double>::type;
+
+}
+}
--- /dev/null
+#ifndef BLOBS_GRAPHICS_GL_TRAITS_HPP_
+#define BLOBS_GRAPHICS_GL_TRAITS_HPP_
+
+#include "glm.hpp"
+
+#include <GL/glew.h>
+
+
+namespace blobs {
+namespace graphics {
+
+template<class T>
+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<signed char> {
+ static constexpr GLint size = 1;
+ static constexpr GLenum type = GL_BYTE;
+};
+
+template<> struct gl_traits<unsigned char> {
+ static constexpr GLint size = 1;
+ static constexpr GLenum type = GL_UNSIGNED_BYTE;
+};
+
+template<> struct gl_traits<short> {
+ static constexpr GLint size = 1;
+ static constexpr GLenum type = GL_SHORT;
+};
+
+template<> struct gl_traits<unsigned short> {
+ static constexpr GLint size = 1;
+ static constexpr GLenum type = GL_UNSIGNED_SHORT;
+};
+
+template<> struct gl_traits<int> {
+ static constexpr GLint size = 1;
+ static constexpr GLenum type = GL_INT;
+};
+
+template<> struct gl_traits<unsigned int> {
+ static constexpr GLint size = 1;
+ static constexpr GLenum type = GL_UNSIGNED_INT;
+};
+
+template<> struct gl_traits<float> {
+ static constexpr GLint size = 1;
+ static constexpr GLenum type = GL_FLOAT;
+};
+
+template<> struct gl_traits<double> {
+ static constexpr GLint size = 1;
+ static constexpr GLenum type = GL_DOUBLE;
+};
+
+// composite types
+
+template<>
+template<class T, glm::precision P>
+struct gl_traits<glm::tvec1<T, P>> {
+ static constexpr GLint size = 1;
+ static constexpr GLenum type = gl_traits<T>::type;
+};
+template<class T, glm::precision P>
+constexpr GLint gl_traits<glm::tvec1<T, P>>::size;
+template<class T, glm::precision P>
+constexpr GLenum gl_traits<glm::tvec1<T, P>>::type;
+
+template<>
+template<class T, glm::precision P>
+struct gl_traits<glm::tvec2<T, P>> {
+ static constexpr GLint size = 2;
+ static constexpr GLenum type = gl_traits<T>::type;
+};
+template<class T, glm::precision P>
+constexpr GLint gl_traits<glm::tvec2<T, P>>::size;
+template<class T, glm::precision P>
+constexpr GLenum gl_traits<glm::tvec2<T, P>>::type;
+
+template<>
+template<class T, glm::precision P>
+struct gl_traits<glm::tvec3<T, P>> {
+ static constexpr GLint size = 3;
+ static constexpr GLenum type = gl_traits<T>::type;
+};
+template<class T, glm::precision P>
+constexpr GLint gl_traits<glm::tvec3<T, P>>::size;
+template<class T, glm::precision P>
+constexpr GLenum gl_traits<glm::tvec3<T, P>>::type;
+
+template<>
+template<class T, glm::precision P>
+struct gl_traits<glm::tvec4<T, P>> {
+ static constexpr GLint size = 4;
+ static constexpr GLenum type = gl_traits<T>::type;
+};
+template<class T, glm::precision P>
+constexpr GLint gl_traits<glm::tvec4<T, P>>::size;
+template<class T, glm::precision P>
+constexpr GLenum gl_traits<glm::tvec4<T, P>>::type;
+
+}
+}
+
+#endif
--- /dev/null
+#ifndef BLOBS_GRAPHICS_GLM_HPP_
+#define BLOBS_GRAPHICS_GLM_HPP_
+
+#ifndef GLM_FORCE_RADIANS
+# define GLM_FORCE_RADIANS 1
+#endif
+
+#include <glm/glm.hpp>
+
+// 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
--- /dev/null
+#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 <algorithm>
+#include <cstring>
+#include <iostream>
+#include <memory>
+#include <stdexcept>
+
+
+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<GLenum TARGET, GLsizei COUNT>
+TextureBase<TARGET, COUNT>::TextureBase() {
+ glGenTextures(COUNT, handle);
+}
+
+template<GLenum TARGET, GLsizei COUNT>
+TextureBase<TARGET, COUNT>::~TextureBase() {
+ glDeleteTextures(COUNT, handle);
+}
+
+template<GLenum TARGET, GLsizei COUNT>
+TextureBase<TARGET, COUNT>::TextureBase(TextureBase &&other) noexcept {
+ std::memcpy(handle, other.handle, sizeof(handle));
+ std::memset(other.handle, 0, sizeof(handle));
+}
+
+template<GLenum TARGET, GLsizei COUNT>
+TextureBase<TARGET, COUNT> &TextureBase<TARGET, COUNT>::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<unsigned char[]> data(new unsigned char[size]);
+ unsigned char *src = reinterpret_cast<unsigned char *>(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<SDL_Surface *>(&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<SDL_Surface *>(&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
+ );
+}
+
+}
+}
--- /dev/null
+#include "PlanetSurface.hpp"
+#include "Program.hpp"
+#include "Shader.hpp"
+
+#include "ArrayTexture.hpp"
+#include "../app/init.hpp"
+
+#include <algorithm>
+#include <iostream>
+#include <memory>
+#include <ostream>
+#include <stdexcept>
+#include <string>
+#include <glm/gtc/type_ptr.hpp>
+
+
+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<char[]> 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<char[]> 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));
+}
+
+}
+}
--- /dev/null
+#include "Camera.hpp"
+#include "Viewport.hpp"
+
+#include "const.hpp"
+
+#include <GL/glew.h>
+#include <glm/gtx/transform.hpp>
+
+
+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);
+}
+
+}
+}
--- /dev/null
+#ifndef BLOBS_WORLD_BODY_HPP_
+#define BLOBS_WORLD_BODY_HPP_
+
+#include <vector>
+
+
+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<Body *> children;
+ double mass;
+ double radius;
+
+};
+
+}
+}
+
+#endif
#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 <cassert>
#include <memory>
+#include <GL/glew.h>
namespace blobs {
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) {
/// 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<Tile []> tiles;
+ struct Attributes {
+ glm::vec3 position;
+ glm::vec3 tex_coord;
+ };
+ graphics::SimpleVAO<Attributes, unsigned int> vao;
+
};
void GenerateTest(Planet &);
--- /dev/null
+#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
--- /dev/null
+#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
--- /dev/null
+#include "Simulation.hpp"
+
+
+namespace blobs {
+namespace world {
+
+Simulation::Simulation(Body &r)
+: root(r) {
+}
+
+Simulation::~Simulation() {
+}
+
+
+void Simulation::Tick() {
+}
+
+}
+}
+#include "Body.hpp"
#include "Planet.hpp"
+#include "Sun.hpp"
#include "Tile.hpp"
+#include "../app/Assets.hpp"
+#include "../graphics/Viewport.hpp"
+
#include <algorithm>
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<glm::vec3>(0, false, offsetof(Attributes, position));
+ vao.AttributePointer<glm::vec3>(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() {
}
}