]> git.localhorst.tv Git - tacos.git/commitdiff
basic floor idea
authorDaniel Karbach <daniel.karbach@localhorst.tv>
Mon, 7 Mar 2016 13:20:09 +0000 (14:20 +0100)
committerDaniel Karbach <daniel.karbach@localhorst.tv>
Mon, 7 Mar 2016 13:27:15 +0000 (14:27 +0100)
23 files changed:
.gitmodules [new file with mode: 0644]
assets [new submodule]
src/app/assets.cpp [new file with mode: 0644]
src/app/assets.hpp [new file with mode: 0644]
src/app/config.cpp [new file with mode: 0644]
src/app/config.hpp [new file with mode: 0644]
src/app/init.cpp
src/app/init.hpp
src/app/window.cpp [deleted file]
src/app/window.hpp [deleted file]
src/graphics/camera.cpp [new file with mode: 0644]
src/graphics/camera.hpp [new file with mode: 0644]
src/graphics/viewport.cpp [new file with mode: 0644]
src/graphics/viewport.hpp [new file with mode: 0644]
src/graphics/window.cpp [new file with mode: 0644]
src/graphics/window.hpp [new file with mode: 0644]
src/rand/GaloisLFSR.hpp [new file with mode: 0644]
src/rand/OctaveNoise.hpp [new file with mode: 0644]
src/rand/SimplexNoise.hpp [new file with mode: 0644]
src/rand/noise.cpp [new file with mode: 0644]
src/tacos.cpp
src/world/Floor.hpp [new file with mode: 0644]
src/world/world.cpp [new file with mode: 0644]

diff --git a/.gitmodules b/.gitmodules
new file mode 100644 (file)
index 0000000..4e1e58a
--- /dev/null
@@ -0,0 +1,3 @@
+[submodule "assets"]
+       path = assets
+       url = http://git.localhorst.tv/repo/tacos-assets.git
diff --git a/assets b/assets
new file mode 160000 (submodule)
index 0000000..c690bc6
--- /dev/null
+++ b/assets
@@ -0,0 +1 @@
+Subproject commit c690bc6e1a92fae19922e48b58c4874e330b490a
diff --git a/src/app/assets.cpp b/src/app/assets.cpp
new file mode 100644 (file)
index 0000000..e7773f5
--- /dev/null
@@ -0,0 +1,63 @@
+#include "assets.hpp"
+
+#include "config.hpp"
+#include "../graphics/shader.hpp"
+
+#include <cstdio>
+#include <memory>
+
+using std::string;
+using std::unique_ptr;
+
+
+namespace {
+
+/// get file's contents as zero terminated string
+unique_ptr<char[]> file_string(const char *path) {
+       FILE *file = std::fopen(path, "rb");
+       if (!file) {
+               throw std::runtime_error(string("failed to open file ") + path);
+       }
+       if (std::fseek(file, 0, SEEK_END) < 0) {
+               std::fclose(file);
+               throw std::runtime_error(string("failed to seek to end of file ") + path);
+       }
+       int file_size = std::ftell(file);
+       if (file_size < 0) {
+               std::fclose(file);
+               throw std::runtime_error(string("failed to seek to end of file ") + path);
+       }
+       std::rewind(file);
+       unique_ptr<char[]> buffer(new char[file_size + 1]);
+       int read = std::fread(buffer.get(), file_size, 1, file);
+       if (read != 1) {
+               std::fclose(file);
+               throw std::runtime_error(string("failed to read file ") + path);
+       }
+       std::fclose(file);
+       buffer[file_size] = '\0';
+       return buffer;
+}
+
+}
+
+namespace tacos {
+
+AssetLoader::AssetLoader(const Config &c)
+: config(c) {
+
+}
+
+Shader AssetLoader::LoadVertexShader(const string &name) const {
+       string path = config.asset_path + "shader/" + name + ".vertex.glsl";
+       unique_ptr<char[]> source(file_string(path.c_str()));
+       return Shader::Vertex(source.get());
+}
+
+Shader AssetLoader::LoadFragmentShader(const string &name) const {
+       string path = config.asset_path + "shader/" + name + ".fragment.glsl";
+       unique_ptr<char[]> source(file_string(path.c_str()));
+       return Shader::Fragment(source.get());
+}
+
+}
diff --git a/src/app/assets.hpp b/src/app/assets.hpp
new file mode 100644 (file)
index 0000000..4c4bcdd
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef TACOS_APP_ASSETS_HPP_
+#define TACOS_APP_ASSETS_HPP_
+
+#include <string>
+
+
+namespace tacos {
+
+class Config;
+class Shader;
+
+class AssetLoader {
+
+public:
+       explicit AssetLoader(const Config &);
+
+       Shader LoadVertexShader(const std::string &name) const;
+       Shader LoadFragmentShader(const std::string &name) const;
+
+private:
+       const Config &config;
+
+};
+
+}
+
+#endif
diff --git a/src/app/config.cpp b/src/app/config.cpp
new file mode 100644 (file)
index 0000000..f1e0087
--- /dev/null
@@ -0,0 +1,61 @@
+#include "config.hpp"
+
+#include <cstring>
+#include <iostream>
+#include <SDL.h>
+
+using namespace std;
+
+
+namespace tacos {
+
+Config::Config() {
+       char *base = SDL_GetBasePath();
+       char *pref = SDL_GetPrefPath("localhorst", "tacos");
+
+       asset_path = string(base) + "assets/";
+       config_path = pref;
+
+       SDL_free(base);
+       SDL_free(pref);
+}
+
+void Config::ReadArgs(int argc, const char *const *argv) {
+       if (argc < 1) return;
+
+       for (int i = 1; i < argc; ++i) {
+               const char *arg = argv[i];
+               if (!arg || arg[0] == '\0') {
+                       cerr << "empty argument at position " << i << endl;
+                       continue;
+               }
+               if (arg[0] != '-' || arg[1] != '-' || arg[2] == '\0') {
+                       cerr << "incomplete argument at position " << i << endl;
+                       continue;
+               }
+               const char *name = arg + 2;
+               if (strcmp(name, "asset-path") == 0) {
+                       ++i;
+                       if (i >= argc || !argv[i]) {
+                               cerr << "missing argument to --asset-path" << endl;
+                               continue;
+                       }
+                       asset_path = argv[i];
+               } else if (strcmp(name, "config-path") == 0) {
+                       ++i;
+                       if (i >= argc || !argv[i]) {
+                               cerr << "missing argument to --config-path" << endl;
+                               continue;
+                       }
+                       config_path = argv[i];
+               } else {
+                       cerr << "unknown argument " << arg << " at position " << i << endl;
+               }
+       }
+}
+
+void Config::ReadFile(const char *path) {
+       // TODO: implement reading config file
+}
+
+}
diff --git a/src/app/config.hpp b/src/app/config.hpp
new file mode 100644 (file)
index 0000000..1a6e115
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef TACOS_APP_CONFIG_HPP_
+#define TACOS_APP_CONFIG_HPP_
+
+#include <string>
+
+
+namespace tacos {
+
+struct Config {
+
+       Config();
+
+       void ReadArgs(int argc, const char *const *argv);
+       void ReadFile(const char *path);
+
+       // system settings
+       std::string asset_path;
+       std::string config_path;
+
+       // video settings
+       bool double_buffer = true;
+       int multi_sample = 0;
+
+};
+
+}
+
+#endif
index 146719b3c039eb128c9541b8cf067311126000e9..52c86c0f9fab415d474459d53e28ecc528b2ffd9 100644 (file)
@@ -1,5 +1,6 @@
 #include "init.hpp"
 
+#include "config.hpp"
 #include "error.hpp"
 
 #include <alut.h>
@@ -85,4 +86,14 @@ InitTTF::~InitTTF() noexcept {
        TTF_Quit();
 }
 
+
+Init::Init(const Config &config)
+: sdl()
+, gl(config.double_buffer, config.multi_sample)
+, img()
+, ttf()
+, alut() {
+
+}
+
 }
index 4c82d08a319aa338d01b3c35befcbe3081f1821e..698bbc260554b890f9d841e1e2cf1492474a91bb 100644 (file)
@@ -3,6 +3,9 @@
 
 namespace tacos {
 
+class Config;
+
+
 struct InitAlut {
 
        InitAlut();
@@ -51,6 +54,8 @@ struct InitTTF {
 
 struct Init {
 
+       explicit Init(const Config &);
+
        InitSDL sdl;
        InitGL gl;
        InitIMG img;
diff --git a/src/app/window.cpp b/src/app/window.cpp
deleted file mode 100644 (file)
index ff7a7e1..0000000
+++ /dev/null
@@ -1,48 +0,0 @@
-#include "window.hpp"
-
-#include "error.hpp"
-
-#include <stdexcept>
-#include <string>
-#include <GL/glew.h>
-
-
-namespace tacos {
-
-Window::Window() {
-       window = SDL_CreateWindow(
-               "tacos", // title
-               SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, // position
-               1440, 900, // size
-               SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE // flags
-       );
-       if (!window) {
-               throw SDLError("SDL_CreateWindow");
-       }
-       context = SDL_GL_CreateContext(window);
-       if (!context) {
-               SDL_DestroyWindow(window);
-               throw SDLError("SDL_GL_CreateContext");
-       }
-       glewExperimental = GL_TRUE;
-       GLenum err = glewInit();
-       if (err != GLEW_OK) {
-               SDL_GL_DeleteContext(context);
-               SDL_DestroyWindow(window);
-               std::string msg("glewInit: ");
-               msg += reinterpret_cast<const char *>(glewGetErrorString(err));
-               throw std::runtime_error(msg);
-       }
-}
-
-Window::~Window() noexcept {
-       SDL_GL_DeleteContext(context);
-       SDL_DestroyWindow(window);
-}
-
-void Window::Flip() noexcept {
-       SDL_GL_SwapWindow(window);
-}
-
-
-}
diff --git a/src/app/window.hpp b/src/app/window.hpp
deleted file mode 100644 (file)
index cc8fc8a..0000000
+++ /dev/null
@@ -1,28 +0,0 @@
-#ifndef TACOS_APP_WINDOW_HPP_
-#define TACOS_APP_WINDOW_HPP_
-
-#include <SDL.h>
-
-
-namespace tacos {
-
-class Window {
-
-public:
-       Window();
-       ~Window() noexcept;
-
-       Window(const Window &) = delete;
-       Window &operator =(const Window &) = delete;
-
-       void Flip() noexcept;
-
-private:
-       SDL_Window *window;
-       SDL_GLContext context;
-
-};
-
-}
-
-#endif
diff --git a/src/graphics/camera.cpp b/src/graphics/camera.cpp
new file mode 100644 (file)
index 0000000..bc43e04
--- /dev/null
@@ -0,0 +1,25 @@
+#include "camera.hpp"
+
+#include <glm/gtx/rotate_vector.hpp>
+
+
+namespace tacos {
+
+Camera::Camera(const glm::vec3 &f)
+: focus(&f)
+, distance(100.0f)
+, pitch(1.04719755119659774614f) // π/3
+, yaw(0.0f) {
+
+}
+
+void Camera::Focus(const glm::vec3 &f) noexcept {
+       focus = &f;
+}
+
+glm::mat4 Camera::View() const noexcept {
+       const glm::vec3 position(*focus - distance * rotateY(rotateX(glm::vec3(0.0f, 0.0f, 1.0f), pitch), yaw));
+       return glm::lookAt(position, *focus, glm::vec3(0.0f, 1.0f, 0.0f));
+}
+
+}
diff --git a/src/graphics/camera.hpp b/src/graphics/camera.hpp
new file mode 100644 (file)
index 0000000..a1660ca
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef TACOS_GRAPHICS_CAMERA_HPP_
+#define TACOS_GRAPHICS_CAMERA_HPP_
+
+#include <glm/glm.hpp>
+
+
+namespace tacos {
+
+/// Camera is facing a focus point. Its position is determined by
+/// displacing said focus point by a distance in the direction indicated
+/// by pitch and yaw.
+/// The up direction is fixed to positive Y.
+class Camera {
+
+public:
+       explicit Camera(const glm::vec3 &focus);
+
+       glm::mat4 View() const noexcept;
+
+       void Focus(const glm::vec3 &) noexcept;
+
+private:
+       const glm::vec3 *focus;
+       float distance;
+       float pitch;
+       float yaw;
+
+};
+
+}
+
+#endif
diff --git a/src/graphics/viewport.cpp b/src/graphics/viewport.cpp
new file mode 100644 (file)
index 0000000..4ee85c8
--- /dev/null
@@ -0,0 +1,30 @@
+#include "viewport.hpp"
+
+#include <GL/glew.h>
+#include <glm/gtc/matrix_transform.hpp>
+
+
+namespace tacos {
+
+Viewport::Viewport(int w, int h)
+: width(w)
+, height(h)
+, fov(0.78539816339744830961f) // π/4
+, aspect(float(w) / float(h))
+, near(0.1f)
+, far(100.0f)
+, perspective(glm::perspective(fov, aspect, near, far))
+, ortho(glm::ortho(0.0f, float(width), float(height), 0.0f, near, far)) {
+
+}
+
+void Viewport::Resize(int w, int h) noexcept {
+       width = w;
+       height = h;
+       aspect = float(width) / float(height);
+       perspective = glm::perspective(fov, aspect, near, far);
+       ortho = glm::ortho(0.0f, float(width), float(height), 0.0f, near, far);
+       glViewport(0, 0, width, height);
+}
+
+}
diff --git a/src/graphics/viewport.hpp b/src/graphics/viewport.hpp
new file mode 100644 (file)
index 0000000..cc9a100
--- /dev/null
@@ -0,0 +1,41 @@
+#ifndef TACOS_GRAPHICS_VIEWPORT_HPP_
+#define TACOS_GRAPHICS_VIEWPORT_HPP_
+
+#include <glm/glm.hpp>
+
+
+namespace tacos {
+
+class Viewport {
+
+public:
+       Viewport(int width, int height);
+
+       Viewport(const Viewport &) = delete;
+       Viewport &operator =(const Viewport &) = delete;
+
+       void Resize(int w, int h) noexcept;
+
+       int Width() const noexcept { return width; }
+       int Height() const noexcept { return height; }
+
+       const glm::mat4 &Perspective() const noexcept { return perspective; }
+       const glm::mat4 &Ortho() const noexcept { return ortho; }
+
+private:
+       int width;
+       int height;
+
+       float fov;
+       float aspect;
+       float near;
+       float far;
+
+       glm::mat4 perspective;
+       glm::mat4 ortho;
+
+};
+
+}
+
+#endif
diff --git a/src/graphics/window.cpp b/src/graphics/window.cpp
new file mode 100644 (file)
index 0000000..99f0090
--- /dev/null
@@ -0,0 +1,48 @@
+#include "window.hpp"
+
+#include "../app/error.hpp"
+
+#include <stdexcept>
+#include <string>
+#include <GL/glew.h>
+
+
+namespace tacos {
+
+Window::Window(int width, int height) {
+       window = SDL_CreateWindow(
+               "tacos", // title
+               SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, // position
+               width, height, // size
+               SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE // flags
+       );
+       if (!window) {
+               throw SDLError("SDL_CreateWindow");
+       }
+       context = SDL_GL_CreateContext(window);
+       if (!context) {
+               SDL_DestroyWindow(window);
+               throw SDLError("SDL_GL_CreateContext");
+       }
+       glewExperimental = GL_TRUE;
+       GLenum err = glewInit();
+       if (err != GLEW_OK) {
+               SDL_GL_DeleteContext(context);
+               SDL_DestroyWindow(window);
+               std::string msg("glewInit: ");
+               msg += reinterpret_cast<const char *>(glewGetErrorString(err));
+               throw std::runtime_error(msg);
+       }
+}
+
+Window::~Window() noexcept {
+       SDL_GL_DeleteContext(context);
+       SDL_DestroyWindow(window);
+}
+
+void Window::Flip() noexcept {
+       SDL_GL_SwapWindow(window);
+}
+
+
+}
diff --git a/src/graphics/window.hpp b/src/graphics/window.hpp
new file mode 100644 (file)
index 0000000..1685cf1
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef TACOS_GRAPHICS_WINDOW_HPP_
+#define TACOS_GRAPHICS_WINDOW_HPP_
+
+#include <SDL.h>
+
+
+namespace tacos {
+
+class Window {
+
+public:
+       Window(int width, int height);
+       ~Window() noexcept;
+
+       Window(const Window &) = delete;
+       Window &operator =(const Window &) = delete;
+
+       void Flip() noexcept;
+
+private:
+       SDL_Window *window;
+       SDL_GLContext context;
+
+};
+
+}
+
+#endif
diff --git a/src/rand/GaloisLFSR.hpp b/src/rand/GaloisLFSR.hpp
new file mode 100644 (file)
index 0000000..94b12d0
--- /dev/null
@@ -0,0 +1,77 @@
+#ifndef TACOS_RAND_GALOISLFSR_HPP_
+#define TACOS_RAND_GALOISLFSR_HPP_
+
+#include <cstdint>
+#include <limits>
+
+
+namespace tacos {
+
+class GaloisLFSR {
+
+public:
+       // seed should be non-zero
+       explicit GaloisLFSR(std::uint64_t seed) noexcept
+       : state(seed) {
+               if (state == 0) {
+                       state = 1;
+               }
+       }
+
+       // get the next bit
+       bool operator ()() noexcept {
+               bool result = state & 1;
+               state >>= 1;
+               if (result) {
+                       state |= 0x8000000000000000;
+                       state ^= mask;
+               } else {
+                       state &= 0x7FFFFFFFFFFFFFFF;
+               }
+               return result;
+       }
+
+       template<class T>
+       T operator ()(T &out) noexcept {
+               constexpr int num_bits =
+                       std::numeric_limits<T>::digits +
+                       std::numeric_limits<T>::is_signed;
+               for (int i = 0; i < num_bits; ++i) {
+                       operator ()();
+               }
+               return out = static_cast<T>(state);
+       }
+
+       template<class T>
+       T Next() noexcept {
+               T next;
+               return (*this)(next);
+       }
+
+       float SNorm() noexcept {
+               return float(Next<std::uint32_t>()) * (1.0f / 2147483647.5f) - 1.0f;
+       }
+
+       float UNorm() noexcept {
+               return float(Next<std::uint32_t>()) * (1.0f / 4294967295.0f);
+       }
+
+       template<class Container>
+       typename Container::reference From(Container &c) {
+               return c[Next<typename Container::size_type>() % c.size()];
+       }
+       template<class Container>
+       typename Container::const_reference From(const Container &c) {
+               return c[Next<typename Container::size_type>() % c.size()];
+       }
+
+private:
+       std::uint64_t state;
+       // bits 64, 63, 61, and 60 set to 1 (counting from 1 lo to hi)
+       static constexpr std::uint64_t mask = 0xD800000000000000;
+
+};
+
+}
+
+#endif
diff --git a/src/rand/OctaveNoise.hpp b/src/rand/OctaveNoise.hpp
new file mode 100644 (file)
index 0000000..bcd872e
--- /dev/null
@@ -0,0 +1,33 @@
+#ifndef TACOS_RAND_OCTAVENOISE_HPP_
+#define TACOS_RAND_OCTAVENOISE_HPP_
+
+#include <glm/glm.hpp>
+
+
+namespace tacos {
+
+template<class Noise>
+float OctaveNoise(
+       const Noise &noise,
+       const glm::vec3 &in,
+       int num,
+       float persistence,
+       float frequency = 1.0f,
+       float amplitude = 1.0f,
+       float growth = 2.0f
+) {
+       float total = 0.0f;
+       float max = 0.0f;
+       for (int i = 0; i < num; ++i) {
+               total += noise(in * frequency) * amplitude;
+               max += amplitude;
+               amplitude *= persistence;
+               frequency *= growth;
+       }
+
+       return total / max;
+}
+
+}
+
+#endif
diff --git a/src/rand/SimplexNoise.hpp b/src/rand/SimplexNoise.hpp
new file mode 100644 (file)
index 0000000..a91e1c4
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef TACOS_RAND_SIMPLEXNOISE_HPP_
+#define TACOS_RAND_SIMPLEXNOISE_HPP_
+
+#include <cstdint>
+#include <glm/glm.hpp>
+
+
+namespace tacos {
+
+class SimplexNoise {
+
+public:
+       explicit SimplexNoise(std::uint64_t seed) noexcept;
+
+       float operator ()(const glm::vec3 &) const noexcept;
+
+private:
+       int Perm(int idx) const noexcept;
+       int Perm12(int idx) const noexcept;
+       const glm::vec3 &Grad(int idx) const noexcept;
+
+private:
+       int perm[512];
+       int perm12[512];
+       glm::vec3 grad[12];
+       glm::ivec3 second_ints[8];
+       glm::ivec3 third_ints[8];
+       glm::vec3 second_floats[8];
+       glm::vec3 third_floats[8];
+
+};
+
+}
+
+#endif
diff --git a/src/rand/noise.cpp b/src/rand/noise.cpp
new file mode 100644 (file)
index 0000000..7466945
--- /dev/null
@@ -0,0 +1,161 @@
+#include "GaloisLFSR.hpp"
+#include "SimplexNoise.hpp"
+
+#include <cmath>
+
+
+namespace {
+
+constexpr float one_third = 1.0f/3.0f;
+constexpr float one_sixth = 1.0f/6.0f;
+
+}
+
+namespace tacos {
+
+SimplexNoise::SimplexNoise(std::uint64_t seed) noexcept
+: grad({
+       {  1.0f,  1.0f,  0.0f },
+       { -1.0f,  1.0f,  0.0f },
+       {  1.0f, -1.0f,  0.0f },
+       { -1.0f, -1.0f,  0.0f },
+       {  1.0f,  0.0f,  1.0f },
+       { -1.0f,  0.0f,  1.0f },
+       {  1.0f,  0.0f, -1.0f },
+       { -1.0f,  0.0f, -1.0f },
+       {  0.0f,  1.0f,  1.0f },
+       {  0.0f, -1.0f,  1.0f },
+       {  0.0f,  1.0f, -1.0f },
+       {  0.0f, -1.0f, -1.0f },
+})
+, second_ints({
+                    // x>y x>z y>z
+       { 0, 0, 1 }, //  0   0   0  ZYX
+       { 0, 1, 0 }, //  0   0   1  YZX
+       { 0, 0, 1 }, //  0   1   0  illogical, but ZYX
+       { 0, 1, 0 }, //  0   1   1  YXZ
+       { 0, 0, 1 }, //  1   0   0  ZXY
+       { 1, 0, 0 }, //  1   0   1  illogical, but XYZ
+       { 1, 0, 0 }, //  1   1   0  XZY
+       { 1, 0, 0 }, //  1   1   1  XYZ
+})
+, third_ints({
+                    // x>y x>z y>z
+       { 0, 1, 1 }, //  0   0   0  ZYX
+       { 0, 1, 1 }, //  0   0   1  YZX
+       { 0, 1, 1 }, //  0   1   0  illogical, but ZYX
+       { 1, 1, 0 }, //  0   1   1  YXZ
+       { 1, 0, 1 }, //  1   0   0  ZXY
+       { 1, 1, 0 }, //  1   0   1  illogical, but XYZ
+       { 1, 0, 1 }, //  1   1   0  XZY
+       { 1, 1, 0 }, //  1   1   1  XYZ
+})
+, second_floats({
+                             // x>y x>z y>z
+       { 0.0f, 0.0f, 1.0f }, //  0   0   0  ZYX
+       { 0.0f, 1.0f, 0.0f }, //  0   0   1  YZX
+       { 0.0f, 0.0f, 1.0f }, //  0   1   0  illogical, but ZYX
+       { 0.0f, 1.0f, 0.0f }, //  0   1   1  YXZ
+       { 0.0f, 0.0f, 1.0f }, //  1   0   0  ZXY
+       { 1.0f, 0.0f, 0.0f }, //  1   0   1  illogical, but XYZ
+       { 1.0f, 0.0f, 0.0f }, //  1   1   0  XZY
+       { 1.0f, 0.0f, 0.0f }, //  1   1   1  XYZ
+})
+, third_floats({
+                             // x>y x>z y>z
+       { 0.0f, 1.0f, 1.0f }, //  0   0   0  ZYX
+       { 0.0f, 1.0f, 1.0f }, //  0   0   1  YZX
+       { 0.0f, 1.0f, 1.0f }, //  0   1   0  illogical, but ZYX
+       { 1.0f, 1.0f, 0.0f }, //  0   1   1  YXZ
+       { 1.0f, 0.0f, 1.0f }, //  1   0   0  ZXY
+       { 1.0f, 1.0f, 0.0f }, //  1   0   1  illogical, but XYZ
+       { 1.0f, 0.0f, 1.0f }, //  1   1   0  XZY
+       { 1.0f, 1.0f, 0.0f }, //  1   1   1  XYZ
+}) {
+       GaloisLFSR random(seed ^ 0x0123456789ACBDEF);
+       unsigned char value;
+       for (size_t i = 0; i < 256; ++i) {
+               perm[i] = random(value);
+               perm[i] &= 0xFF;
+               perm[i + 256] = perm[i];
+               perm12[i] = perm[i] % 12;
+               perm12[i + 256] = perm12[i];
+       }
+}
+
+
+float SimplexNoise::operator ()(const glm::vec3 &in) const noexcept {
+       float skew = (in.x + in.y + in.z) * one_third;
+
+       glm::vec3 skewed(glm::floor(in + skew));
+       float tr = (skewed.x + skewed.y + skewed.z) * one_sixth;
+
+       glm::vec3 unskewed(skewed - tr);
+       glm::vec3 relative(in - unskewed);
+
+       bool x_ge_y = relative.x >= relative.y;
+       bool x_ge_z = relative.x >= relative.z;
+       bool y_ge_z = relative.y >= relative.z;
+       unsigned int st = (x_ge_y << 2) | (x_ge_z << 1) | y_ge_z;
+
+       glm::ivec3 second_int(second_ints[st]);
+       glm::ivec3 third_int(third_ints[st]);
+       glm::vec3 second_float(second_floats[st]);
+       glm::vec3 third_float(third_floats[st]);
+
+       glm::vec3 offset[4] = {
+               in - unskewed,
+               relative - second_float + one_sixth,
+               relative - third_float + one_third,
+               relative - 0.5f,
+       };
+
+       int index[3] = {
+               (int)(skewed.x) & 0xFF,
+               (int)(skewed.y) & 0xFF,
+               (int)(skewed.z) & 0xFF,
+       };
+
+       float n = 0.0f;
+
+       // 0
+       float t = glm::clamp(0.5f - dot(offset[0], offset[0]), 0.0f, 1.0f);
+       t *= t;
+       int corner = Perm12(index[0] + Perm(index[1] + Perm(index[2])));
+       n += t * t * dot(Grad(corner), offset[0]);
+
+       // 1
+       t = glm::clamp(0.5f - dot(offset[1], offset[1]), 0.0f, 1.0f);
+       t *= t;
+       corner = Perm12(index[0] + second_int.x + Perm(index[1] + second_int.y + Perm(index[2] + second_int.z)));
+       n += t * t * dot(Grad(corner), offset[1]);
+
+       // 2
+       t = glm::clamp(0.5f - dot(offset[2], offset[2]), 0.0f, 1.0f);
+       t *= t;
+       corner = Perm12(index[0] + third_int.x + Perm(index[1] + third_int.y + Perm(index[2] + third_int.z)));
+       n += t * t * dot(Grad(corner), offset[2]);
+
+       // 3
+       t = glm::clamp(0.5f - dot(offset[3], offset[3]), 0.0f, 1.0f);
+       t *= t;
+       corner = Perm12(index[0] + 1 + Perm(index[1] + 1 + Perm(index[2] + 1)));
+       n += t * t * dot(Grad(corner), offset[3]);
+
+       return 32.0f * n;
+}
+
+
+int SimplexNoise::Perm(int idx) const noexcept {
+       return perm[idx];
+}
+
+int SimplexNoise::Perm12(int idx) const noexcept {
+       return perm12[idx];
+}
+
+const glm::vec3 &SimplexNoise::Grad(int idx) const noexcept {
+       return grad[idx];
+}
+
+}
index 7e0976f6dab2a54d4d247802ace1d1019896b578..580becb9caa0e391a5b779de2010976a9609ad22 100644 (file)
@@ -1,10 +1,92 @@
+#include "app/assets.hpp"
+#include "app/config.hpp"
 #include "app/init.hpp"
-#include "app/window.hpp"
+#include "graphics/camera.hpp"
+#include "graphics/shader.hpp"
+#include "graphics/viewport.hpp"
+#include "graphics/window.hpp"
+#include "rand/SimplexNoise.hpp"
+#include "world/Floor.hpp"
+
+#include <iostream>
+#include <GL/glew.h>
+#include <glm/glm.hpp>
+#include <glm/gtc/matrix_transform.hpp>
 
 using namespace tacos;
 
 int main(int argc, char *argv[]) {
-       Init init;
-       Window window;
+       Config config;
+       config.ReadArgs(argc, argv);
+
+       Init init(config);
+       AssetLoader asset_loader(config);
+       Window window(1440, 900);
+       Viewport viewport(1440, 900);
+
+       constexpr float noise_scale = 1.0f/64.0f;
+       constexpr float height_scale = 5.0f;
+
+       // === test ===
+       Shader floor_vert(asset_loader.LoadVertexShader("floor"));
+       Shader floor_frag(asset_loader.LoadFragmentShader("floor"));
+       Program floor_program;
+       floor_program.Attach(floor_vert);
+       floor_program.Attach(floor_frag);
+       floor_program.Link();
+       GLint mv_location = floor_program.UniformLocation("MV");
+       GLint mvp_location = floor_program.UniformLocation("MVP");
+
+       Floor floor(64, 50);
+       SimplexNoise noise(0);
+       for (int z = 0; z < floor.Depth(); ++z) {
+               for (int x = 0; x < floor.Width(); ++x) {
+                       floor.SetElevation(x, z, noise(glm::vec3(x * noise_scale, 0.0f, z * noise_scale)) * height_scale);
+               }
+       }
+       floor.GenerateVertices();
+
+       glm::vec3 focus(20.0f, 0.0f, 20.0f);
+       Camera camera(focus);
+
+       glm::mat4 M;
+       glm::mat4 V;
+       glm::mat4 MV;
+       glm::mat4 MVP;
+
+       floor_program.Use();
+
+       bool running = true;
+       SDL_Event event;
+       while (running) {
+               while (SDL_PollEvent(&event)) {
+                       switch (event.type) {
+                               case SDL_QUIT:
+                                       running = false;
+                                       break;
+                               case SDL_WINDOWEVENT:
+                                       if (event.window.event == SDL_WINDOWEVENT_RESIZED) {
+                                               viewport.Resize(event.window.data1, event.window.data2);
+                                       }
+                                       break;
+                               default:
+                                       break;
+                       }
+               }
+               V = camera.View();
+               glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+               for (int z = 0; z < floor.VAODepth(); ++z) {
+                       for (int x = 0; x < floor.VAOWidth(); ++x) {
+                               M = glm::translate(glm::mat4(1.0f), floor.VAOPosition(x, z));
+                               MV = camera.View() * M;
+                               MVP = viewport.Perspective() * MV;
+                               floor_program.Uniform(mv_location, MV);
+                               floor_program.Uniform(mvp_location, MVP);
+                               floor.DrawVAO(x, z);
+                       }
+               }
+               window.Flip();
+       }
+
        return 0;
 }
diff --git a/src/world/Floor.hpp b/src/world/Floor.hpp
new file mode 100644 (file)
index 0000000..a7046b5
--- /dev/null
@@ -0,0 +1,87 @@
+#ifndef TACOS_WORLD_FLOOR_HPP_
+#define TACOS_WORLD_FLOOR_HPP_
+
+#include <vector>
+#include <GL/glew.h>
+#include <glm/glm.hpp>
+
+
+namespace tacos {
+
+class Floor {
+
+public:
+       /// width and depth in vertices, so will have one less
+       /// in each dimension in tiles
+       Floor(int width, int depth);
+       ~Floor() noexcept;
+
+       Floor(const Floor &) = delete;
+       Floor &operator =(const Floor &) = delete;
+
+       static constexpr int VAO_DIVISOR = 32;
+
+       int Width() const noexcept { return width; }
+       int Depth() const noexcept { return depth; }
+
+       int VAOWidth() const noexcept { return vao_width; }
+       int VAODepth() const noexcept { return vao_depth; }
+
+       glm::vec3 VAOPosition(int vao_x, int vao_z) const noexcept {
+               return glm::vec3(float(vao_x * VAO_DIVISOR), 0.0f, float(vao_z * VAO_DIVISOR));
+       }
+       void DrawVAO(int vao_x, int vao_z) const noexcept;
+
+       void SetElevation(int x, int z, float e) noexcept {
+               elevation[z * width + x] = e;
+       }
+       float GetElevation(int x, int z) const noexcept {
+               return elevation[z * width + x];
+       }
+       float ClampedElevation(int x, int z) const noexcept {
+               return GetElevation(glm::clamp(x, 0, width), glm::clamp(z, 0, depth));
+       }
+       glm::vec3 GetNormal(int x, int z) const noexcept;
+
+       void GenerateVertices();
+
+private:
+       void SetupVAO(int which, GLuint element_buffer, int vertex_count) noexcept;
+       void FillElementBuffer(GLuint which, int tile_width, int tile_depth);
+       void FillAttribBuffer(int vao_x, int vao_z);
+       glm::ivec2 Tiles(int vao_x, int vao_z) const noexcept;
+       int NumTiles(int vao_x, int vao_z) const noexcept;
+       int NumVertices(int vao_x, int vao_z) const noexcept;
+
+private:
+       int width;
+       int depth;
+       std::vector<float> elevation;
+
+       int tile_width;
+       int tile_depth;
+       int unclean_width;
+       int unclean_depth;
+       int vao_width;
+       int vao_depth;
+       std::vector<GLuint> vaos;
+       // index of general element buffer
+       int general_elements;
+       // index of unclean width element buffer, if any
+       int unclean_width_elements;
+       // index of unclean depth element buffer, if any
+       int unclean_depth_elements;
+       // index of unclean corner element buffer, if any
+       int unclean_corner_elements;
+       std::vector<GLuint> buffers;
+
+       struct Attributes {
+               glm::vec3 position;
+               glm::vec3 normal;
+       };
+
+};
+
+}
+
+#endif
diff --git a/src/world/world.cpp b/src/world/world.cpp
new file mode 100644 (file)
index 0000000..eba12ea
--- /dev/null
@@ -0,0 +1,143 @@
+#include "Floor.hpp"
+
+
+namespace tacos {
+
+constexpr int Floor::VAO_DIVISOR;
+
+Floor::Floor(int w, int d)
+: width(w)
+, depth(d)
+, elevation(w * d)
+, tile_width(width - 1)
+, tile_depth(depth - 1)
+, unclean_width(tile_width % VAO_DIVISOR)
+, unclean_depth(tile_depth % VAO_DIVISOR)
+, vao_width(tile_width / VAO_DIVISOR + bool(unclean_width))
+, vao_depth(tile_depth / VAO_DIVISOR + bool(unclean_depth))
+, vaos(vao_width * vao_depth)
+, general_elements(vao_width * vao_depth)
+, unclean_width_elements(general_elements + bool(unclean_width))
+, unclean_depth_elements(unclean_depth ? (unclean_width_elements + 1) : general_elements)
+, unclean_corner_elements(std::max(unclean_width_elements, unclean_depth_elements) + (unclean_width && unclean_depth))
+, buffers(unclean_corner_elements + 1) {
+       glGenVertexArrays(vaos.size(), vaos.data());
+       glGenBuffers(buffers.size(), buffers.data());
+       int x_end = vao_width - bool(unclean_width);
+       int z_end = vao_depth - bool(unclean_depth);
+       for (int z = 0; z < z_end; ++z) {
+               for (int x = 0; x < x_end; ++x) {
+                       SetupVAO(z * vao_width + x, buffers[general_elements], (VAO_DIVISOR + 1) * (VAO_DIVISOR + 1));
+               }
+       }
+       // always fill general element buffer (there won't be one needed for maps with x or z less than divisor,
+       // but that shouldn't happen in practice, only during tests in which case I don't care about the overhead
+       FillElementBuffer(buffers[general_elements], VAO_DIVISOR, VAO_DIVISOR);
+       if (unclean_width) {
+               for (int z = 0; z < z_end; ++z) {
+                       SetupVAO(z * vao_width + x_end, buffers[unclean_width_elements], (unclean_width + 1) * (VAO_DIVISOR + 1));
+               }
+               FillElementBuffer(buffers[unclean_width_elements], unclean_width, VAO_DIVISOR);
+       }
+       if (unclean_depth) {
+               for (int x = 0; x < x_end; ++x) {
+                       SetupVAO(z_end * vao_width + x, buffers[unclean_depth_elements], (VAO_DIVISOR + 1) * (unclean_depth + 1));
+               }
+               FillElementBuffer(buffers[unclean_depth_elements], VAO_DIVISOR, unclean_depth);
+       }
+       if (unclean_width && unclean_depth) {
+               SetupVAO(z_end * vao_width + x_end, buffers[unclean_corner_elements], (unclean_width + 1) * (unclean_depth + 1));
+               FillElementBuffer(buffers[unclean_corner_elements], unclean_width, unclean_depth);
+       }
+}
+
+Floor::~Floor() noexcept {
+       glDeleteBuffers(buffers.size(), buffers.data());
+       glDeleteVertexArrays(vaos.size(), vaos.data());
+}
+
+void Floor::SetupVAO(int which, GLuint element_buffer, int vertex_count) noexcept {
+       glBindVertexArray(vaos[which]);
+       glBindBuffer(GL_ARRAY_BUFFER, buffers[which]);
+       glBufferData(GL_ARRAY_BUFFER, vertex_count * sizeof(Attributes), nullptr, GL_STATIC_DRAW);
+       glEnableVertexAttribArray(0);
+       glVertexAttribPointer(0, 3, GL_FLOAT, 0, sizeof(Attributes), reinterpret_cast<const void *>(offsetof(Attributes, position)));
+       glEnableVertexAttribArray(1);
+       glVertexAttribPointer(1, 3, GL_FLOAT, 0, sizeof(Attributes), reinterpret_cast<const void *>(offsetof(Attributes, normal)));
+       glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, element_buffer);
+}
+
+void Floor::FillElementBuffer(GLuint which, int tile_width, int tile_depth) {
+       // unbind VAO so we don't accidentally trash an element buffer binding
+       glBindVertexArray(0);
+       glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, which);
+       glBufferData(GL_ELEMENT_ARRAY_BUFFER, tile_width * tile_depth * sizeof(short) * 6, nullptr, GL_STATIC_DRAW);
+       // TODO: this can return null on error (out of memory in this case)
+       //       might be worth checking eventually
+       short *data = reinterpret_cast<short *>(glMapBuffer(GL_ELEMENT_ARRAY_BUFFER, GL_WRITE_ONLY));
+       for (int z = 0, i = 0; z < tile_depth; ++z) {
+               for (int x = 0; x < tile_width; ++x, ++i) {
+                       data[i * 6 + 0] = (z + 0) * (tile_width + 1) + (x + 0);
+                       data[i * 6 + 1] = (z + 0) * (tile_width + 1) + (x + 1);
+                       data[i * 6 + 2] = (z + 1) * (tile_width + 1) + (x + 0);
+                       data[i * 6 + 3] = (z + 0) * (tile_width + 1) + (x + 1);
+                       data[i * 6 + 4] = (z + 1) * (tile_width + 1) + (x + 1);
+                       data[i * 6 + 5] = (z + 1) * (tile_width + 1) + (x + 0);
+               }
+       }
+       glUnmapBuffer(GL_ELEMENT_ARRAY_BUFFER);
+}
+
+void Floor::FillAttribBuffer(int vao_x, int vao_z) {
+       glBindBuffer(GL_ARRAY_BUFFER, buffers[vao_z * vao_width + vao_x]);
+       Attributes *data = reinterpret_cast<Attributes *>(glMapBuffer(GL_ARRAY_BUFFER, GL_WRITE_ONLY));
+       glm::ivec2 tiles(Tiles(vao_x, vao_z));
+       for (int z = 0, abs_z = vao_z * VAO_DIVISOR, i = 0; z < tiles.y + 1; ++z, ++abs_z) {
+               for (int x = 0, abs_x = vao_x * VAO_DIVISOR; x < tiles.x + 1; ++x, ++abs_x, ++i) {
+                       data[i].position = glm::vec3(x, GetElevation(abs_x, abs_z), z);
+                       data[i].normal = GetNormal(abs_x, abs_z);
+               }
+       }
+       glUnmapBuffer(GL_ARRAY_BUFFER);
+}
+
+glm::vec3 Floor::GetNormal(int x, int z) const noexcept {
+       // TODO: not sure about the sign here
+       return normalize(glm::vec3(
+               ClampedElevation(x - 1, z) - ClampedElevation(x + 1, z),
+               2.0f,
+               ClampedElevation(x, z - 1) - ClampedElevation(x, z + 1)
+       ));
+}
+
+glm::ivec2 Floor::Tiles(int vao_x, int vao_z) const noexcept {
+       return glm::ivec2(
+               (unclean_width && vao_x == vao_width - 1) ? unclean_width : VAO_DIVISOR,
+               (unclean_depth && vao_z == vao_depth - 1) ? unclean_depth : VAO_DIVISOR);
+}
+
+int Floor::NumTiles(int vao_x, int vao_z) const noexcept {
+       glm::ivec2 tiles = Tiles(vao_x, vao_z);
+       return tiles.x * tiles.y;
+}
+
+int Floor::NumVertices(int vao_x, int vao_z) const noexcept {
+       glm::ivec2 tiles = Tiles(vao_x, vao_z);
+       return (tiles.x + 1) * (tiles.y + 1);
+}
+
+void Floor::GenerateVertices() {
+       for (int z = 0; z < vao_depth; ++z) {
+               for (int x = 0; x < vao_width; ++x) {
+                       FillAttribBuffer(x, z);
+               }
+       }
+}
+
+void Floor::DrawVAO(int vao_x, int vao_z) const noexcept {
+       glBindVertexArray(vaos[vao_z * vao_width + vao_x]);
+       // TODO: this cries for triangle strips
+       glDrawElements(GL_TRIANGLES, NumTiles(vao_x, vao_z) * 6, GL_UNSIGNED_SHORT, nullptr);
+}
+
+}