RELEASE_DIR := build/release
DIR := $(RELEASE_DIR) $(DEBUG_DIR) $(PROFILE_DIR) build
-SRC := $(wildcard $(SOURCE_DIR)/*.cpp)
+LIB_SRC := $(wildcard $(SOURCE_DIR)/*/*.cpp)
+BIN_SRC := $(wildcard $(SOURCE_DIR)/*.cpp)
+SRC := $(LIB_SRC) $(BIN_SRC)
RELEASE_OBJ := $(patsubst $(SOURCE_DIR)/%.cpp, $(RELEASE_DIR)/%.o, $(SRC))
DEBUG_OBJ := $(patsubst $(SOURCE_DIR)/%.cpp, $(DEBUG_DIR)/%.o, $(SRC))
PROFILE_OBJ := $(patsubst $(SOURCE_DIR)/%.cpp, $(PROFILE_DIR)/%.o, $(SRC))
@$(LDXX) -o $@ $(CXXFLAGS) $(LDXXFLAGS) $(PROFILE_FLAGS) $^
$(RELEASE_DIR)/%.o: $(SOURCE_DIR)/%.cpp | $(RELEASE_DIR)
+ @mkdir -p "$(@D)"
@echo compile: $@
@$(CXX) -c $(CPPFLAGS) $(CXXFLAGS) $(RELEASE_FLAGS) -o $@ -MMD -MP -MF"$(@:.o=.d)" -MT"$@" $<
$(DEBUG_DIR)/%.o: $(SOURCE_DIR)/%.cpp | $(DEBUG_DIR)
+ @mkdir -p "$(@D)"
@echo compile: $@
@$(CXX) -c $(CPPFLAGS) $(CXXFLAGS) $(DEBUG_FLAGS) -o $@ -MMD -MP -MF"$(@:.o=.d)" -MT"$@" $<
$(PROFILE_DIR)/%.o: $(SOURCE_DIR)/%.cpp | $(PROFILE_DIR)
+ @mkdir -p "$(@D)"
@echo compile: $@
@$(CXX) -c $(CPPFLAGS) $(CXXFLAGS) $(PROFILE_FLAGS) -o $@ -MMD -MP -MF"$(@:.o=.d)" -MT"$@" $<
this could be solved by using a pre-interpolated light texture and
mapping light levels to coordinates on that
+ there seems to be a bug with adding/removing obstacles...again
+
also: how could block light affect entity lighting?
+ maybe get the interpolated light level at the entity's center and use
+ that as the light power for the directional lighting shader and use a
+ direction that's fixed relative to the camera?
gravity
maybe like light levels? should also store a direction with it in
that case. also, global gravity may be a world option.
+ no, per-block gravity vector is most probably too expensive.
+ better have the chunks store a few point masses (maybe blocks that
+ emit gravitation?) and calculate from that
block attributes
maybe the chunk loader should keep an index of interesting, if not
all chunks by position, possibly base-relative
+ profiling indicates that this is not neccessary atm. maybe it will
+ when some more action is in the world
+
transparency (blocks and entities)
transparent blocks because awesome
+++ /dev/null
-#include "app.hpp"
-
-#include <iostream>
-#include <stdexcept>
-
-
-namespace blank {
-
-Application::Application(const Config &config)
-: init_sdl()
-, init_img()
-, init_gl(config.doublebuf, config.multisampling)
-, window()
-, ctx(window.CreateContext())
-, init_glew()
-, chunk_prog()
-, entity_prog()
-, cam()
-, world(config.world)
-, interface(config.interface, world)
-, test_controller(MakeTestEntity(world))
-, running(false) {
- if (config.vsync) {
- GLContext::EnableVSync();
- }
-
- glClearColor(0.0, 0.0, 0.0, 1.0);
-}
-
-Entity &Application::MakeTestEntity(World &world) {
- Entity &e = world.AddEntity();
- e.Position({ 0.0f, 0.0f, 0.0f });
- e.SetShape(world.BlockTypes()[1].shape, { 1.0f, 1.0f, 0.0f });
- e.AngularVelocity(glm::quat(glm::vec3{ 0.00001f, 0.000006f, 0.000013f }));
- return e;
-}
-
-
-void Application::RunN(size_t n) {
- Uint32 last = SDL_GetTicks();
- for (size_t i = 0; i < n; ++i) {
- Uint32 now = SDL_GetTicks();
- int delta = now - last;
- Loop(delta);
- last = now;
- }
-}
-
-void Application::RunT(size_t t) {
- Uint32 last = SDL_GetTicks();
- Uint32 finish = last + t;
- while (last < finish) {
- Uint32 now = SDL_GetTicks();
- int delta = now - last;
- Loop(delta);
- last = now;
- }
-}
-
-void Application::RunS(size_t n, size_t t) {
- for (size_t i = 0; i < n; ++i) {
- Loop(t);
- }
-}
-
-
-void Application::Run() {
- running = true;
- Uint32 last = SDL_GetTicks();
- window.GrabMouse();
- while (running) {
- Uint32 now = SDL_GetTicks();
- int delta = now - last;
- Loop(delta);
- last = now;
- }
-}
-
-void Application::Loop(int dt) {
- HandleEvents();
- Update(dt);
- Render();
-}
-
-
-void Application::HandleEvents() {
- SDL_Event event;
- while (SDL_PollEvent(&event)) {
- switch (event.type) {
- case SDL_KEYDOWN:
- interface.HandlePress(event.key);
- break;
- case SDL_KEYUP:
- interface.HandleRelease(event.key);
- break;
- case SDL_MOUSEBUTTONDOWN:
- interface.HandlePress(event.button);
- break;
- case SDL_MOUSEBUTTONUP:
- interface.HandleRelease(event.button);
- break;
- case SDL_MOUSEMOTION:
- interface.Handle(event.motion);
- break;
- case SDL_MOUSEWHEEL:
- interface.Handle(event.wheel);
- break;
- case SDL_QUIT:
- running = false;
- break;
- case SDL_WINDOWEVENT:
- switch (event.window.event) {
- case SDL_WINDOWEVENT_FOCUS_GAINED:
- window.GrabMouse();
- break;
- case SDL_WINDOWEVENT_FOCUS_LOST:
- window.ReleaseMouse();
- break;
- case SDL_WINDOWEVENT_RESIZED:
- cam.Viewport(event.window.data1, event.window.data2);
- interface.Handle(event.window);
- break;
- default:
- interface.Handle(event.window);
- break;
- }
- break;
- default:
- break;
- }
- }
-}
-
-void Application::Update(int dt) {
- interface.Update(dt);
- test_controller.Update(dt);
- world.Update(dt);
-}
-
-void Application::Render() {
- GLContext::Clear();
-
- chunk_prog.SetProjection(cam.Projection());
- entity_prog.SetProjection(cam.Projection());
-
- world.Render(chunk_prog, entity_prog);
-
- interface.Render(entity_prog);
-
- window.Flip();
-}
-
-}
+++ /dev/null
-#ifndef BLANK_APP_HPP_
-#define BLANK_APP_HPP_
-
-#include "camera.hpp"
-#include "controller.hpp"
-#include "init.hpp"
-#include "interface.hpp"
-#include "shader.hpp"
-#include "world.hpp"
-
-
-namespace blank {
-
-class Application {
-
-public:
- struct Config {
- bool vsync = true;
- bool doublebuf = true;
- int multisampling = 1;
-
- Interface::Config interface = Interface::Config();
- World::Config world = World::Config();
- };
-
- explicit Application(const Config &);
-
- Application(const Application &) = delete;
- Application &operator =(const Application &) = delete;
-
- /// run until user quits
- void Run();
- void Loop(int dt);
-
- /// run for n frames
- void RunN(size_t n);
- /// run for t milliseconds
- void RunT(size_t t);
- /// run for n frames, assuming t milliseconds for each
- void RunS(size_t n, size_t t);
-
- void HandleEvents();
- void Update(int dt);
- void Render();
-
- static Entity &MakeTestEntity(World &);
-
-private:
- InitSDL init_sdl;
- InitIMG init_img;
- InitGL init_gl;
- Window window;
- GLContext ctx;
- InitGLEW init_glew;
- BlockLighting chunk_prog;
- DirectionalLighting entity_prog;
-
- Camera cam;
- World world;
- Interface interface;
-
- RandomWalk test_controller;
-
- bool running;
-
-};
-
-}
-
-#endif
--- /dev/null
+#include "Application.hpp"
+
+#include "../world/BlockType.hpp"
+#include "../world/Entity.hpp"
+
+#include <iostream>
+#include <stdexcept>
+
+
+namespace blank {
+
+Application::Application(const Config &config)
+: init_sdl()
+, init_img()
+, init_gl(config.doublebuf, config.multisampling)
+, window()
+, ctx(window.CreateContext())
+, init_glew()
+, chunk_prog()
+, entity_prog()
+, cam()
+, world(config.world)
+, interface(config.interface, world)
+, test_controller(MakeTestEntity(world))
+, running(false) {
+ if (config.vsync) {
+ GLContext::EnableVSync();
+ }
+
+ glClearColor(0.0, 0.0, 0.0, 1.0);
+}
+
+Entity &Application::MakeTestEntity(World &world) {
+ Entity &e = world.AddEntity();
+ e.Position({ 0.0f, 0.0f, 0.0f });
+ e.SetShape(world.BlockTypes()[1].shape, { 1.0f, 1.0f, 0.0f });
+ e.AngularVelocity(glm::quat(glm::vec3{ 0.00001f, 0.000006f, 0.000013f }));
+ return e;
+}
+
+
+void Application::RunN(size_t n) {
+ Uint32 last = SDL_GetTicks();
+ for (size_t i = 0; i < n; ++i) {
+ Uint32 now = SDL_GetTicks();
+ int delta = now - last;
+ Loop(delta);
+ last = now;
+ }
+}
+
+void Application::RunT(size_t t) {
+ Uint32 last = SDL_GetTicks();
+ Uint32 finish = last + t;
+ while (last < finish) {
+ Uint32 now = SDL_GetTicks();
+ int delta = now - last;
+ Loop(delta);
+ last = now;
+ }
+}
+
+void Application::RunS(size_t n, size_t t) {
+ for (size_t i = 0; i < n; ++i) {
+ Loop(t);
+ }
+}
+
+
+void Application::Run() {
+ running = true;
+ Uint32 last = SDL_GetTicks();
+ window.GrabMouse();
+ while (running) {
+ Uint32 now = SDL_GetTicks();
+ int delta = now - last;
+ Loop(delta);
+ last = now;
+ }
+}
+
+void Application::Loop(int dt) {
+ HandleEvents();
+ Update(dt);
+ Render();
+}
+
+
+void Application::HandleEvents() {
+ SDL_Event event;
+ while (SDL_PollEvent(&event)) {
+ switch (event.type) {
+ case SDL_KEYDOWN:
+ interface.HandlePress(event.key);
+ break;
+ case SDL_KEYUP:
+ interface.HandleRelease(event.key);
+ break;
+ case SDL_MOUSEBUTTONDOWN:
+ interface.HandlePress(event.button);
+ break;
+ case SDL_MOUSEBUTTONUP:
+ interface.HandleRelease(event.button);
+ break;
+ case SDL_MOUSEMOTION:
+ interface.Handle(event.motion);
+ break;
+ case SDL_MOUSEWHEEL:
+ interface.Handle(event.wheel);
+ break;
+ case SDL_QUIT:
+ running = false;
+ break;
+ case SDL_WINDOWEVENT:
+ switch (event.window.event) {
+ case SDL_WINDOWEVENT_FOCUS_GAINED:
+ window.GrabMouse();
+ break;
+ case SDL_WINDOWEVENT_FOCUS_LOST:
+ window.ReleaseMouse();
+ break;
+ case SDL_WINDOWEVENT_RESIZED:
+ cam.Viewport(event.window.data1, event.window.data2);
+ interface.Handle(event.window);
+ break;
+ default:
+ interface.Handle(event.window);
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void Application::Update(int dt) {
+ interface.Update(dt);
+ test_controller.Update(dt);
+ world.Update(dt);
+}
+
+void Application::Render() {
+ GLContext::Clear();
+
+ chunk_prog.SetProjection(cam.Projection());
+ entity_prog.SetProjection(cam.Projection());
+
+ world.Render(chunk_prog, entity_prog);
+
+ interface.Render(entity_prog);
+
+ window.Flip();
+}
+
+}
--- /dev/null
+#ifndef BLANK_APP_APPLICATION_HPP_
+#define BLANK_APP_APPLICATION_HPP_
+
+#include "init.hpp"
+#include "RandomWalk.hpp"
+#include "../graphics/BlockLighting.hpp"
+#include "../graphics/Camera.hpp"
+#include "../graphics/DirectionalLighting.hpp"
+#include "../ui/Interface.hpp"
+#include "../world/World.hpp"
+
+
+namespace blank {
+
+class Application {
+
+public:
+ struct Config {
+ bool vsync = true;
+ bool doublebuf = true;
+ int multisampling = 1;
+
+ Interface::Config interface = Interface::Config();
+ World::Config world = World::Config();
+ };
+
+ explicit Application(const Config &);
+
+ Application(const Application &) = delete;
+ Application &operator =(const Application &) = delete;
+
+ /// run until user quits
+ void Run();
+ void Loop(int dt);
+
+ /// run for n frames
+ void RunN(size_t n);
+ /// run for t milliseconds
+ void RunT(size_t t);
+ /// run for n frames, assuming t milliseconds for each
+ void RunS(size_t n, size_t t);
+
+ void HandleEvents();
+ void Update(int dt);
+ void Render();
+
+ static Entity &MakeTestEntity(World &);
+
+private:
+ InitSDL init_sdl;
+ InitIMG init_img;
+ InitGL init_gl;
+ Window window;
+ GLContext ctx;
+ InitGLEW init_glew;
+ BlockLighting chunk_prog;
+ DirectionalLighting entity_prog;
+
+ Camera cam;
+ World world;
+ Interface interface;
+
+ RandomWalk test_controller;
+
+ bool running;
+
+};
+
+}
+
+#endif
--- /dev/null
+#ifndef BLANK_APP_FPSCONTROLLER_HPP_
+#define BLANK_APP_FPSCONTROLLER_HPP_
+
+#include "../model/geometry.hpp"
+#include "../world/Entity.hpp"
+
+#include <glm/glm.hpp>
+
+
+namespace blank {
+
+class FPSController {
+
+public:
+ explicit FPSController(Entity &) noexcept;
+
+ Ray Aim() const noexcept { return entity.Aim(entity.ChunkCoords()); }
+
+ const glm::vec3 &Velocity() const noexcept { return velocity; }
+ void Velocity(const glm::vec3 &vel) noexcept { velocity = vel; }
+
+ // all angles in radians (full circle = 2Ï€)
+ float Pitch() const noexcept { return pitch; }
+ void Pitch(float p) noexcept;
+ void RotatePitch(float delta) noexcept;
+ float Yaw() const noexcept { return yaw; }
+ void Yaw(float y) noexcept;
+ void RotateYaw(float delta) noexcept;
+
+ void Update(int dt) noexcept;
+
+private:
+ Entity &entity;
+
+ glm::vec3 velocity;
+
+ float pitch;
+ float yaw;
+
+};
+
+}
+
+#endif
--- /dev/null
+#ifndef BLANK_APP_INTERVALTIMER_HPP
+#define BLANK_APP_INTERVALTIMER_HPP
+
+
+namespace blank {
+
+class IntervalTimer {
+
+public:
+ explicit IntervalTimer(int interval_ms) noexcept
+ : intv(interval_ms) { }
+
+ void Start() noexcept {
+ speed = 1;
+ }
+ void Stop() noexcept {
+ value = 0;
+ speed = 0;
+ }
+
+ bool Running() const noexcept {
+ return speed != 0;
+ }
+ bool Hit() const noexcept {
+ return Running() && value % intv < last_dt;
+ }
+ int Elapsed() const noexcept {
+ return value;
+ }
+ int Iteration() const noexcept {
+ return value / intv;
+ }
+
+ void Update(int dt) noexcept {
+ value += dt * speed;
+ last_dt = dt;
+ }
+
+private:
+ int intv;
+ int value = 0;
+ int speed = 0;
+ int last_dt = 0;
+
+};
+
+}
+
+#endif
--- /dev/null
+#ifndef BLANK_APP_RANDOMWALK_HPP_
+#define BLANK_APP_RANDOMWALK_HPP_
+
+#include <glm/glm.hpp>
+
+
+namespace blank {
+
+class Entity;
+
+class RandomWalk {
+
+public:
+ explicit RandomWalk(Entity &) noexcept;
+
+ void Update(int dt) noexcept;
+
+private:
+ Entity &entity;
+
+ int time_left;
+
+};
+
+}
+
+#endif
--- /dev/null
+#include "Runtime.hpp"
+
+#include <cctype>
+#include <cstdlib>
+#include <iostream>
+
+using namespace std;
+
+
+namespace blank {
+
+Runtime::Runtime() noexcept
+: name("blank")
+, mode(NORMAL)
+, n(0)
+, t(0)
+, config() {
+
+}
+
+
+void Runtime::ReadArgs(int argc, const char *const *argv) {
+ if (argc <= 0) return;
+ name = argv[0];
+
+ bool options = true;
+ bool error = false;
+
+ for (int i = 1; i < argc; ++i) {
+ const char *arg = argv[i];
+ if (!arg || arg[0] == '\0') {
+ cerr << "warning: found empty argument at position " << i << endl;
+ continue;
+ }
+ if (options && arg[0] == '-') {
+ if (arg[1] == '\0') {
+ cerr << "warning: incomplete option list at position " << i << endl;
+ } else if (arg[1] == '-') {
+ if (arg[2] == '\0') {
+ // stopper
+ options = false;
+ } else {
+ // long option
+ if (strcmp(arg + 2, "no-vsync") == 0) {
+ config.vsync = false;
+ } else if (strcmp(arg + 2, "no-keyboard") == 0) {
+ config.interface.keyboard_disabled = true;
+ } else if (strcmp(arg + 2, "no-mouse") == 0) {
+ config.interface.mouse_disabled = true;
+ } else if (strcmp(arg + 2, "no-hud") == 0) {
+ config.interface.visual_disabled = true;
+ } else {
+ cerr << "unknown option " << arg << endl;
+ error = true;
+ }
+ }
+ } else {
+ // short options
+ for (int j = 1; arg[j] != '\0'; ++j) {
+ switch (arg[j]) {
+ case 'd':
+ config.doublebuf = false;
+ break;
+ case 'm':
+ ++i;
+ if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
+ cerr << "missing argument to -m" << endl;
+ error = true;
+ } else {
+ config.multisampling = strtoul(argv[i], nullptr, 10);
+ }
+ break;
+ case 'n':
+ ++i;
+ if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
+ cerr << "missing argument to -n" << endl;
+ error = true;
+ } else {
+ n = strtoul(argv[i], nullptr, 10);
+ }
+ break;
+ case 's':
+ ++i;
+ if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
+ cerr << "missing argument to -s" << endl;
+ error = true;
+ } else {
+ config.world.gen.solid_seed = strtoul(argv[i], nullptr, 10);
+ config.world.gen.type_seed = config.world.gen.solid_seed;
+ }
+ break;
+ case 't':
+ ++i;
+ if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
+ cerr << "missing argument to -t" << endl;
+ error = true;
+ } else {
+ t = strtoul(argv[i], nullptr, 10);
+ }
+ break;
+ case '-':
+ // stopper
+ options = false;
+ break;
+ default:
+ cerr << "unknown option " << arg[j] << endl;
+ error = true;
+ break;
+ }
+ }
+ }
+ } else if (isdigit(arg[0])) {
+ // positional number interpreted as -n
+ n = strtoul(arg, nullptr, 10);
+ } else {
+ cerr << "unable to interpret argument "
+ << i << " (" << arg << ")" << endl;
+ error = true;
+ }
+ }
+
+ if (error) {
+ mode = ERROR;
+ return;
+ }
+
+ if (n > 0) {
+ if (t > 0) {
+ mode = FIXED_FRAME_LIMIT;
+ } else {
+ mode = FRAME_LIMIT;
+ }
+ } else if (t > 0) {
+ mode = TIME_LIMIT;
+ } else {
+ mode = NORMAL;
+ }
+}
+
+int Runtime::Execute() {
+ if (mode == ERROR) {
+ return 1;
+ }
+
+ Application app(config);
+ switch (mode) {
+ default:
+ case NORMAL:
+ app.Run();
+ break;
+ case FRAME_LIMIT:
+ app.RunN(n);
+ break;
+ case TIME_LIMIT:
+ app.RunT(t);
+ break;
+ case FIXED_FRAME_LIMIT:
+ app.RunS(n, t);
+ break;
+ }
+ return 0;
+}
+
+}
--- /dev/null
+#ifndef BLANK_RUNTIME_HPP_
+#define BLANK_RUNTIME_HPP_
+
+#include "Application.hpp"
+
+#include <cstddef>
+
+
+namespace blank {
+
+class Runtime {
+
+public:
+ enum Mode {
+ NORMAL,
+ FRAME_LIMIT,
+ TIME_LIMIT,
+ FIXED_FRAME_LIMIT,
+ ERROR,
+ };
+
+ Runtime() noexcept;
+
+ void ReadArgs(int argc, const char *const *argv);
+
+ int Execute();
+
+private:
+ const char *name;
+ Mode mode;
+ std::size_t n;
+ std::size_t t;
+ Application::Config config;
+
+};
+
+}
+
+#endif
--- /dev/null
+#include "FPSController.hpp"
+#include "RandomWalk.hpp"
+
+#include <glm/gtx/euler_angles.hpp>
+#include <glm/gtx/rotate_vector.hpp>
+
+
+namespace blank {
+
+FPSController::FPSController(Entity &entity) noexcept
+: entity(entity)
+, pitch(0)
+, yaw(0) {
+
+}
+
+
+void FPSController::Pitch(float p) noexcept {
+ pitch = p;
+ if (pitch > PI / 2) {
+ pitch = PI / 2;
+ } else if (pitch < -PI / 2) {
+ pitch = -PI / 2;
+ }
+}
+
+void FPSController::RotatePitch(float delta) noexcept {
+ Pitch(pitch + delta);
+}
+
+void FPSController::Yaw(float y) noexcept {
+ yaw = y;
+ if (yaw > PI) {
+ yaw -= PI * 2;
+ } else if (yaw < -PI) {
+ yaw += PI * 2;
+ }
+}
+
+void FPSController::RotateYaw(float delta) noexcept {
+ Yaw(yaw + delta);
+}
+
+
+void FPSController::Update(int dt) noexcept {
+ entity.Rotation(glm::eulerAngleYX(yaw, pitch));
+ entity.Velocity(glm::rotateY(velocity, yaw));
+}
+
+
+RandomWalk::RandomWalk(Entity &e) noexcept
+: entity(e)
+, time_left(0) {
+
+}
+
+
+void RandomWalk::Update(int dt) noexcept {
+ time_left -= dt;
+ if (time_left > 0) return;
+ time_left += 2500 + (rand() % 5000);
+
+ constexpr float move_vel = 0.0005f;
+
+ glm::vec3 new_vel = entity.Velocity();
+
+ switch (rand() % 9) {
+ case 0:
+ new_vel.x = -move_vel;
+ break;
+ case 1:
+ new_vel.x = 0.0f;
+ break;
+ case 2:
+ new_vel.x = move_vel;
+ break;
+ case 3:
+ new_vel.y = -move_vel;
+ break;
+ case 4:
+ new_vel.y = 0.0f;
+ break;
+ case 5:
+ new_vel.y = move_vel;
+ break;
+ case 6:
+ new_vel.z = -move_vel;
+ break;
+ case 7:
+ new_vel.z = 0.0f;
+ break;
+ case 8:
+ new_vel.z = move_vel;
+ break;
+ }
+
+ entity.Velocity(new_vel);
+}
+
+}
--- /dev/null
+#include "init.hpp"
+
+#include <algorithm>
+#include <SDL.h>
+#include <SDL_image.h>
+#include <stdexcept>
+#include <string>
+#include <GL/glew.h>
+
+
+namespace {
+
+void sdl_error(std::string msg) {
+ const char *error = SDL_GetError();
+ if (*error != '\0') {
+ msg += ": ";
+ msg += error;
+ SDL_ClearError();
+ }
+ throw std::runtime_error(msg);
+}
+
+}
+
+namespace blank {
+
+InitSDL::InitSDL() {
+ if (SDL_Init(SDL_INIT_VIDEO) != 0) {
+ sdl_error("SDL_Init(SDL_INIT_VIDEO)");
+ }
+}
+
+InitSDL::~InitSDL() {
+ SDL_Quit();
+}
+
+
+InitIMG::InitIMG() {
+ if (IMG_Init(IMG_INIT_PNG) == 0) {
+ sdl_error("IMG_Init(IMG_INIT_PNG)");
+ }
+}
+
+InitIMG::~InitIMG() {
+ IMG_Quit();
+}
+
+
+InitGL::InitGL(bool double_buffer, int sample_size) {
+ if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3) != 0) {
+ sdl_error("SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3)");
+ }
+ if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3) != 0) {
+ sdl_error("SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3)");
+ }
+ if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE) != 0) {
+ sdl_error("SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE)");
+ }
+
+ if (double_buffer) {
+ if (SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1) != 0) {
+ sdl_error("SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1)");
+ }
+ }
+
+ if (sample_size > 1) {
+ if (SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1) != 0) {
+ sdl_error("SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS)");
+ }
+ if (SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, sample_size) != 0) {
+ sdl_error("SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES)");
+ }
+ }
+}
+
+
+Window::Window()
+: handle(SDL_CreateWindow(
+ "blank",
+ SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
+ 960, 600,
+ SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE
+)) {
+ if (!handle) {
+ sdl_error("SDL_CreateWindow");
+ }
+}
+
+Window::~Window() {
+ SDL_DestroyWindow(handle);
+}
+
+void Window::GrabInput() {
+ SDL_SetWindowGrab(handle, SDL_TRUE);
+}
+
+void Window::ReleaseInput() {
+ SDL_SetWindowGrab(handle, SDL_FALSE);
+}
+
+void Window::GrabMouse() {
+ if (SDL_SetRelativeMouseMode(SDL_TRUE) != 0) {
+ sdl_error("SDL_SetRelativeMouseMode");
+ }
+}
+
+void Window::ReleaseMouse() {
+ if (SDL_SetRelativeMouseMode(SDL_FALSE) != 0) {
+ sdl_error("SDL_SetRelativeMouseMode");
+ }
+}
+
+GLContext Window::CreateContext() {
+ return GLContext(handle);
+}
+
+void Window::Flip() {
+ SDL_GL_SwapWindow(handle);
+}
+
+
+GLContext::GLContext(SDL_Window *win)
+: handle(SDL_GL_CreateContext(win)) {
+ if (!handle) {
+ sdl_error("SDL_GL_CreateContext");
+ }
+}
+
+GLContext::~GLContext() {
+ if (handle) {
+ SDL_GL_DeleteContext(handle);
+ }
+}
+
+
+GLContext::GLContext(GLContext &&other)
+: handle(other.handle) {
+ other.handle = nullptr;
+}
+
+GLContext &GLContext::operator =(GLContext &&other) {
+ std::swap(handle, other.handle);
+ return *this;
+}
+
+void GLContext::EnableVSync() {
+ if (SDL_GL_SetSwapInterval(1) != 0) {
+ sdl_error("SDL_GL_SetSwapInterval");
+ }
+}
+
+void GLContext::EnableDepthTest() noexcept {
+ glEnable(GL_DEPTH_TEST);
+ glDepthFunc(GL_LESS);
+}
+
+void GLContext::EnableBackfaceCulling() noexcept {
+ glEnable(GL_CULL_FACE);
+}
+
+void GLContext::Clear() noexcept {
+ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+}
+
+void GLContext::ClearDepthBuffer() noexcept {
+ glClear(GL_DEPTH_BUFFER_BIT);
+}
+
+
+InitGLEW::InitGLEW() {
+ glewExperimental = GL_TRUE;
+ GLenum glew_err = glewInit();
+ if (glew_err != GLEW_OK) {
+ std::string msg("glewInit: ");
+ const GLubyte *errBegin = glewGetErrorString(glew_err);
+ const GLubyte *errEnd = errBegin;
+ while (*errEnd != '\0') {
+ ++errEnd;
+ }
+ msg.append(errBegin, errEnd);
+ throw std::runtime_error(msg);
+ }
+}
+
+}
--- /dev/null
+#ifndef BLANK_APP_INIT_HPP_
+#define BLANK_APP_INIT_HPP_
+
+#include <SDL.h>
+
+
+namespace blank {
+
+class GLContext;
+
+
+class InitSDL {
+
+public:
+ InitSDL();
+ ~InitSDL();
+
+ InitSDL(const InitSDL &) = delete;
+ InitSDL &operator =(const InitSDL &) = delete;
+
+};
+
+
+class InitIMG {
+
+public:
+ InitIMG();
+ ~InitIMG();
+
+ InitIMG(const InitIMG &) = delete;
+ InitIMG &operator =(const InitIMG &) = delete;
+
+};
+
+
+class InitGL {
+
+public:
+ explicit InitGL(bool double_buffer = true, int sample_size = 1);
+
+ InitGL(const InitGL &) = delete;
+ InitGL &operator =(const InitGL &) = delete;
+
+};
+
+
+class Window {
+
+public:
+ Window();
+ ~Window();
+
+ Window(const Window &) = delete;
+ Window &operator =(const Window &) = delete;
+
+ void GrabInput();
+ void ReleaseInput();
+
+ void GrabMouse();
+ void ReleaseMouse();
+
+ GLContext CreateContext();
+
+ void Flip();
+
+private:
+ SDL_Window *handle;
+
+};
+
+
+class GLContext {
+
+public:
+ explicit GLContext(SDL_Window *);
+ ~GLContext();
+
+ GLContext(GLContext &&);
+ GLContext &operator =(GLContext &&);
+
+ GLContext(const GLContext &) = delete;
+ GLContext &operator =(const GLContext &) = delete;
+
+ static void EnableVSync();
+ static void EnableDepthTest() noexcept;
+ static void EnableBackfaceCulling() noexcept;
+
+ static void Clear() noexcept;
+ static void ClearDepthBuffer() noexcept;
+
+private:
+ SDL_GLContext handle;
+
+};
+
+
+class InitGLEW {
+
+public:
+ InitGLEW();
+
+ InitGLEW(const InitGLEW &) = delete;
+ InitGLEW &operator =(const InitGLEW &) = delete;
+
+};
+
+}
+
+#endif
--- /dev/null
+#include "app/Runtime.hpp"
+
+using namespace blank;
+
+int main(int argc, char *argv[]) {
+ Runtime rt;
+ rt.ReadArgs(argc, argv);
+ return rt.Execute();
+}
+++ /dev/null
-#include "block.hpp"
-
-#include "geometry.hpp"
-
-#include <glm/gtx/euler_angles.hpp>
-#include <glm/gtx/transform.hpp>
-
-
-namespace blank {
-
-const NullShape BlockType::DEFAULT_SHAPE;
-
-BlockType::BlockType(bool v, const glm::vec3 &col, const Shape *s) noexcept
-: shape(s)
-, color(col)
-, outline_color(-1, -1, -1)
-, id(0)
-, luminosity(0)
-, visible(v)
-, block_light(false)
-, fill({ false, false, false, false, false, false }) {
-
-}
-
-void BlockType::FillModel(
- Model::Buffer &buf,
- const glm::mat4 &transform,
- Model::Index idx_offset
-) const noexcept {
- shape->Vertices(buf.vertices, buf.normals, buf.indices, transform, idx_offset);
- buf.colors.insert(buf.colors.end(), shape->VertexCount(), color);
-}
-
-void BlockType::FillBlockModel(
- BlockModel::Buffer &buf,
- const glm::mat4 &transform,
- BlockModel::Index idx_offset
-) const noexcept {
- shape->Vertices(buf.vertices, buf.indices, transform, idx_offset);
- buf.colors.insert(buf.colors.end(), shape->VertexCount(), color);
-}
-
-void BlockType::FillOutlineModel(
- OutlineModel &model,
- const glm::vec3 &pos_offset,
- OutlineModel::Index idx_offset
-) const noexcept {
- shape->Outline(model.vertices, model.indices, pos_offset, idx_offset);
- model.colors.insert(model.colors.end(), shape->OutlineCount(), outline_color);
-}
-
-
-BlockTypeRegistry::BlockTypeRegistry() {
- Add(BlockType());
-}
-
-Block::Type BlockTypeRegistry::Add(const BlockType &t) {
- int id = types.size();
- types.push_back(t);
- types.back().id = id;
- return id;
-}
-
-
-const glm::mat4 Block::orient2transform[ORIENT_COUNT] = {
- { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, }, // face: up, turn: none
- { 0, 0, -1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, }, // face: up, turn: left
- { -1, 0, 0, 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, }, // face: up, turn: around
- { 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0, 1, }, // face: up, turn: right
- { 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, }, // face: down, turn: none
- { 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 0, 0, 0, 0, 1, }, // face: down, turn: left
- { -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, }, // face: down, turn: around
- { 0, 0, 1, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, }, // face: down, turn: right
- { 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, }, // face: right, turn: none
- { 0, -1, 0, 0, 0, 0, -1, 0, 1, 0, 0, 0, 0, 0, 0, 1, }, // face: right, turn: left
- { 0, -1, 0, 0, -1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, }, // face: right, turn: around
- { 0, -1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0, 0, 0, 1, }, // face: right, turn: right
- { 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, }, // face: left, turn: none
- { 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, }, // face: left, turn: left
- { 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, }, // face: left, turn: around
- { 0, 1, 0, 0, 0, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, 1, }, // face: left, turn: right
- { 1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, }, // face: front, turn: none
- { 0, 0, -1, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 1, }, // face: front, turn: left
- { -1, 0, 0, 0, 0, 0, -1, 0, 0, -1, 0, 0, 0, 0, 0, 1, }, // face: front, turn: around
- { 0, 0, 1, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 1, }, // face: front, turn: right
- { 1, 0, 0, 0, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1, }, // face: back, turn: none
- { 0, 0, -1, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, }, // face: back, turn: left
- { -1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, }, // face: back, turn: around
- { 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, }, // face: back, turn: right
-};
-
-const glm::tvec3<int> Block::face2normal[FACE_COUNT] = {
- { 0, 1, 0 },
- { 0, -1, 0 },
- { 1, 0, 0 },
- { -1, 0, 0 },
- { 0, 0, 1 },
- { 0, 0, -1 },
-};
-
-const Block::Face Block::orient2face[ORIENT_COUNT][FACE_COUNT] = {
- { FACE_UP, FACE_DOWN, FACE_RIGHT, FACE_LEFT, FACE_FRONT, FACE_BACK, }, // face: up, turn: none
- { FACE_UP, FACE_DOWN, FACE_FRONT, FACE_BACK, FACE_LEFT, FACE_RIGHT, }, // face: up, turn: left
- { FACE_UP, FACE_DOWN, FACE_LEFT, FACE_RIGHT, FACE_BACK, FACE_FRONT, }, // face: up, turn: around
- { FACE_UP, FACE_DOWN, FACE_BACK, FACE_FRONT, FACE_RIGHT, FACE_LEFT, }, // face: up, turn: right
- { FACE_DOWN, FACE_UP, FACE_RIGHT, FACE_LEFT, FACE_BACK, FACE_FRONT, }, // face: down, turn: none
- { FACE_DOWN, FACE_UP, FACE_BACK, FACE_FRONT, FACE_LEFT, FACE_RIGHT, }, // face: down, turn: left
- { FACE_DOWN, FACE_UP, FACE_LEFT, FACE_RIGHT, FACE_FRONT, FACE_BACK, }, // face: down, turn: around
- { FACE_DOWN, FACE_UP, FACE_FRONT, FACE_BACK, FACE_RIGHT, FACE_LEFT, }, // face: down, turn: right
- { FACE_LEFT, FACE_RIGHT, FACE_UP, FACE_DOWN, FACE_FRONT, FACE_BACK, }, // face: right, turn: none
- { FACE_LEFT, FACE_RIGHT, FACE_FRONT, FACE_BACK, FACE_DOWN, FACE_UP, }, // face: right, turn: left
- { FACE_LEFT, FACE_RIGHT, FACE_DOWN, FACE_UP, FACE_BACK, FACE_FRONT, }, // face: right, turn: around
- { FACE_LEFT, FACE_RIGHT, FACE_BACK, FACE_FRONT, FACE_UP, FACE_DOWN, }, // face: right, turn: right
- { FACE_RIGHT, FACE_LEFT, FACE_DOWN, FACE_UP, FACE_FRONT, FACE_BACK, }, // face: left, turn: none
- { FACE_RIGHT, FACE_LEFT, FACE_FRONT, FACE_BACK, FACE_UP, FACE_DOWN, }, // face: left, turn: left
- { FACE_RIGHT, FACE_LEFT, FACE_UP, FACE_DOWN, FACE_BACK, FACE_FRONT, }, // face: left, turn: around
- { FACE_RIGHT, FACE_LEFT, FACE_BACK, FACE_FRONT, FACE_DOWN, FACE_UP, }, // face: left, turn: right
- { FACE_BACK, FACE_FRONT, FACE_RIGHT, FACE_LEFT, FACE_UP, FACE_DOWN, }, // face: front, turn: none
- { FACE_BACK, FACE_FRONT, FACE_UP, FACE_DOWN, FACE_LEFT, FACE_RIGHT, }, // face: front, turn: left
- { FACE_BACK, FACE_FRONT, FACE_LEFT, FACE_RIGHT, FACE_DOWN, FACE_UP, }, // face: front, turn: around
- { FACE_BACK, FACE_FRONT, FACE_DOWN, FACE_UP, FACE_RIGHT, FACE_LEFT, }, // face: front, turn: right
- { FACE_FRONT, FACE_BACK, FACE_RIGHT, FACE_LEFT, FACE_DOWN, FACE_UP, }, // face: back, turn: none
- { FACE_FRONT, FACE_BACK, FACE_DOWN, FACE_UP, FACE_LEFT, FACE_RIGHT, }, // face: back, turn: left
- { FACE_FRONT, FACE_BACK, FACE_LEFT, FACE_RIGHT, FACE_UP, FACE_DOWN, }, // face: back, turn: around
- { FACE_FRONT, FACE_BACK, FACE_UP, FACE_DOWN, FACE_RIGHT, FACE_LEFT, }, // face: back, turn: right
-};
-
-}
+++ /dev/null
-#ifndef BLANK_BLOCK_HPP_
-#define BLANK_BLOCK_HPP_
-
-#include "geometry.hpp"
-#include "model.hpp"
-#include "shape.hpp"
-
-#include <vector>
-#include <glm/glm.hpp>
-
-
-namespace blank {
-
-/// single 1x1x1 cube
-struct Block {
-
- using Type = unsigned short;
- using Pos = glm::vec3;
-
- enum Face {
- FACE_UP,
- FACE_DOWN,
- FACE_RIGHT,
- FACE_LEFT,
- FACE_FRONT,
- FACE_BACK,
- FACE_COUNT,
- };
- enum Turn {
- TURN_NONE,
- TURN_LEFT,
- TURN_AROUND,
- TURN_RIGHT,
- TURN_COUNT,
- };
-
- static constexpr int ORIENT_COUNT = FACE_COUNT * TURN_COUNT;
-
- Type type;
- unsigned char orient;
-
- constexpr explicit Block(Type type = 0, Face face = FACE_UP, Turn turn = TURN_NONE) noexcept
- : type(type), orient(face * TURN_COUNT + turn) { }
-
- const glm::mat4 &Transform() const noexcept { return orient2transform[orient]; }
-
- Face GetFace() const noexcept { return Face(orient / TURN_COUNT); }
- void SetFace(Face face) noexcept { orient = face * TURN_COUNT + GetTurn(); }
- Turn GetTurn() const noexcept { return Turn(orient % TURN_COUNT); }
- void SetTurn(Turn turn) noexcept { orient = GetFace() * TURN_COUNT + turn; }
-
- Face OrientedFace(Face f) const noexcept { return orient2face[orient][f]; }
-
- static Face Opposite(Face f) noexcept {
- return Face(f ^ 1);
- }
-
- static int Axis(Face f) noexcept {
- switch (f) {
- case FACE_UP:
- case FACE_DOWN:
- return 1;
- default:
- case FACE_RIGHT:
- case FACE_LEFT:
- return 0;
- case FACE_FRONT:
- case FACE_BACK:
- return 2;
- }
- }
-
- static glm::tvec3<int> FaceNormal(Face face) noexcept {
- return face2normal[face];
- }
-
- static Face NormalFace(const glm::vec3 &norm) noexcept {
- const glm::vec3 anorm(abs(norm));
- if (anorm.x > anorm.y) {
- if (anorm.x > anorm.z) {
- return norm.x > 0.0f ? FACE_RIGHT : FACE_LEFT;
- } else {
- return norm.z > 0.0f ? FACE_FRONT : FACE_BACK;
- }
- } else {
- if (anorm.y > anorm.z) {
- return norm.y > 0.0f ? FACE_UP : FACE_DOWN;
- } else {
- return norm.z > 0.0f ? FACE_FRONT : FACE_BACK;
- }
- }
- }
-
- struct FaceSet {
-
- explicit FaceSet(unsigned char v = 0)
- : value(v) { }
-
- bool IsSet(Face f) const {
- return value & Mask(f);
- }
- void Set(Face f) {
- value |= Mask(f);
- }
- void Unset(Face f) {
- value |= ~Mask(f);
- }
-
- void Clear() {
- value = 0;
- }
- void Fill() {
- value = Mask(FACE_COUNT) - 1;
- }
-
- bool Empty() const {
- return value == 0;
- }
- bool All() const {
- return value == Mask(FACE_COUNT) - 1;
- }
-
- unsigned char Mask(Face f) const {
- return 1 << f;
- }
-
- unsigned char value;
-
- };
-
-private:
- static const glm::tvec3<int> face2normal[6];
- static const glm::mat4 orient2transform[ORIENT_COUNT];
- static const Face orient2face[ORIENT_COUNT][FACE_COUNT];
-
-};
-
-
-/// attributes of a type of block
-struct BlockType {
-
- const Shape *shape;
- glm::vec3 color;
- glm::vec3 outline_color;
-
- Block::Type id;
-
- int luminosity;
-
- bool visible;
- bool block_light;
-
- struct Faces {
- bool face[Block::FACE_COUNT];
- Faces &operator =(const Faces &other) noexcept {
- for (int i = 0; i < Block::FACE_COUNT; ++i) {
- face[i] = other.face[i];
- }
- return *this;
- }
- bool operator [](Block::Face f) const noexcept {
- return face[f];
- }
- } fill;
-
- explicit BlockType(
- bool v = false,
- const glm::vec3 &color = { 1, 1, 1 },
- const Shape *shape = &DEFAULT_SHAPE
- ) noexcept;
-
- static const NullShape DEFAULT_SHAPE;
-
- bool FaceFilled(const Block &block, Block::Face face) const noexcept {
- return fill[block.OrientedFace(face)];
- }
-
- void FillModel(
- Model::Buffer &m,
- const glm::mat4 &transform = glm::mat4(1.0f),
- Model::Index idx_offset = 0
- ) const noexcept;
- void FillBlockModel(
- BlockModel::Buffer &m,
- const glm::mat4 &transform = glm::mat4(1.0f),
- BlockModel::Index idx_offset = 0
- ) const noexcept;
- void FillOutlineModel(
- OutlineModel &m,
- const glm::vec3 &pos_offset = { 0, 0, 0 },
- OutlineModel::Index idx_offset = 0
- ) const noexcept;
-
-};
-
-
-class BlockTypeRegistry {
-
-public:
- BlockTypeRegistry();
-
-public:
- Block::Type Add(const BlockType &);
-
- size_t Size() const noexcept { return types.size(); }
-
- BlockType &operator [](Block::Type id) { return types[id]; }
- const BlockType &Get(Block::Type id) const { return types[id]; }
-
-private:
- std::vector<BlockType> types;
-
-};
-
-}
-
-#endif
+++ /dev/null
-#include "camera.hpp"
-
-#include "geometry.hpp"
-
-#include <GL/glew.h>
-#include <glm/gtc/matrix_transform.hpp>
-
-
-namespace blank {
-
-Camera::Camera() noexcept
-: fov(PI_0p25)
-, aspect(1.0f)
-, near_clip(0.1f)
-, far_clip(256.0f)
-, projection(glm::perspective(fov, aspect, near_clip, far_clip)) {
-
-}
-
-
-void Camera::Viewport(int width, int height) noexcept {
- Viewport(0, 0, width, height);
-}
-
-void Camera::Viewport(int x, int y, int width, int height) noexcept {
- glViewport(x, y, width, height);
- Aspect(width, height);
-}
-
-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 near, float far) noexcept {
- near_clip = near;
- far_clip = far;
- UpdateProjection();
-}
-
-
-void Camera::UpdateProjection() noexcept {
- projection = glm::perspective(fov, aspect, near_clip, far_clip);
-}
-
-}
+++ /dev/null
-#ifndef BLANK_CAMERA_HPP_
-#define BLANK_CAMERA_HPP_
-
-#include <glm/glm.hpp>
-
-
-namespace blank {
-
-class Camera {
-
-public:
- Camera() noexcept;
-
- void Viewport(int width, int height) noexcept;
- void Viewport(int x, int y, int width, int height) noexcept;
-
- /// FOV in radians
- 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() noexcept { return projection; }
-
-private:
- void UpdateProjection() noexcept;
-
-private:
- float fov;
- float aspect;
- float near_clip;
- float far_clip;
-
- glm::mat4 projection;
-
-};
-
-}
-
-#endif
+++ /dev/null
-#include "chunk.hpp"
-
-#include "generator.hpp"
-
-#include <algorithm>
-#include <limits>
-#include <queue>
-
-
-namespace blank {
-
-constexpr int Chunk::width;
-constexpr int Chunk::height;
-constexpr int Chunk::depth;
-constexpr int Chunk::size;
-
-
-Chunk::Chunk(const BlockTypeRegistry &types) noexcept
-: types(&types)
-, neighbor{0}
-, blocks{}
-, light{0}
-, model()
-, position(0, 0, 0)
-, dirty(false) {
-
-}
-
-Chunk::Chunk(Chunk &&other) noexcept
-: types(other.types)
-, model(std::move(other.model))
-, position(other.position)
-, dirty(other.dirty) {
- std::copy(other.neighbor, other.neighbor + sizeof(neighbor), neighbor);
- std::copy(other.blocks, other.blocks + sizeof(blocks), blocks);
- std::copy(other.light, other.light + sizeof(light), light);
-}
-
-Chunk &Chunk::operator =(Chunk &&other) noexcept {
- types = other.types;
- std::copy(other.neighbor, other.neighbor + sizeof(neighbor), neighbor);
- std::copy(other.blocks, other.blocks + sizeof(blocks), blocks);
- std::copy(other.light, other.light + sizeof(light), light);
- model = std::move(other.model);
- position = other.position;
- dirty = other.dirty;
- return *this;
-}
-
-
-namespace {
-
-struct SetNode {
-
- Chunk *chunk;
- Chunk::Pos pos;
-
- SetNode(Chunk *chunk, Chunk::Pos pos)
- : chunk(chunk), pos(pos) { }
-
- int Get() const noexcept { return chunk->GetLight(pos); }
- void Set(int level) noexcept { chunk->SetLight(pos, level); }
-
- bool HasNext(Block::Face face) noexcept {
- const BlockLookup next(chunk, pos, face);
- return next && !next.GetType().block_light;
- }
- SetNode GetNext(Block::Face face) noexcept {
- const BlockLookup next(chunk, pos, face);
- return SetNode(&next.GetChunk(), next.GetBlockPos());
- }
-
-};
-
-struct UnsetNode
-: public SetNode {
-
- int level;
-
- UnsetNode(Chunk *chunk, Chunk::Pos pos)
- : SetNode(chunk, pos), level(Get()) { }
-
- UnsetNode(const SetNode &set)
- : SetNode(set), level(Get()) { }
-
-
- bool HasNext(Block::Face face) noexcept {
- const BlockLookup next(chunk, pos, face);
- return next;
- }
- UnsetNode GetNext(Block::Face face) noexcept { return UnsetNode(SetNode::GetNext(face)); }
-
-};
-
-std::queue<SetNode> light_queue;
-std::queue<UnsetNode> dark_queue;
-
-void work_light() noexcept {
- while (!light_queue.empty()) {
- SetNode node = light_queue.front();
- light_queue.pop();
-
- int level = node.Get() - 1;
- for (int face = 0; face < Block::FACE_COUNT; ++face) {
- if (node.HasNext(Block::Face(face))) {
- SetNode other = node.GetNext(Block::Face(face));
- if (other.Get() < level) {
- other.Set(level);
- light_queue.emplace(other);
- }
- }
- }
- }
-}
-
-void work_dark() noexcept {
- while (!dark_queue.empty()) {
- UnsetNode node = dark_queue.front();
- dark_queue.pop();
-
- for (int face = 0; face < Block::FACE_COUNT; ++face) {
- if (node.HasNext(Block::Face(face))) {
- UnsetNode other = node.GetNext(Block::Face(face));
- // TODO: if there a light source here with the same level this will err
- if (other.Get() != 0 && other.Get() < node.level) {
- other.Set(0);
- dark_queue.emplace(other);
- } else {
- light_queue.emplace(other);
- }
- }
- }
- }
-}
-
-}
-
-void Chunk::SetBlock(int index, const Block &block) noexcept {
- const BlockType &old_type = Type(blocks[index]);
- const BlockType &new_type = Type(block);
-
- blocks[index] = block;
-
- if (&old_type == &new_type) return;
-
- if (new_type.luminosity > old_type.luminosity) {
- // light added
- SetLight(index, new_type.luminosity);
- light_queue.emplace(this, ToPos(index));
- work_light();
- } else if (new_type.luminosity < old_type.luminosity) {
- // light removed
- dark_queue.emplace(this, ToPos(index));
- SetLight(index, 0);
- work_dark();
- SetLight(index, new_type.luminosity);
- light_queue.emplace(this, ToPos(index));
- work_light();
- } else if (new_type.block_light && !old_type.block_light) {
- // obstacle added
- if (GetLight(index) > 0) {
- dark_queue.emplace(this, ToPos(index));
- SetLight(index, 0);
- work_dark();
- work_light();
- }
- } else if (!new_type.block_light && old_type.block_light) {
- // obstacle removed
- int level = 0;
- for (int face = 0; face < Block::FACE_COUNT; ++face) {
- BlockLookup next_block(this, ToPos(index), Block::Face(face));
- if (next_block) {
- level = std::min(level, next_block.GetLight());
- }
- }
- if (level > 1) {
- SetLight(index, level - 1);
- light_queue.emplace(this, ToPos(index));
- work_light();
- }
- }
-}
-
-void Chunk::SetNeighbor(Chunk &other) noexcept {
- if (other.position == position + Pos(-1, 0, 0)) {
- if (neighbor[Block::FACE_LEFT] != &other) {
- neighbor[Block::FACE_LEFT] = &other;
- other.neighbor[Block::FACE_RIGHT] = this;
- for (int z = 0; z < depth; ++z) {
- for (int y = 0; y < height; ++y) {
- Pos my_pos(0, y, z);
- Pos other_pos(width - 1, y, z);
- if (GetLight(my_pos) > 0) {
- light_queue.emplace(this, my_pos);
- }
- if (other.GetLight(other_pos) > 0) {
- light_queue.emplace(&other, other_pos);
- }
- }
- }
- work_light();
- }
- } else if (other.position == position + Pos(1, 0, 0)) {
- if (neighbor[Block::FACE_RIGHT] != &other) {
- neighbor[Block::FACE_RIGHT] = &other;
- other.neighbor[Block::FACE_LEFT] = this;
- for (int z = 0; z < depth; ++z) {
- for (int y = 0; y < height; ++y) {
- Pos my_pos(width - 1, y, z);
- Pos other_pos(0, y, z);
- if (GetLight(my_pos) > 0) {
- light_queue.emplace(this, my_pos);
- }
- if (other.GetLight(other_pos) > 0) {
- light_queue.emplace(&other, other_pos);
- }
- }
- }
- work_light();
- }
- } else if (other.position == position + Pos(0, -1, 0)) {
- if (neighbor[Block::FACE_DOWN] != &other) {
- neighbor[Block::FACE_DOWN] = &other;
- other.neighbor[Block::FACE_UP] = this;
- for (int z = 0; z < depth; ++z) {
- for (int x = 0; x < width; ++x) {
- Pos my_pos(x, 0, z);
- Pos other_pos(x, height - 1, z);
- if (GetLight(my_pos) > 0) {
- light_queue.emplace(this, my_pos);
- }
- if (other.GetLight(other_pos) > 0) {
- light_queue.emplace(&other, other_pos);
- }
- }
- }
- work_light();
- }
- } else if (other.position == position + Pos(0, 1, 0)) {
- if (neighbor[Block::FACE_UP] != &other) {
- neighbor[Block::FACE_UP] = &other;
- other.neighbor[Block::FACE_DOWN] = this;
- for (int z = 0; z < depth; ++z) {
- for (int x = 0; x < width; ++x) {
- Pos my_pos(x, height - 1, z);
- Pos other_pos(x, 0, z);
- if (GetLight(my_pos) > 0) {
- light_queue.emplace(this, my_pos);
- }
- if (other.GetLight(other_pos) > 0) {
- light_queue.emplace(&other, other_pos);
- }
- }
- }
- work_light();
- }
- } else if (other.position == position + Pos(0, 0, -1)) {
- if (neighbor[Block::FACE_BACK] != &other) {
- neighbor[Block::FACE_BACK] = &other;
- other.neighbor[Block::FACE_FRONT] = this;
- for (int y = 0; y < height; ++y) {
- for (int x = 0; x < width; ++x) {
- Pos my_pos(x, y, 0);
- Pos other_pos(x, y, depth - 1);
- if (GetLight(my_pos) > 0) {
- light_queue.emplace(this, my_pos);
- }
- if (other.GetLight(other_pos) > 0) {
- light_queue.emplace(&other, other_pos);
- }
- }
- }
- work_light();
- }
- } else if (other.position == position + Pos(0, 0, 1)) {
- if (neighbor[Block::FACE_FRONT] != &other) {
- neighbor[Block::FACE_FRONT] = &other;
- other.neighbor[Block::FACE_BACK] = this;
- for (int y = 0; y < height; ++y) {
- for (int x = 0; x < width; ++x) {
- Pos my_pos(x, y, depth - 1);
- Pos other_pos(x, y, 0);
- if (GetLight(my_pos) > 0) {
- light_queue.emplace(this, my_pos);
- }
- if (other.GetLight(other_pos) > 0) {
- light_queue.emplace(&other, other_pos);
- }
- }
- }
- work_light();
- }
- }
-}
-
-void Chunk::ClearNeighbors() noexcept {
- for (int i = 0; i < Block::FACE_COUNT; ++i) {
- neighbor[i] = nullptr;
- }
-}
-
-void Chunk::Unlink() noexcept {
- for (int face = 0; face < Block::FACE_COUNT; ++face) {
- if (neighbor[face]) {
- neighbor[face]->neighbor[Block::Opposite(Block::Face(face))] = nullptr;
- }
- }
-}
-
-void Chunk::Relink() noexcept {
- for (int face = 0; face < Block::FACE_COUNT; ++face) {
- if (neighbor[face]) {
- neighbor[face]->neighbor[Block::Opposite(Block::Face(face))] = this;
- }
- }
-}
-
-
-void Chunk::SetLight(int index, int level) noexcept {
- if (light[index] != level) {
- light[index] = level;
- Invalidate();
- }
-}
-
-int Chunk::GetLight(int index) const noexcept {
- return light[index];
-}
-
-float Chunk::GetVertexLight(const Pos &pos, const BlockModel::Position &vtx, const Model::Normal &norm) const noexcept {
- int index = ToIndex(pos);
- float light = GetLight(index);
-
- Block::Face direct_face(Block::NormalFace(norm));
- // tis okay
- BlockLookup direct(const_cast<Chunk *>(this), pos, Block::NormalFace(norm));
- if (direct) {
- float direct_light = direct.GetLight();
- if (direct_light > light) {
- light = direct_light;
- }
- } else {
- return light;
- }
-
- if (Type(BlockAt(index)).luminosity > 0 || direct.GetType().block_light) {
- return light;
- }
-
- Block::Face edge[2];
- switch (Block::Axis(direct_face)) {
- case 0: // X
- edge[0] = (vtx.y - pos.y) > 0.5f ? Block::FACE_UP : Block::FACE_DOWN;
- edge[1] = (vtx.z - pos.z) > 0.5f ? Block::FACE_FRONT : Block::FACE_BACK;
- break;
- case 1: // Y
- edge[0] = (vtx.z - pos.z) > 0.5f ? Block::FACE_FRONT : Block::FACE_BACK;
- edge[1] = (vtx.x - pos.x) > 0.5f ? Block::FACE_RIGHT : Block::FACE_LEFT;
- break;
- case 2: // Z
- edge[0] = (vtx.x - pos.x) > 0.5f ? Block::FACE_RIGHT : Block::FACE_LEFT;
- edge[1] = (vtx.y - pos.y) > 0.5f ? Block::FACE_UP : Block::FACE_DOWN;
- break;
- }
-
- int num = 1;
- int occlusion = 0;
-
- BlockLookup next[2] = {
- direct.Next(edge[0]),
- direct.Next(edge[1]),
- };
-
- if (next[0]) {
- if (next[0].GetType().block_light) {
- ++occlusion;
- } else {
- light += next[0].GetLight();
- ++num;
- }
- }
- if (next[1]) {
- if (next[1].GetType().block_light) {
- ++occlusion;
- } else {
- light += next[1].GetLight();
- ++num;
- }
- }
- if (occlusion < 2) {
- if (next[0]) {
- BlockLookup corner = next[0].Next(edge[1]);
- if (corner) {
- if (corner.GetType().block_light) {
- ++occlusion;
- } else {
- light += corner.GetLight();
- ++num;
- }
- }
- } else if (next[1]) {
- BlockLookup corner = next[1].Next(edge[0]);
- if (corner) {
- if (corner.GetType().block_light) {
- ++occlusion;
- } else {
- light += corner.GetLight();
- ++num;
- }
- }
- }
- } else {
- ++occlusion;
- }
-
- return (light / num) - (occlusion * 0.8f);
-}
-
-
-bool Chunk::IsSurface(const Pos &pos) const noexcept {
- const Block &block = BlockAt(pos);
- if (!Type(block).visible) {
- return false;
- }
- for (int face = 0; face < Block::FACE_COUNT; ++face) {
- BlockLookup next = BlockLookup(const_cast<Chunk *>(this), pos, Block::Face(face));
- if (!next || !next.GetType().visible) {
- return true;
- }
- }
- return false;
-}
-
-
-void Chunk::Draw() noexcept {
- if (dirty) {
- Update();
- }
- model.Draw();
-}
-
-
-bool Chunk::Intersection(
- const Ray &ray,
- const glm::mat4 &M,
- int &blkid,
- float &dist,
- glm::vec3 &normal
-) const noexcept {
- // TODO: should be possible to heavily optimize this
- int idx = 0;
- blkid = -1;
- dist = std::numeric_limits<float>::infinity();
- for (int z = 0; z < depth; ++z) {
- for (int y = 0; y < height; ++y) {
- for (int x = 0; x < width; ++x, ++idx) {
- const BlockType &type = Type(idx);
- if (!type.visible) {
- continue;
- }
- float cur_dist;
- glm::vec3 cur_norm;
- if (type.shape->Intersects(ray, M * ToTransform(Pos(x, y, z), idx), cur_dist, cur_norm)) {
- if (cur_dist < dist) {
- blkid = idx;
- dist = cur_dist;
- normal = cur_norm;
- }
- }
- }
- }
- }
-
- if (blkid < 0) {
- return false;
- } else {
- normal = glm::vec3(BlockAt(blkid).Transform() * glm::vec4(normal, 0.0f));
- return true;
- }
-}
-
-
-namespace {
-
-BlockModel::Buffer buf;
-
-}
-
-void Chunk::CheckUpdate() noexcept {
- if (dirty) {
- Update();
- }
-}
-
-void Chunk::Update() noexcept {
- int vtx_count = 0, idx_count = 0;
- for (const auto &block : blocks) {
- const Shape *shape = Type(block).shape;
- vtx_count += shape->VertexCount();
- idx_count += shape->VertexIndexCount();
- }
- buf.Clear();
- buf.Reserve(vtx_count, idx_count);
-
- int idx = 0;
- BlockModel::Index vtx_counter = 0;
- for (size_t z = 0; z < depth; ++z) {
- for (size_t y = 0; y < height; ++y) {
- for (size_t x = 0; x < width; ++x, ++idx) {
- const BlockType &type = Type(BlockAt(idx));
- const Pos pos(x, y, z);
-
- if (!type.visible || Obstructed(pos).All()) continue;
-
- type.FillBlockModel(buf, ToTransform(pos, idx), vtx_counter);
- size_t vtx_begin = vtx_counter;
- vtx_counter += type.shape->VertexCount();
-
- for (size_t vtx = vtx_begin; vtx < vtx_counter; ++vtx) {
- buf.lights.emplace_back(GetVertexLight(
- pos,
- buf.vertices[vtx],
- type.shape->VertexNormal(vtx - vtx_begin, BlockAt(idx).Transform())
- ));
- }
- }
- }
- }
-
- model.Update(buf);
- dirty = false;
-}
-
-Block::FaceSet Chunk::Obstructed(const Pos &pos) const noexcept {
- Block::FaceSet result;
-
- for (int f = 0; f < Block::FACE_COUNT; ++f) {
- Block::Face face = Block::Face(f);
- BlockLookup next(const_cast<Chunk *>(this), pos, face);
- if (next && next.GetType().FaceFilled(next.GetBlock(), Block::Opposite(face))) {
- result.Set(face);
- }
- }
-
- return result;
-}
-
-glm::mat4 Chunk::ToTransform(const Pos &pos, int idx) const noexcept {
- return glm::translate(ToCoords(pos)) * BlockAt(idx).Transform();
-}
-
-
-BlockLookup::BlockLookup(Chunk *c, const Chunk::Pos &p) noexcept
-: chunk(c), pos(p) {
- while (pos.x >= Chunk::width) {
- if (chunk->HasNeighbor(Block::FACE_RIGHT)) {
- chunk = &chunk->GetNeighbor(Block::FACE_RIGHT);
- pos.x -= Chunk::width;
- } else {
- chunk = nullptr;
- return;
- }
- }
- while (pos.x < 0) {
- if (chunk->HasNeighbor(Block::FACE_LEFT)) {
- chunk = &chunk->GetNeighbor(Block::FACE_LEFT);
- pos.x += Chunk::width;
- } else {
- chunk = nullptr;
- return;
- }
- }
- while (pos.y >= Chunk::height) {
- if (chunk->HasNeighbor(Block::FACE_UP)) {
- chunk = &chunk->GetNeighbor(Block::FACE_UP);
- pos.y -= Chunk::height;
- } else {
- chunk = nullptr;
- return;
- }
- }
- while (pos.y < 0) {
- if (chunk->HasNeighbor(Block::FACE_DOWN)) {
- chunk = &chunk->GetNeighbor(Block::FACE_DOWN);
- pos.y += Chunk::height;
- } else {
- chunk = nullptr;
- return;
- }
- }
- while (pos.z >= Chunk::depth) {
- if (chunk->HasNeighbor(Block::FACE_FRONT)) {
- chunk = &chunk->GetNeighbor(Block::FACE_FRONT);
- pos.z -= Chunk::depth;
- } else {
- chunk = nullptr;
- return;
- }
- }
- while (pos.z < 0) {
- if (chunk->HasNeighbor(Block::FACE_BACK)) {
- chunk = &chunk->GetNeighbor(Block::FACE_BACK);
- pos.z += Chunk::depth;
- } else {
- chunk = nullptr;
- return;
- }
- }
-}
-
-BlockLookup::BlockLookup(Chunk *c, const Chunk::Pos &p, Block::Face face) noexcept
-: chunk(c), pos(p) {
- pos += Block::FaceNormal(face);
- if (!Chunk::InBounds(pos)) {
- pos -= Block::FaceNormal(face) * Chunk::Extent();
- chunk = &chunk->GetNeighbor(face);
- }
-}
-
-
-ChunkLoader::ChunkLoader(const Config &config, const BlockTypeRegistry ®, const Generator &gen) noexcept
-: base(0, 0, 0)
-, reg(reg)
-, gen(gen)
-, loaded()
-, to_generate()
-, to_free()
-, load_dist(config.load_dist)
-, unload_dist(config.unload_dist) {
-
-}
-
-namespace {
-
-struct ChunkLess {
-
- explicit ChunkLess(const Chunk::Pos &base) noexcept
- : base(base) { }
-
- bool operator ()(const Chunk::Pos &a, const Chunk::Pos &b) const noexcept {
- Chunk::Pos da(base - a);
- Chunk::Pos db(base - b);
- return
- da.x * da.x + da.y * da.y + da.z * da.z <
- db.x * db.x + db.y * db.y + db.z * db.z;
- }
-
- Chunk::Pos base;
-
-};
-
-}
-
-void ChunkLoader::Generate(const Chunk::Pos &from, const Chunk::Pos &to) {
- for (int z = from.z; z < to.z; ++z) {
- for (int y = from.y; y < to.y; ++y) {
- for (int x = from.x; x < to.x; ++x) {
- Chunk::Pos pos(x, y, z);
- if (Known(pos)) {
- continue;
- } else if (pos == base) {
- Generate(pos);
-
- // light testing
- // for (int i = 0; i < 16; ++i) {
- // for (int j = 0; j < 16; ++j) {
- // loaded.back().SetBlock(Chunk::Pos{ i, j, 0 }, Block(1));
- // loaded.back().SetBlock(Chunk::Pos{ i, j, 15 }, Block(1));
- // loaded.back().SetBlock(Chunk::Pos{ 0, j, i }, Block(1));
- // loaded.back().SetBlock(Chunk::Pos{ 15, j, i }, Block(1));
- // }
- // }
- // loaded.back().SetBlock(Chunk::Pos{ 1, 0, 1 }, Block(13));
- // loaded.back().SetBlock(Chunk::Pos{ 14, 0, 1 }, Block(13));
- // loaded.back().SetBlock(Chunk::Pos{ 1, 0, 14 }, Block(13));
- // loaded.back().SetBlock(Chunk::Pos{ 14, 0, 14 }, Block(13));
- // loaded.back().SetBlock(Chunk::Pos{ 1, 15, 1 }, Block(13));
- // loaded.back().SetBlock(Chunk::Pos{ 14, 15, 1 }, Block(13));
- // loaded.back().SetBlock(Chunk::Pos{ 1, 15, 14 }, Block(13));
- // loaded.back().SetBlock(Chunk::Pos{ 14, 15, 14 }, Block(13));
- // loaded.back().SetBlock(Chunk::Pos{ 7, 7, 0 }, Block(13));
- // loaded.back().SetBlock(Chunk::Pos{ 8, 7, 0 }, Block(13));
- // loaded.back().SetBlock(Chunk::Pos{ 7, 8, 0 }, Block(13));
- // loaded.back().SetBlock(Chunk::Pos{ 8, 8, 0 }, Block(13));
- // loaded.back().SetBlock(Chunk::Pos{ 7, 7, 15 }, Block(13));
- // loaded.back().SetBlock(Chunk::Pos{ 8, 7, 15 }, Block(13));
- // loaded.back().SetBlock(Chunk::Pos{ 7, 8, 15 }, Block(13));
- // loaded.back().SetBlock(Chunk::Pos{ 8, 8, 15 }, Block(13));
- // loaded.back().SetBlock(Chunk::Pos{ 0, 7, 7 }, Block(13));
- // loaded.back().SetBlock(Chunk::Pos{ 0, 7, 8 }, Block(13));
- // loaded.back().SetBlock(Chunk::Pos{ 0, 8, 7 }, Block(13));
- // loaded.back().SetBlock(Chunk::Pos{ 0, 8, 8 }, Block(13));
- // loaded.back().SetBlock(Chunk::Pos{ 15, 7, 7 }, Block(13));
- // loaded.back().SetBlock(Chunk::Pos{ 15, 7, 8 }, Block(13));
- // loaded.back().SetBlock(Chunk::Pos{ 15, 8, 7 }, Block(13));
- // loaded.back().SetBlock(Chunk::Pos{ 15, 8, 8 }, Block(13));
- // loaded.back().Invalidate();
- // loaded.back().CheckUpdate();
-
- // orientation testing
- // for (int i = 0; i < Block::FACE_COUNT; ++i) {
- // for (int j = 0; j < Block::TURN_COUNT; ++j) {
- // loaded.back().BlockAt(512 * j + 2 * i) = Block(3 * (j + 1), Block::Face(i), Block::Turn(j));
- // }
- // }
- // loaded.back().Invalidate();
- // loaded.back().CheckUpdate();
- } else {
- to_generate.emplace_back(pos);
- }
- }
- }
- }
- to_generate.sort(ChunkLess(base));
-}
-
-Chunk &ChunkLoader::Generate(const Chunk::Pos &pos) {
- loaded.emplace_back(reg);
- Chunk &chunk = loaded.back();
- chunk.Position(pos);
- gen(chunk);
- Insert(chunk);
- return chunk;
-}
-
-void ChunkLoader::Insert(Chunk &chunk) noexcept {
- for (Chunk &other : loaded) {
- chunk.SetNeighbor(other);
- }
-}
-
-void ChunkLoader::Remove(Chunk &chunk) noexcept {
- chunk.Unlink();
-}
-
-Chunk *ChunkLoader::Loaded(const Chunk::Pos &pos) noexcept {
- for (Chunk &chunk : loaded) {
- if (chunk.Position() == pos) {
- return &chunk;
- }
- }
- return nullptr;
-}
-
-bool ChunkLoader::Queued(const Chunk::Pos &pos) noexcept {
- for (const Chunk::Pos &chunk : to_generate) {
- if (chunk == pos) {
- return true;
- }
- }
- return nullptr;
-}
-
-bool ChunkLoader::Known(const Chunk::Pos &pos) noexcept {
- if (Loaded(pos)) return true;
- return Queued(pos);
-}
-
-Chunk &ChunkLoader::ForceLoad(const Chunk::Pos &pos) {
- Chunk *chunk = Loaded(pos);
- if (chunk) {
- return *chunk;
- }
-
- for (auto iter(to_generate.begin()), end(to_generate.end()); iter != end; ++iter) {
- if (*iter == pos) {
- to_generate.erase(iter);
- break;
- }
- }
-
- return Generate(pos);
-}
-
-void ChunkLoader::Rebase(const Chunk::Pos &new_base) {
- if (new_base == base) {
- return;
- }
- base = new_base;
-
- // unload far away chunks
- for (auto iter(loaded.begin()), end(loaded.end()); iter != end;) {
- if (std::abs(base.x - iter->Position().x) > unload_dist
- || std::abs(base.y - iter->Position().y) > unload_dist
- || std::abs(base.z - iter->Position().z) > unload_dist) {
- auto saved = iter;
- Remove(*saved);
- ++iter;
- to_free.splice(to_free.end(), loaded, saved);
- } else {
- ++iter;
- }
- }
- // abort far away queued chunks
- for (auto iter(to_generate.begin()), end(to_generate.end()); iter != end;) {
- if (std::abs(base.x - iter->x) > unload_dist
- || std::abs(base.y - iter->y) > unload_dist
- || std::abs(base.z - iter->z) > unload_dist) {
- iter = to_generate.erase(iter);
- } else {
- ++iter;
- }
- }
- // add missing new chunks
- GenerateSurrounding(base);
-}
-
-void ChunkLoader::GenerateSurrounding(const Chunk::Pos &pos) {
- const Chunk::Pos offset(load_dist, load_dist, load_dist);
- Generate(pos - offset, pos + offset);
-}
-
-void ChunkLoader::Update() {
- if (to_generate.empty()) {
- return;
- }
-
- Chunk::Pos pos(to_generate.front());
- to_generate.pop_front();
-
- for (auto iter(to_free.begin()), end(to_free.end()); iter != end; ++iter) {
- if (iter->Position() == pos) {
- iter->Relink();
- loaded.splice(loaded.end(), to_free, iter);
- return;
- }
- }
-
- if (to_free.empty()) {
- loaded.emplace_back(reg);
- } else {
- to_free.front().ClearNeighbors();
- loaded.splice(loaded.end(), to_free, to_free.begin());
- }
- Chunk &chunk = loaded.back();
- chunk.Position(pos);
- gen(chunk);
- Insert(chunk);
-}
-
-}
+++ /dev/null
-#ifndef BLANK_CHUNK_HPP_
-#define BLANK_CHUNK_HPP_
-
-#include "block.hpp"
-#include "geometry.hpp"
-#include "model.hpp"
-
-#include <list>
-#include <vector>
-#include <glm/glm.hpp>
-#include <glm/gtx/transform.hpp>
-
-
-namespace blank {
-
-/// cube of size 16 (256 tiles, 4096 blocks)
-class Chunk {
-
-public:
- using Pos = glm::tvec3<int>;
-
-public:
- explicit Chunk(const BlockTypeRegistry &) noexcept;
-
- Chunk(Chunk &&) noexcept;
- Chunk &operator =(Chunk &&) noexcept;
-
- static constexpr int width = 16;
- static constexpr int height = 16;
- static constexpr int depth = 16;
- static Pos Extent() noexcept { return { width, height, depth }; }
- static constexpr int size = width * height * depth;
-
- static AABB Bounds() noexcept { return AABB{ { 0, 0, 0 }, Extent() }; }
-
- static constexpr bool InBounds(const Block::Pos &pos) noexcept {
- return
- pos.x >= 0 && pos.x < width &&
- pos.y >= 0 && pos.y < height &&
- pos.z >= 0 && pos.z < depth;
- }
- static constexpr bool InBounds(const Pos &pos) noexcept {
- return
- pos.x >= 0 && pos.x < width &&
- pos.y >= 0 && pos.y < height &&
- pos.z >= 0 && pos.z < depth;
- }
- static constexpr int ToIndex(const Pos &pos) noexcept {
- return pos.x + pos.y * width + pos.z * width * height;
- }
- static constexpr bool InBounds(int idx) noexcept {
- return idx >= 0 && idx < size;
- }
- static Block::Pos ToCoords(int idx) noexcept {
- return Block::Pos(
- 0.5f + (idx % width),
- 0.5f + ((idx / width) % height),
- 0.5f + (idx / (width * height))
- );
- }
- static Block::Pos ToCoords(const Pos &pos) noexcept {
- return Block::Pos(pos) + 0.5f;
- }
- static Pos ToPos(int idx) noexcept {
- return Pos(
- (idx % width),
- ((idx / width) % height),
- (idx / (width * height))
- );
- }
- glm::mat4 ToTransform(const Pos &pos, int idx) const noexcept;
-
- static constexpr bool IsBorder(int idx) noexcept {
- return
- idx < width * height || // low Z plane
- idx % width == 0 || // low X plane
- (idx / (width * height)) == depth - 1 || // high Z plane
- idx % width == width - 1 || // high X plane
- (idx / width) % height == 0 || // low Y plane
- (idx / width) % height == height - 1; // high Y plane
- }
-
- bool IsSurface(int index) const noexcept { return IsSurface(ToPos(index)); }
- bool IsSurface(const Block::Pos &pos) const noexcept { return IsSurface(Pos(pos)); }
- bool IsSurface(const Pos &pos) const noexcept;
-
- void SetNeighbor(Chunk &) noexcept;
- bool HasNeighbor(Block::Face f) const noexcept { return neighbor[f]; }
- Chunk &GetNeighbor(Block::Face f) noexcept { return *neighbor[f]; }
- const Chunk &GetNeighbor(Block::Face f) const noexcept { return *neighbor[f]; }
- void ClearNeighbors() noexcept;
- void Unlink() noexcept;
- void Relink() noexcept;
-
- // check which faces of a block at given index are obstructed (and therefore invisible)
- Block::FaceSet Obstructed(const Pos &) const noexcept;
-
- void Invalidate() noexcept { dirty = true; }
-
- void SetBlock(int index, const Block &) noexcept;
- void SetBlock(const Block::Pos &pos, const Block &block) noexcept { SetBlock(ToIndex(pos), block); }
- void SetBlock(const Pos &pos, const Block &block) noexcept { SetBlock(ToIndex(pos), block); }
-
- const Block &BlockAt(int index) const noexcept { return blocks[index]; }
- const Block &BlockAt(const Block::Pos &pos) const noexcept { return BlockAt(ToIndex(pos)); }
- const Block &BlockAt(const Pos &pos) const noexcept { return BlockAt(ToIndex(pos)); }
-
- const BlockType &Type(const Block &b) const noexcept { return types->Get(b.type); }
- const BlockType &Type(int index) const noexcept { return Type(BlockAt(index)); }
-
- void SetLight(int index, int level) noexcept;
- void SetLight(const Pos &pos, int level) noexcept { SetLight(ToIndex(pos), level); }
- void SetLight(const Block::Pos &pos, int level) noexcept { SetLight(ToIndex(pos), level); }
-
- int GetLight(int index) const noexcept;
- int GetLight(const Pos &pos) const noexcept { return GetLight(ToIndex(pos)); }
- int GetLight(const Block::Pos &pos) const noexcept { return GetLight(ToIndex(pos)); }
-
- float GetVertexLight(const Pos &, const BlockModel::Position &, const Model::Normal &) const noexcept;
-
- bool Intersection(
- const Ray &ray,
- const glm::mat4 &M,
- float &dist
- ) const noexcept {
- return blank::Intersection(ray, Bounds(), M, &dist);
- }
-
- bool Intersection(
- const Ray &,
- const glm::mat4 &M,
- int &blkid,
- float &dist,
- glm::vec3 &normal) const noexcept;
-
- void Position(const Pos &pos) noexcept { position = pos; }
- const Pos &Position() const noexcept { return position; }
- glm::mat4 Transform(const Pos &offset) const noexcept {
- return glm::translate((position - offset) * Extent());
- }
-
- void CheckUpdate() noexcept;
- void Draw() noexcept;
-
-private:
- void Update() noexcept;
-
-private:
- const BlockTypeRegistry *types;
- Chunk *neighbor[Block::FACE_COUNT];
- Block blocks[16 * 16 * 16];
- unsigned char light[16 * 16 * 16];
- BlockModel model;
- Pos position;
- bool dirty;
-
-};
-
-
-class BlockLookup {
-
-public:
- // resolve chunk/position from oob coordinates
- BlockLookup(Chunk *c, const Chunk::Pos &p) noexcept;
-
- // resolve chunk/position from ib coordinates and direction
- BlockLookup(Chunk *c, const Chunk::Pos &p, Block::Face dir) noexcept;
-
- // check if lookup was successful
- operator bool() const { return chunk; }
-
- // only valid if lookup was successful
- Chunk &GetChunk() const noexcept { return *chunk; }
- const Chunk::Pos &GetBlockPos() const noexcept { return pos; }
- const Block &GetBlock() const noexcept { return GetChunk().BlockAt(GetBlockPos()); }
- const BlockType &GetType() const noexcept { return GetChunk().Type(GetBlock()); }
- int GetLight() const noexcept { return GetChunk().GetLight(GetBlockPos()); }
-
- // traverse in given direction
- BlockLookup Next(Block::Face f) const { return BlockLookup(chunk, pos, f); }
-
-private:
- Chunk *chunk;
- Chunk::Pos pos;
-
-};
-
-
-class Generator;
-
-class ChunkLoader {
-
-public:
- struct Config {
- int load_dist = 6;
- int unload_dist = 8;
- };
-
- ChunkLoader(const Config &, const BlockTypeRegistry &, const Generator &) noexcept;
-
- void Generate(const Chunk::Pos &from, const Chunk::Pos &to);
- void GenerateSurrounding(const Chunk::Pos &);
-
- std::list<Chunk> &Loaded() noexcept { return loaded; }
-
- Chunk *Loaded(const Chunk::Pos &) noexcept;
- bool Queued(const Chunk::Pos &) noexcept;
- bool Known(const Chunk::Pos &) noexcept;
- Chunk &ForceLoad(const Chunk::Pos &);
-
- void Rebase(const Chunk::Pos &);
- void Update();
-
-private:
- Chunk &Generate(const Chunk::Pos &pos);
- void Insert(Chunk &) noexcept;
- void Remove(Chunk &) noexcept;
-
-private:
- Chunk::Pos base;
-
- const BlockTypeRegistry ®
- const Generator &gen;
-
- std::list<Chunk> loaded;
- std::list<Chunk::Pos> to_generate;
- std::list<Chunk> to_free;
-
- int load_dist;
- int unload_dist;
-
-};
-
-}
-
-#endif
+++ /dev/null
-#include "controller.hpp"
-
-#include <glm/gtx/euler_angles.hpp>
-#include <glm/gtx/rotate_vector.hpp>
-
-
-namespace blank {
-
-FPSController::FPSController(Entity &entity) noexcept
-: entity(entity)
-, pitch(0)
-, yaw(0) {
-
-}
-
-
-void FPSController::Pitch(float p) noexcept {
- pitch = p;
- if (pitch > PI / 2) {
- pitch = PI / 2;
- } else if (pitch < -PI / 2) {
- pitch = -PI / 2;
- }
-}
-
-void FPSController::RotatePitch(float delta) noexcept {
- Pitch(pitch + delta);
-}
-
-void FPSController::Yaw(float y) noexcept {
- yaw = y;
- if (yaw > PI) {
- yaw -= PI * 2;
- } else if (yaw < -PI) {
- yaw += PI * 2;
- }
-}
-
-void FPSController::RotateYaw(float delta) noexcept {
- Yaw(yaw + delta);
-}
-
-
-void FPSController::Update(int dt) noexcept {
- entity.Rotation(glm::eulerAngleYX(yaw, pitch));
- entity.Velocity(glm::rotateY(velocity, yaw));
-}
-
-
-RandomWalk::RandomWalk(Entity &e) noexcept
-: entity(e)
-, time_left(0) {
-
-}
-
-
-void RandomWalk::Update(int dt) noexcept {
- time_left -= dt;
- if (time_left > 0) return;
- time_left += 2500 + (rand() % 5000);
-
- constexpr float move_vel = 0.0005f;
-
- glm::vec3 new_vel = entity.Velocity();
-
- switch (rand() % 9) {
- case 0:
- new_vel.x = -move_vel;
- break;
- case 1:
- new_vel.x = 0.0f;
- break;
- case 2:
- new_vel.x = move_vel;
- break;
- case 3:
- new_vel.y = -move_vel;
- break;
- case 4:
- new_vel.y = 0.0f;
- break;
- case 5:
- new_vel.y = move_vel;
- break;
- case 6:
- new_vel.z = -move_vel;
- break;
- case 7:
- new_vel.z = 0.0f;
- break;
- case 8:
- new_vel.z = move_vel;
- break;
- }
-
- entity.Velocity(new_vel);
-}
-
-}
+++ /dev/null
-#ifndef BLANK_CONTROLLER_HPP_
-#define BLANK_CONTROLLER_HPP_
-
-#include "entity.hpp"
-#include "geometry.hpp"
-
-#include <glm/glm.hpp>
-
-
-namespace blank {
-
-class FPSController {
-
-public:
- explicit FPSController(Entity &) noexcept;
-
- Ray Aim() const noexcept { return entity.Aim(entity.ChunkCoords()); }
-
- const glm::vec3 &Velocity() const noexcept { return velocity; }
- void Velocity(const glm::vec3 &vel) noexcept { velocity = vel; }
-
- // all angles in radians (full circle = 2Ï€)
- float Pitch() const noexcept { return pitch; }
- void Pitch(float p) noexcept;
- void RotatePitch(float delta) noexcept;
- float Yaw() const noexcept { return yaw; }
- void Yaw(float y) noexcept;
- void RotateYaw(float delta) noexcept;
-
- void Update(int dt) noexcept;
-
-private:
- Entity &entity;
-
- glm::vec3 velocity;
-
- float pitch;
- float yaw;
-
-};
-
-
-class RandomWalk {
-
-public:
- explicit RandomWalk(Entity &) noexcept;
-
- void Update(int dt) noexcept;
-
-private:
- Entity &entity;
-
- int time_left;
-
-};
-
-}
-
-#endif
+++ /dev/null
-#include "entity.hpp"
-
-#include "geometry.hpp"
-#include "shape.hpp"
-
-#include <cmath>
-#include <glm/gtx/transform.hpp>
-
-namespace {
-
-blank::Model::Buffer model_buffer;
-
-}
-
-namespace blank {
-
-Entity::Entity() noexcept
-: shape(nullptr)
-, model()
-, velocity(0, 0, 0)
-, position(0, 0, 0)
-, chunk(0, 0, 0)
-, angular_velocity(1.0f, 0.0f, 0.0f, 0.0f)
-, rotation(1.0f) {
-
-}
-
-
-void Entity::SetShape(const Shape *s, const glm::vec3 &color) {
- shape = s;
- model_buffer.Clear();
- shape->Vertices(model_buffer.vertices, model_buffer.normals, model_buffer.indices);
- model_buffer.colors.resize(shape->VertexCount(), color);
- model.Update(model_buffer);
-}
-
-void Entity::SetShapeless() noexcept {
- shape = nullptr;
-}
-
-
-void Entity::Velocity(const glm::vec3 &vel) noexcept {
- velocity = vel;
-}
-
-void Entity::Position(const Block::Pos &pos) noexcept {
- position = pos;
- while (position.x >= Chunk::width) {
- position.x -= Chunk::width;
- ++chunk.x;
- }
- while (position.x < 0) {
- position.x += Chunk::width;
- --chunk.x;
- }
- while (position.y >= Chunk::height) {
- position.y -= Chunk::height;
- ++chunk.y;
- }
- while (position.y < 0) {
- position.y += Chunk::height;
- --chunk.y;
- }
- while (position.z >= Chunk::depth) {
- position.z -= Chunk::depth;
- ++chunk.z;
- }
- while (position.z < 0) {
- position.z += Chunk::depth;
- --chunk.z;
- }
-}
-
-void Entity::Move(const glm::vec3 &delta) noexcept {
- Position(position + delta);
-}
-
-void Entity::AngularVelocity(const glm::quat &v) noexcept {
- angular_velocity = v;
-}
-
-void Entity::Rotation(const glm::mat4 &rot) noexcept {
- rotation = rot;
-}
-
-void Entity::Rotate(const glm::quat &delta) noexcept {
- Rotation(rotation * glm::mat4_cast(delta));
-}
-
-glm::mat4 Entity::Transform(const Chunk::Pos &chunk_offset) const noexcept {
- const glm::vec3 chunk_pos = (chunk - chunk_offset) * Chunk::Extent();
- return glm::translate(position + chunk_pos) * rotation;
-}
-
-Ray Entity::Aim(const Chunk::Pos &chunk_offset) const noexcept {
- glm::mat4 transform = Transform(chunk_offset);
- glm::vec4 from = transform * glm::vec4(0.0f, 0.0f, 0.0f, 1.0f);
- from /= from.w;
- glm::vec4 to = transform * glm::vec4(0.0f, 0.0f, -1.0f, 1.0f);
- to /= to.w;
- return Ray{ glm::vec3(from), glm::normalize(glm::vec3(to - from)) };
-}
-
-void Entity::Update(int dt) noexcept {
- Move(velocity * float(dt));
- Rotate(angular_velocity * float(dt));
-}
-
-
-void Entity::Draw() noexcept {
- model.Draw();
-}
-
-}
+++ /dev/null
-#ifndef BLANK_ENTITY_HPP_
-#define BLANK_ENTITY_HPP_
-
-#include "block.hpp"
-#include "chunk.hpp"
-#include "model.hpp"
-
-#include <glm/glm.hpp>
-#include <glm/gtc/quaternion.hpp>
-
-
-namespace blank {
-
-class Ray;
-class Shape;
-
-class Entity {
-
-public:
- Entity() noexcept;
-
- bool HasShape() const noexcept { return shape; }
- const Shape *GetShape() const noexcept { return shape; }
- void SetShape(const Shape *, const glm::vec3 &color);
- void SetShapeless() noexcept;
-
- const glm::vec3 &Velocity() const noexcept { return velocity; }
- void Velocity(const glm::vec3 &) noexcept;
-
- const Block::Pos &Position() const noexcept { return position; }
- void Position(const Block::Pos &) noexcept;
- void Move(const glm::vec3 &delta) noexcept;
-
- const Chunk::Pos ChunkCoords() const noexcept { return chunk; }
-
- const glm::quat &AngularVelocity() const noexcept { return angular_velocity; }
- void AngularVelocity(const glm::quat &) noexcept;
-
- const glm::mat4 &Rotation() const noexcept { return rotation; }
- void Rotation(const glm::mat4 &) noexcept;
- void Rotate(const glm::quat &delta) noexcept;
-
- glm::mat4 Transform(const Chunk::Pos &chunk_offset) const noexcept;
- Ray Aim(const Chunk::Pos &chunk_offset) const noexcept;
-
- void Update(int dt) noexcept;
-
- void Draw() noexcept;
-
-private:
- const Shape *shape;
- Model model;
-
- glm::vec3 velocity;
- Block::Pos position;
- Chunk::Pos chunk;
-
- glm::quat angular_velocity;
- glm::mat4 rotation;
-
-};
-
-}
-
-#endif
+++ /dev/null
-#include "generator.hpp"
-
-#include <glm/glm.hpp>
-
-
-namespace blank {
-
-Generator::Generator(const Config &config) noexcept
-: solidNoise(config.solid_seed)
-, typeNoise(config.type_seed)
-, stretch(1.0f/config.stretch)
-, solid_threshold(config.solid_threshold)
-, space(0)
-, light(0)
-, solids() {
-
-}
-
-
-void Generator::operator ()(Chunk &chunk) const noexcept {
- Chunk::Pos pos(chunk.Position());
- glm::vec3 coords(pos * Chunk::Extent());
- for (int z = 0; z < Chunk::depth; ++z) {
- for (int y = 0; y < Chunk::height; ++y) {
- for (int x = 0; x < Chunk::width; ++x) {
- Block::Pos block_pos(x, y, z);
- glm::vec3 gen_pos = (coords + block_pos) * stretch;
- float val = OctaveNoise(solidNoise, coords + block_pos, 3, 0.5f, stretch, 2.0f);
- if (val > solid_threshold) {
- int type_val = int((typeNoise(gen_pos) + 1.0f) * solids.size()) % solids.size();
- chunk.SetBlock(block_pos, Block(solids[type_val]));
- } else {
- chunk.SetBlock(block_pos, Block(space));
- }
- }
- }
- }
- unsigned int random = 263167 * pos.x + 2097593 * pos.y + 426389 * pos.z;
- for (int index = 0; index < Chunk::size; ++index) {
- if (chunk.IsSurface(index)) {
- random = random * 666649 + 7778777;
- if ((random % 32) == 0) {
- chunk.SetBlock(index, Block(light));
- }
- }
- }
- chunk.Invalidate();
- chunk.CheckUpdate();
-}
-
-}
+++ /dev/null
-#ifndef BLANK_GENERATOR_HPP_
-#define BLANK_GENERATOR_HPP_
-
-#include "block.hpp"
-#include "chunk.hpp"
-#include "noise.hpp"
-
-#include <vector>
-
-
-namespace blank {
-
-class Generator {
-
-public:
- struct Config {
- unsigned int solid_seed = 0;
- unsigned int type_seed = 0;
- float stretch = 64.0f;
- float solid_threshold = 0.5f;
- };
-
- explicit Generator(const Config &) noexcept;
-
- void operator ()(Chunk &) const noexcept;
-
- void Space(Block::Type t) noexcept { space = t; }
- void Light(Block::Type t) noexcept { light = t; }
- void Solids(const std::vector<Block::Type> &s) { solids = s; }
-
-private:
- SimplexNoise solidNoise;
- WorleyNoise typeNoise;
-
- float stretch;
- float solid_threshold;
-
- Block::Type space;
- Block::Type light;
- std::vector<Block::Type> solids;
-
-};
-
-}
-
-#endif
+++ /dev/null
-#include "geometry.hpp"
-
-#include <limits>
-
-
-namespace blank {
-
-bool Intersection(
- const Ray &ray,
- const AABB &aabb,
- const glm::mat4 &M,
- float *dist,
- glm::vec3 *normal
-) noexcept {
- float t_min = 0.0f;
- float t_max = std::numeric_limits<float>::infinity();
- const glm::vec3 aabb_pos(M[3].x, M[3].y, M[3].z);
- const glm::vec3 delta = aabb_pos - ray.orig;
-
- glm::vec3 t1(t_min, t_min, t_min), t2(t_max, t_max, t_max);
-
- for (int i = 0; i < 3; ++i) {
- const glm::vec3 axis(M[i].x, M[i].y, M[i].z);
- const float e = glm::dot(axis, delta);
- const float f = glm::dot(axis, ray.dir);
-
- if (std::abs(f) > std::numeric_limits<float>::epsilon()) {
- t1[i] = (e + aabb.min[i]) / f;
- t2[i] = (e + aabb.max[i]) / f;
-
- t_min = std::max(t_min, std::min(t1[i], t2[i]));
- t_max = std::min(t_max, std::max(t1[i], t2[i]));
-
- if (t_max < t_min) {
- return false;
- }
- } else {
- if (aabb.min[i] - e < 0.0f || -aabb.max[i] - e > 0.0f) {
- return false;
- }
- }
- }
-
- glm::vec3 min_all(min(t1, t2));
-
- if (dist) {
- *dist = t_min;
- }
- if (normal) {
- glm::vec4 norm(0.0f);
- if (min_all.x > min_all.y) {
- if (min_all.x > min_all.z) {
- norm.x = t2.x < t1.x ? 1 : -1;
- } else {
- norm.z = t2.z < t1.z ? 1 : -1;
- }
- } else if (min_all.y > min_all.z) {
- norm.y = t2.y < t1.y ? 1 : -1;
- } else {
- norm.z = t2.z < t1.z ? 1 : -1;
- }
- norm = M * norm;
- *normal = glm::vec3(norm);
- }
- return true;
-}
-
-bool CullTest(const AABB &box, const glm::mat4 &MVP) noexcept {
- // transform corners into clip space
- glm::vec4 corners[8] = {
- { box.min.x, box.min.y, box.min.z, 1.0f },
- { box.min.x, box.min.y, box.max.z, 1.0f },
- { box.min.x, box.max.y, box.min.z, 1.0f },
- { box.min.x, box.max.y, box.max.z, 1.0f },
- { box.max.x, box.min.y, box.min.z, 1.0f },
- { box.max.x, box.min.y, box.max.z, 1.0f },
- { box.max.x, box.max.y, box.min.z, 1.0f },
- { box.max.x, box.max.y, box.max.z, 1.0f },
- };
- for (glm::vec4 &corner : corners) {
- corner = MVP * corner;
- corner /= corner.w;
- }
-
- int hits[6] = { 0, 0, 0, 0, 0, 0 };
-
- // check how many corners lie outside
- for (const glm::vec4 &corner : corners) {
- if (corner.x > 1.0f) ++hits[0];
- if (corner.x < -1.0f) ++hits[1];
- if (corner.y > 1.0f) ++hits[2];
- if (corner.y < -1.0f) ++hits[3];
- if (corner.z > 1.0f) ++hits[4];
- if (corner.z < -1.0f) ++hits[5];
- }
-
- // if all corners are outside any given clip plane, the test is true
- for (int hit : hits) {
- if (hit == 8) return true;
- }
-
- // otherwise the box might still get culled completely, but can't say for sure ;)
- return false;
-}
-
-}
+++ /dev/null
-#ifndef BLANK_GEOMETRY_H_
-#define BLANK_GEOMETRY_H_
-
-#include <algorithm>
-#include <glm/glm.hpp>
-
-
-namespace blank {
-
-constexpr float PI = 3.141592653589793238462643383279502884;
-constexpr float PI_0p25 = PI * 0.25f;
-constexpr float PI_0p5 = PI * 0.5f;
-constexpr float PI_1p5 = PI * 1.5f;
-constexpr float PI_2p0 = PI * 2.0f;
-
-struct AABB {
- glm::vec3 min;
- glm::vec3 max;
-
- void Adjust() noexcept {
- if (max.x < min.x) std::swap(max.x, min.x);
- if (max.y < min.y) std::swap(max.y, min.y);
- if (max.z < min.z) std::swap(max.z, min.z);
- }
-};
-
-struct Ray {
- glm::vec3 orig;
- glm::vec3 dir;
-};
-
-bool Intersection(
- const Ray &,
- const AABB &,
- const glm::mat4 &M,
- float *dist = nullptr,
- glm::vec3 *normal = nullptr) noexcept;
-
-bool CullTest(const AABB &box, const glm::mat4 &MVP) noexcept;
-
-}
-
-#endif
--- /dev/null
+#ifndef BLANK_GRAPHICS_BLOCKLIGHTING_HPP_
+#define BLANK_GRAPHICS_BLOCKLIGHTING_HPP_
+
+#include "Program.hpp"
+
+#include <GL/glew.h>
+#include <glm/glm.hpp>
+
+
+namespace blank {
+
+class BlockLighting {
+
+public:
+ BlockLighting();
+
+ void Activate() noexcept;
+
+ void SetFogDensity(float) noexcept;
+
+ void SetM(const glm::mat4 &m) noexcept;
+ void SetProjection(const glm::mat4 &p) noexcept;
+ void SetView(const glm::mat4 &v) noexcept;
+ void SetVP(const glm::mat4 &v, const glm::mat4 &p) noexcept;
+ void SetMVP(const glm::mat4 &m, const glm::mat4 &v, const glm::mat4 &p) noexcept;
+
+ const glm::mat4 &Projection() const noexcept { return projection; }
+ const glm::mat4 &View() const noexcept { return view; }
+ const glm::mat4 &GetVP() const noexcept { return vp; }
+
+private:
+ Program program;
+
+ float fog_density;
+
+ glm::mat4 projection;
+ glm::mat4 view;
+ glm::mat4 vp;
+
+ GLuint mv_handle;
+ GLuint mvp_handle;
+ GLuint light_direction_handle;
+ GLuint light_color_handle;
+ GLuint fog_density_handle;
+
+};
+
+}
+
+#endif
--- /dev/null
+#include "Camera.hpp"
+
+#include "../model/geometry.hpp"
+
+#include <GL/glew.h>
+#include <glm/gtc/matrix_transform.hpp>
+
+
+namespace blank {
+
+Camera::Camera() noexcept
+: fov(PI_0p25)
+, aspect(1.0f)
+, near_clip(0.1f)
+, far_clip(256.0f)
+, projection(glm::perspective(fov, aspect, near_clip, far_clip)) {
+
+}
+
+
+void Camera::Viewport(int width, int height) noexcept {
+ Viewport(0, 0, width, height);
+}
+
+void Camera::Viewport(int x, int y, int width, int height) noexcept {
+ glViewport(x, y, width, height);
+ Aspect(width, height);
+}
+
+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 near, float far) noexcept {
+ near_clip = near;
+ far_clip = far;
+ UpdateProjection();
+}
+
+
+void Camera::UpdateProjection() noexcept {
+ projection = glm::perspective(fov, aspect, near_clip, far_clip);
+}
+
+}
--- /dev/null
+#ifndef BLANK_GRAPHICS_CAMERA_HPP_
+#define BLANK_GRAPHICS_CAMERA_HPP_
+
+#include <glm/glm.hpp>
+
+
+namespace blank {
+
+class Camera {
+
+public:
+ Camera() noexcept;
+
+ void Viewport(int width, int height) noexcept;
+ void Viewport(int x, int y, int width, int height) noexcept;
+
+ /// FOV in radians
+ 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() noexcept { return projection; }
+
+private:
+ void UpdateProjection() noexcept;
+
+private:
+ float fov;
+ float aspect;
+ float near_clip;
+ float far_clip;
+
+ glm::mat4 projection;
+
+};
+
+}
+
+#endif
--- /dev/null
+#ifndef BLANK_GRAPHICS_DIRECTIONALLIGHTING_HPP_
+#define BLANK_GRAPHICS_DIRECTIONALLIGHTING_HPP_
+
+#include "Program.hpp"
+
+#include <GL/glew.h>
+#include <glm/glm.hpp>
+
+
+namespace blank {
+
+class DirectionalLighting {
+
+public:
+ DirectionalLighting();
+
+ void Activate() noexcept;
+
+ void SetLightDirection(const glm::vec3 &) noexcept;
+
+ void SetFogDensity(float) noexcept;
+
+ void SetM(const glm::mat4 &m) noexcept;
+ void SetProjection(const glm::mat4 &p) noexcept;
+ void SetView(const glm::mat4 &v) noexcept;
+ void SetVP(const glm::mat4 &v, const glm::mat4 &p) noexcept;
+ void SetMVP(const glm::mat4 &m, const glm::mat4 &v, const glm::mat4 &p) noexcept;
+
+ const glm::mat4 &Projection() const noexcept { return projection; }
+ const glm::mat4 &View() const noexcept { return view; }
+ const glm::mat4 &GetVP() const noexcept { return vp; }
+
+private:
+ Program program;
+
+ glm::vec3 light_direction;
+ glm::vec3 light_color;
+
+ float fog_density;
+
+ glm::mat4 projection;
+ glm::mat4 view;
+ glm::mat4 vp;
+
+ GLuint m_handle;
+ GLuint mv_handle;
+ GLuint mvp_handle;
+ GLuint light_direction_handle;
+ GLuint light_color_handle;
+ GLuint fog_density_handle;
+
+};
+
+}
+
+#endif
--- /dev/null
+#ifndef BLANK_GRAPHICS_PROGRAM_HPP_
+#define BLANK_GRAPHICS_PROGRAM_HPP_
+
+#include <iosfwd>
+#include <list>
+#include <GL/glew.h>
+
+
+namespace blank {
+
+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 Use() const noexcept { glUseProgram(handle); }
+
+private:
+ GLuint handle;
+ std::list<Shader> shaders;
+
+};
+
+}
+
+#endif
--- /dev/null
+#ifndef BLANK_GRAPHICS_SHADER_HPP_
+#define BLANK_GRAPHICS_SHADER_HPP_
+
+#include <iosfwd>
+#include <GL/glew.h>
+
+
+namespace blank {
+
+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
+#include "BlockLighting.hpp"
+#include "DirectionalLighting.hpp"
+#include "Program.hpp"
+#include "Shader.hpp"
+
+#include "../app/init.hpp"
+
+#include <algorithm>
+#include <iostream>
+#include <memory>
+#include <ostream>
+#include <stdexcept>
+#include <string>
+
+
+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 blank {
+
+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);
+}
+
+
+DirectionalLighting::DirectionalLighting()
+: program()
+, light_direction(1.0f, 3.0f, 2.0f)
+, light_color(0.9f, 0.9f, 0.9f)
+, vp(1.0f)
+, m_handle(0)
+, mv_handle(0)
+, mvp_handle(0)
+, light_direction_handle(0)
+, light_color_handle(0)
+, fog_density_handle(0) {
+ program.LoadShader(
+ GL_VERTEX_SHADER,
+ "#version 330 core\n"
+ "layout(location = 0) in vec3 vtx_position;\n"
+ "layout(location = 1) in vec3 vtx_color;\n"
+ "layout(location = 2) in vec3 vtx_normal;\n"
+ "uniform mat4 M;\n"
+ "uniform mat4 MV;\n"
+ "uniform mat4 MVP;\n"
+ "out vec3 frag_color;\n"
+ "out vec3 vtx_viewspace;\n"
+ "out vec3 normal;\n"
+ "void main() {\n"
+ "gl_Position = MVP * vec4(vtx_position, 1);\n"
+ "vtx_viewspace = (MV * vec4(vtx_position, 1)).xyz;\n"
+ "normal = (M * vec4(vtx_normal, 0)).xyz;\n"
+ "frag_color = vtx_color;\n"
+ "}\n"
+ );
+ program.LoadShader(
+ GL_FRAGMENT_SHADER,
+ "#version 330 core\n"
+ "in vec3 frag_color;\n"
+ "in vec3 vtx_viewspace;\n"
+ "in vec3 normal;\n"
+ "uniform vec3 light_direction;\n"
+ "uniform vec3 light_color;\n"
+ "uniform float fog_density;\n"
+ "out vec3 color;\n"
+ "void main() {\n"
+ "vec3 ambient = vec3(0.1, 0.1, 0.1) * frag_color;\n"
+ // this should be the same as the clear color, otherwise looks really weird
+ "vec3 fog_color = vec3(0, 0, 0);\n"
+ "float e = 2.718281828;\n"
+ "vec3 n = normalize(normal);\n"
+ "vec3 l = normalize(light_direction);\n"
+ "float cos_theta = clamp(dot(n, l), 0, 1);\n"
+ "vec3 reflect_color = ambient + frag_color * light_color * cos_theta;\n"
+ "float value = pow(e, -pow(fog_density * length(vtx_viewspace), 5));"
+ "color = mix(fog_color, reflect_color, value);\n"
+ "}\n"
+ );
+ program.Link();
+ if (!program.Linked()) {
+ program.Log(std::cerr);
+ throw std::runtime_error("link program");
+ }
+
+ m_handle = program.UniformLocation("M");
+ mv_handle = program.UniformLocation("MV");
+ mvp_handle = program.UniformLocation("MVP");
+ light_direction_handle = program.UniformLocation("light_direction");
+ light_color_handle = program.UniformLocation("light_color");
+ fog_density_handle = program.UniformLocation("fog_density");
+}
+
+
+void DirectionalLighting::Activate() noexcept {
+ GLContext::EnableDepthTest();
+ GLContext::EnableBackfaceCulling();
+ program.Use();
+
+ glUniform3f(light_direction_handle, light_direction.x, light_direction.y, light_direction.z);
+ glUniform3f(light_color_handle, light_color.x, light_color.y, light_color.z);
+}
+
+void DirectionalLighting::SetM(const glm::mat4 &m) noexcept {
+ glm::mat4 mv(view * m);
+ glm::mat4 mvp(vp * m);
+ glUniformMatrix4fv(m_handle, 1, GL_FALSE, &m[0][0]);
+ glUniformMatrix4fv(mv_handle, 1, GL_FALSE, &mv[0][0]);
+ glUniformMatrix4fv(mvp_handle, 1, GL_FALSE, &mvp[0][0]);
+}
+
+void DirectionalLighting::SetLightDirection(const glm::vec3 &dir) noexcept {
+ light_direction = -dir;
+ glUniform3f(light_direction_handle, light_direction.x, light_direction.y, light_direction.z);
+}
+
+void DirectionalLighting::SetFogDensity(float f) noexcept {
+ fog_density = f;
+ glUniform1f(fog_density_handle, fog_density);
+}
+
+void DirectionalLighting::SetProjection(const glm::mat4 &p) noexcept {
+ projection = p;
+ vp = p * view;
+}
+
+void DirectionalLighting::SetView(const glm::mat4 &v) noexcept {
+ view = v;
+ vp = projection * v;
+}
+
+void DirectionalLighting::SetVP(const glm::mat4 &v, const glm::mat4 &p) noexcept {
+ projection = p;
+ view = v;
+ vp = p * v;
+}
+
+void DirectionalLighting::SetMVP(const glm::mat4 &m, const glm::mat4 &v, const glm::mat4 &p) noexcept {
+ SetVP(v, p);
+ SetM(m);
+}
+
+
+BlockLighting::BlockLighting()
+: program()
+, vp(1.0f)
+, mv_handle(0)
+, mvp_handle(0)
+, fog_density_handle(0) {
+ program.LoadShader(
+ GL_VERTEX_SHADER,
+ "#version 330 core\n"
+ "layout(location = 0) in vec3 vtx_position;\n"
+ "layout(location = 1) in vec3 vtx_color;\n"
+ "layout(location = 2) in float vtx_light;\n"
+ "uniform mat4 MV;\n"
+ "uniform mat4 MVP;\n"
+ "out vec3 frag_color;\n"
+ "out vec3 vtx_viewspace;\n"
+ "out float frag_light;\n"
+ "void main() {\n"
+ "gl_Position = MVP * vec4(vtx_position, 1);\n"
+ "frag_color = vtx_color;\n"
+ "vtx_viewspace = (MV * vec4(vtx_position, 1)).xyz;\n"
+ "frag_light = vtx_light;\n"
+ "}\n"
+ );
+ program.LoadShader(
+ GL_FRAGMENT_SHADER,
+ "#version 330 core\n"
+ "in vec3 frag_color;\n"
+ "in vec3 vtx_viewspace;\n"
+ "in float frag_light;\n"
+ "uniform float fog_density;\n"
+ "out vec3 color;\n"
+ "void main() {\n"
+ "vec3 ambient = vec3(0.1, 0.1, 0.1) * frag_color;\n"
+ "float light_power = clamp(pow(0.8, 15 - frag_light), 0, 1);\n"
+ "vec3 fog_color = vec3(0, 0, 0);\n"
+ "float e = 2.718281828;\n"
+ //"vec3 reflect_color = ambient + frag_color * light_power;\n"
+ "vec3 reflect_color = frag_color * light_power;\n"
+ "float value = pow(e, -pow(fog_density * length(vtx_viewspace), 5));"
+ "color = mix(fog_color, reflect_color, value);\n"
+ "}\n"
+ );
+ program.Link();
+ if (!program.Linked()) {
+ program.Log(std::cerr);
+ throw std::runtime_error("link program");
+ }
+
+ mv_handle = program.UniformLocation("MV");
+ mvp_handle = program.UniformLocation("MVP");
+ fog_density_handle = program.UniformLocation("fog_density");
+}
+
+
+void BlockLighting::Activate() noexcept {
+ GLContext::EnableDepthTest();
+ GLContext::EnableBackfaceCulling();
+ program.Use();
+}
+
+void BlockLighting::SetM(const glm::mat4 &m) noexcept {
+ glm::mat4 mv(view * m);
+ glm::mat4 mvp(vp * m);
+ glUniformMatrix4fv(mv_handle, 1, GL_FALSE, &mv[0][0]);
+ glUniformMatrix4fv(mvp_handle, 1, GL_FALSE, &mvp[0][0]);
+}
+
+void BlockLighting::SetFogDensity(float f) noexcept {
+ fog_density = f;
+ glUniform1f(fog_density_handle, fog_density);
+}
+
+void BlockLighting::SetProjection(const glm::mat4 &p) noexcept {
+ projection = p;
+ vp = p * view;
+}
+
+void BlockLighting::SetView(const glm::mat4 &v) noexcept {
+ view = v;
+ vp = projection * v;
+}
+
+void BlockLighting::SetVP(const glm::mat4 &v, const glm::mat4 &p) noexcept {
+ projection = p;
+ view = v;
+ vp = p * v;
+}
+
+void BlockLighting::SetMVP(const glm::mat4 &m, const glm::mat4 &v, const glm::mat4 &p) noexcept {
+ SetVP(v, p);
+ SetM(m);
+}
+
+}
+++ /dev/null
-#include "hud.hpp"
-
-#include "block.hpp"
-#include "init.hpp"
-#include "shader.hpp"
-#include "shape.hpp"
-
-#include <glm/gtc/matrix_transform.hpp>
-
-
-namespace blank {
-
-HUD::HUD(const BlockTypeRegistry &types)
-: types(types)
-, block()
-, block_buf()
-, block_transform(1.0f)
-, block_visible(false)
-, crosshair()
-, crosshair_transform(1.0f)
-, near(100.0f)
-, far(-100.0f)
-, projection(glm::ortho(0.0f, 1.0f, 1.0f, 0.0f, near, far))
-, view(glm::translate(glm::mat4(1.0f), glm::vec3(-0.5f, -0.5f, 0))) {
- block_transform = glm::translate(block_transform, glm::vec3(50.0f, 50.0f, 0.0f));
- block_transform = glm::scale(block_transform, glm::vec3(50.0f));
- block_transform = glm::rotate(block_transform, 3.5f, glm::vec3(1.0f, 0.0f, 0.0f));
- block_transform = glm::rotate(block_transform, 0.35f, glm::vec3(0.0f, 1.0f, 0.0f));
-
- crosshair.vertices = std::vector<glm::vec3>({
- { -10.0f, 0.0f, 0.0f }, { 10.0f, 0.0f, 0.0f },
- { 0.0f, -10.0f, 0.0f }, { 0.0f, 10.0f, 0.0f },
- });
- crosshair.indices = std::vector<OutlineModel::Index>({
- 0, 1, 2, 3
- });
- crosshair.colors.resize(4, { 10.0f, 10.0f, 10.0f });
- crosshair.Invalidate();
-}
-
-
-void HUD::Viewport(float width, float height) noexcept {
- Viewport(0, 0, width, height);
-}
-
-void HUD::Viewport(float x, float y, float width, float height) noexcept {
- projection = glm::ortho(x, width, height, y, near, far);
- crosshair_transform = glm::translate(glm::mat4(1.0f), glm::vec3(width * 0.5f, height * 0.5f, 0.0f));
-}
-
-
-void HUD::Display(const Block &b) {
- const BlockType &type = types.Get(b.type);
-
- block_buf.Clear();
- type.FillModel(block_buf, b.Transform());
- block.Update(block_buf);
- block_visible = type.visible;
-}
-
-
-void HUD::Render(DirectionalLighting &program) noexcept {
- if (block_visible) {
- program.SetLightDirection({ 1.0f, 3.0f, 5.0f });
- // disable distance fog
- program.SetFogDensity(0.0f);
- GLContext::ClearDepthBuffer();
- program.SetMVP(block_transform, view, projection);
- block.Draw();
- program.SetM(crosshair_transform);
- crosshair.Draw();
- }
-}
-
-}
+++ /dev/null
-#ifndef BLANK_HUD_H_
-#define BLANK_HUD_H_
-
-#include "model.hpp"
-#include "world.hpp"
-
-#include <glm/glm.hpp>
-
-
-namespace blank {
-
-class BlockTypeRegistry;
-class DirectionalLighting;
-
-class HUD {
-
-public:
- explicit HUD(const BlockTypeRegistry &);
-
- HUD(const HUD &) = delete;
- HUD &operator =(const HUD &) = delete;
-
- void Viewport(float width, float height) noexcept;
- void Viewport(float x, float y, float width, float height) noexcept;
-
- void Display(const Block &);
-
- void Render(DirectionalLighting &) noexcept;
-
-private:
- const BlockTypeRegistry &types;
-
- Model block;
- Model::Buffer block_buf;
- glm::mat4 block_transform;
- bool block_visible;
-
- OutlineModel crosshair;
- glm::mat4 crosshair_transform;
-
- float near, far;
- glm::mat4 projection;
- glm::mat4 view;
-
-};
-
-}
-
-#endif
+++ /dev/null
-#include "init.hpp"
-
-#include <algorithm>
-#include <SDL.h>
-#include <SDL_image.h>
-#include <stdexcept>
-#include <string>
-#include <GL/glew.h>
-
-
-namespace {
-
-void sdl_error(std::string msg) {
- const char *error = SDL_GetError();
- if (*error != '\0') {
- msg += ": ";
- msg += error;
- SDL_ClearError();
- }
- throw std::runtime_error(msg);
-}
-
-}
-
-namespace blank {
-
-InitSDL::InitSDL() {
- if (SDL_Init(SDL_INIT_VIDEO) != 0) {
- sdl_error("SDL_Init(SDL_INIT_VIDEO)");
- }
-}
-
-InitSDL::~InitSDL() {
- SDL_Quit();
-}
-
-
-InitIMG::InitIMG() {
- if (IMG_Init(IMG_INIT_PNG) == 0) {
- sdl_error("IMG_Init(IMG_INIT_PNG)");
- }
-}
-
-InitIMG::~InitIMG() {
- IMG_Quit();
-}
-
-
-InitGL::InitGL(bool double_buffer, int sample_size) {
- if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3) != 0) {
- sdl_error("SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3)");
- }
- if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3) != 0) {
- sdl_error("SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3)");
- }
- if (SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE) != 0) {
- sdl_error("SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE)");
- }
-
- if (double_buffer) {
- if (SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1) != 0) {
- sdl_error("SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1)");
- }
- }
-
- if (sample_size > 1) {
- if (SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, 1) != 0) {
- sdl_error("SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS)");
- }
- if (SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, sample_size) != 0) {
- sdl_error("SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES)");
- }
- }
-}
-
-
-Window::Window()
-: handle(SDL_CreateWindow(
- "blank",
- SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
- 960, 600,
- SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE
-)) {
- if (!handle) {
- sdl_error("SDL_CreateWindow");
- }
-}
-
-Window::~Window() {
- SDL_DestroyWindow(handle);
-}
-
-void Window::GrabInput() {
- SDL_SetWindowGrab(handle, SDL_TRUE);
-}
-
-void Window::ReleaseInput() {
- SDL_SetWindowGrab(handle, SDL_FALSE);
-}
-
-void Window::GrabMouse() {
- if (SDL_SetRelativeMouseMode(SDL_TRUE) != 0) {
- sdl_error("SDL_SetRelativeMouseMode");
- }
-}
-
-void Window::ReleaseMouse() {
- if (SDL_SetRelativeMouseMode(SDL_FALSE) != 0) {
- sdl_error("SDL_SetRelativeMouseMode");
- }
-}
-
-GLContext Window::CreateContext() {
- return GLContext(handle);
-}
-
-void Window::Flip() {
- SDL_GL_SwapWindow(handle);
-}
-
-
-GLContext::GLContext(SDL_Window *win)
-: handle(SDL_GL_CreateContext(win)) {
- if (!handle) {
- sdl_error("SDL_GL_CreateContext");
- }
-}
-
-GLContext::~GLContext() {
- if (handle) {
- SDL_GL_DeleteContext(handle);
- }
-}
-
-
-GLContext::GLContext(GLContext &&other)
-: handle(other.handle) {
- other.handle = nullptr;
-}
-
-GLContext &GLContext::operator =(GLContext &&other) {
- std::swap(handle, other.handle);
- return *this;
-}
-
-void GLContext::EnableVSync() {
- if (SDL_GL_SetSwapInterval(1) != 0) {
- sdl_error("SDL_GL_SetSwapInterval");
- }
-}
-
-void GLContext::EnableDepthTest() noexcept {
- glEnable(GL_DEPTH_TEST);
- glDepthFunc(GL_LESS);
-}
-
-void GLContext::EnableBackfaceCulling() noexcept {
- glEnable(GL_CULL_FACE);
-}
-
-void GLContext::Clear() noexcept {
- glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
-}
-
-void GLContext::ClearDepthBuffer() noexcept {
- glClear(GL_DEPTH_BUFFER_BIT);
-}
-
-
-InitGLEW::InitGLEW() {
- glewExperimental = GL_TRUE;
- GLenum glew_err = glewInit();
- if (glew_err != GLEW_OK) {
- std::string msg("glewInit: ");
- const GLubyte *errBegin = glewGetErrorString(glew_err);
- const GLubyte *errEnd = errBegin;
- while (*errEnd != '\0') {
- ++errEnd;
- }
- msg.append(errBegin, errEnd);
- throw std::runtime_error(msg);
- }
-}
-
-}
+++ /dev/null
-#ifndef BLANK_INIT_HPP_
-#define BLANK_INIT_HPP_
-
-#include <SDL.h>
-
-
-namespace blank {
-
-class GLContext;
-
-
-class InitSDL {
-
-public:
- InitSDL();
- ~InitSDL();
-
- InitSDL(const InitSDL &) = delete;
- InitSDL &operator =(const InitSDL &) = delete;
-
-};
-
-
-class InitIMG {
-
-public:
- InitIMG();
- ~InitIMG();
-
- InitIMG(const InitIMG &) = delete;
- InitIMG &operator =(const InitIMG &) = delete;
-
-};
-
-
-class InitGL {
-
-public:
- explicit InitGL(bool double_buffer = true, int sample_size = 1);
-
- InitGL(const InitGL &) = delete;
- InitGL &operator =(const InitGL &) = delete;
-
-};
-
-
-class Window {
-
-public:
- Window();
- ~Window();
-
- Window(const Window &) = delete;
- Window &operator =(const Window &) = delete;
-
- void GrabInput();
- void ReleaseInput();
-
- void GrabMouse();
- void ReleaseMouse();
-
- GLContext CreateContext();
-
- void Flip();
-
-private:
- SDL_Window *handle;
-
-};
-
-
-class GLContext {
-
-public:
- explicit GLContext(SDL_Window *);
- ~GLContext();
-
- GLContext(GLContext &&);
- GLContext &operator =(GLContext &&);
-
- GLContext(const GLContext &) = delete;
- GLContext &operator =(const GLContext &) = delete;
-
- static void EnableVSync();
- static void EnableDepthTest() noexcept;
- static void EnableBackfaceCulling() noexcept;
-
- static void Clear() noexcept;
- static void ClearDepthBuffer() noexcept;
-
-private:
- SDL_GLContext handle;
-
-};
-
-
-class InitGLEW {
-
-public:
- InitGLEW();
-
- InitGLEW(const InitGLEW &) = delete;
- InitGLEW &operator =(const InitGLEW &) = delete;
-
-};
-
-}
-
-#endif
+++ /dev/null
-#include "interface.hpp"
-
-#include "world.hpp"
-
-#include <iostream>
-#include <glm/gtc/matrix_transform.hpp>
-#include <glm/gtx/io.hpp>
-
-
-namespace blank {
-
-Interface::Interface(const Config &config, World &world)
-: world(world)
-, ctrl(world.Player())
-, hud(world.BlockTypes())
-, aim{{ 0, 0, 0 }, { 0, 0, -1 }}
-, aim_chunk(nullptr)
-, aim_block(0)
-, aim_normal()
-, outline()
-, outline_transform(1.0f)
-, config(config)
-, place_timer(256)
-, remove_timer(256)
-, remove(0)
-, selection(1)
-, fwd(0)
-, rev(0) {
- hud.Viewport(960, 600);
- hud.Display(selection);
-}
-
-
-void Interface::HandlePress(const SDL_KeyboardEvent &event) {
- if (config.keyboard_disabled) return;
-
- switch (event.keysym.sym) {
- case SDLK_w:
- rev.z = 1;
- break;
- case SDLK_s:
- fwd.z = 1;
- break;
- case SDLK_a:
- rev.x = 1;
- break;
- case SDLK_d:
- fwd.x = 1;
- break;
- case SDLK_SPACE:
- fwd.y = 1;
- break;
- case SDLK_LSHIFT:
- rev.y = 1;
- break;
-
- case SDLK_q:
- FaceBlock();
- break;
- case SDLK_e:
- TurnBlock();
- break;
-
- case SDLK_b:
- PrintBlockInfo();
- break;
- case SDLK_c:
- PrintChunkInfo();
- break;
- case SDLK_l:
- PrintLightInfo();
- break;
- case SDLK_p:
- PrintSelectionInfo();
- break;
- }
-}
-
-void Interface::HandleRelease(const SDL_KeyboardEvent &event) {
- if (config.keyboard_disabled) return;
-
- switch (event.keysym.sym) {
- case SDLK_w:
- rev.z = 0;
- break;
- case SDLK_s:
- fwd.z = 0;
- break;
- case SDLK_a:
- rev.x = 0;
- break;
- case SDLK_d:
- fwd.x = 0;
- break;
- case SDLK_SPACE:
- fwd.y = 0;
- break;
- case SDLK_LSHIFT:
- rev.y = 0;
- break;
- }
-}
-
-void Interface::FaceBlock() {
- selection.SetFace(Block::Face((selection.GetFace() + 1) % Block::FACE_COUNT));
- hud.Display(selection);
-}
-
-void Interface::TurnBlock() {
- selection.SetTurn(Block::Turn((selection.GetTurn() + 1) % Block::TURN_COUNT));
- hud.Display(selection);
-}
-
-void Interface::PrintBlockInfo() {
- std::cout << std::endl;
- if (!aim_chunk) {
- std::cout << "not looking at any block" << std::endl;
- Ray aim = ctrl.Aim();
- std::cout << "aim ray: " << aim.orig << ", " << aim.dir << std::endl;
- return;
- }
- std::cout << "looking at block " << aim_block
- << " " << Chunk::ToCoords(aim_block)
- << " of chunk " << aim_chunk->Position()
- << std::endl;
- Print(aim_chunk->BlockAt(aim_block));
-}
-
-void Interface::PrintChunkInfo() {
- std::cout << std::endl;
- if (!aim_chunk) {
- std::cout << "not looking at any block" << std::endl;
- return;
- }
- std::cout << "looking at chunk "
- << aim_chunk->Position()
- << std::endl;
-
- std::cout << " neighbors:" << std::endl;
- if (aim_chunk->HasNeighbor(Block::FACE_LEFT)) {
- std::cout << " left " << aim_chunk->GetNeighbor(Block::FACE_LEFT).Position() << std::endl;
- }
- if (aim_chunk->HasNeighbor(Block::FACE_RIGHT)) {
- std::cout << " right " << aim_chunk->GetNeighbor(Block::FACE_RIGHT).Position() << std::endl;
- }
- if (aim_chunk->HasNeighbor(Block::FACE_UP)) {
- std::cout << " up " << aim_chunk->GetNeighbor(Block::FACE_UP).Position() << std::endl;
- }
- if (aim_chunk->HasNeighbor(Block::FACE_DOWN)) {
- std::cout << " down " << aim_chunk->GetNeighbor(Block::FACE_DOWN).Position() << std::endl;
- }
- if (aim_chunk->HasNeighbor(Block::FACE_FRONT)) {
- std::cout << " front " << aim_chunk->GetNeighbor(Block::FACE_FRONT).Position() << std::endl;
- }
- if (aim_chunk->HasNeighbor(Block::FACE_BACK)) {
- std::cout << " back " << aim_chunk->GetNeighbor(Block::FACE_BACK).Position() << std::endl;
- }
- std::cout << std::endl;
-}
-
-void Interface::PrintLightInfo() {
- std::cout
- << "light level " << world.PlayerChunk().GetLight(world.Player().Position())
- << " at position " << world.Player().Position()
- << std::endl;
-}
-
-void Interface::PrintSelectionInfo() {
- std::cout << std::endl;
- Print(selection);
-}
-
-void Interface::Print(const Block &block) {
- std::cout << "type: " << block.type
- << ", face: " << block.GetFace()
- << ", turn: " << block.GetTurn()
- << std::endl;
-}
-
-
-void Interface::Handle(const SDL_MouseMotionEvent &event) {
- if (config.mouse_disabled) return;
- ctrl.RotateYaw(event.xrel * config.yaw_sensitivity);
- ctrl.RotatePitch(event.yrel * config.pitch_sensitivity);
-}
-
-void Interface::HandlePress(const SDL_MouseButtonEvent &event) {
- if (config.mouse_disabled) return;
-
- if (event.button == 1) {
- RemoveBlock();
- remove_timer.Start();
- } else if (event.button == 2) {
- PickBlock();
- } else if (event.button == 3) {
- PlaceBlock();
- place_timer.Start();
- }
-}
-
-void Interface::HandleRelease(const SDL_MouseButtonEvent &event) {
- if (config.mouse_disabled) return;
-
- if (event.button == 1) {
- remove_timer.Stop();
- } else if (event.button == 3) {
- place_timer.Stop();
- }
-}
-
-void Interface::PickBlock() {
- if (!aim_chunk) return;
- selection = aim_chunk->BlockAt(aim_block);
- hud.Display(selection);
-}
-
-void Interface::PlaceBlock() {
- if (!aim_chunk) return;
- Chunk *mod_chunk = aim_chunk;
- glm::vec3 next_pos = Chunk::ToCoords(aim_block) + aim_normal;
- if (!Chunk::InBounds(next_pos)) {
- mod_chunk = &world.Next(*aim_chunk, aim_normal);
- next_pos -= aim_normal * glm::vec3(Chunk::Extent());
- }
- mod_chunk->SetBlock(next_pos, selection);
- mod_chunk->Invalidate();
-}
-
-void Interface::RemoveBlock() noexcept {
- if (!aim_chunk) return;
- aim_chunk->SetBlock(aim_block, remove);
- aim_chunk->Invalidate();
-}
-
-
-void Interface::Handle(const SDL_MouseWheelEvent &event) {
- if (config.mouse_disabled) return;
-
- if (event.y < 0) {
- SelectNext();
- } else if (event.y > 0) {
- SelectPrevious();
- }
-}
-
-void Interface::SelectNext() {
- ++selection.type;
- if (size_t(selection.type) >= world.BlockTypes().Size()) {
- selection.type = 1;
- }
- hud.Display(selection);
-}
-
-void Interface::SelectPrevious() {
- --selection.type;
- if (selection.type <= 0) {
- selection.type = world.BlockTypes().Size() - 1;
- }
- hud.Display(selection);
-}
-
-void Interface::Handle(const SDL_WindowEvent &event) noexcept {
- if (event.event == SDL_WINDOWEVENT_RESIZED) {
- hud.Viewport(event.data1, event.data2);
- }
-}
-
-
-void Interface::Update(int dt) {
- ctrl.Velocity(glm::vec3(fwd - rev) * config.move_velocity);
- ctrl.Update(dt);
-
- place_timer.Update(dt);
- remove_timer.Update(dt);
-
- aim = ctrl.Aim();
- CheckAim();
-
- if (remove_timer.Hit()) {
- RemoveBlock();
- CheckAim();
- }
-
- if (place_timer.Hit()) {
- PlaceBlock();
- CheckAim();
- }
-}
-
-void Interface::CheckAim() {
- float dist;
- if (world.Intersection(aim, glm::mat4(1.0f), &aim_chunk, &aim_block, &dist, &aim_normal)) {
- outline.Clear();
- aim_chunk->Type(aim_chunk->BlockAt(aim_block)).FillOutlineModel(outline);
- outline_transform = glm::scale(glm::vec3(1.0002f));
- outline_transform *= aim_chunk->Transform(world.Player().ChunkCoords());
- outline_transform *= aim_chunk->ToTransform(Chunk::ToPos(aim_block), aim_block);
- } else {
- aim_chunk = nullptr;
- }
-}
-
-
-void Interface::Render(DirectionalLighting &program) noexcept {
- if (config.visual_disabled) return;
-
- if (aim_chunk) {
- program.SetM(outline_transform);
- outline.Draw();
- }
-
- hud.Render(program);
-}
-
-}
+++ /dev/null
-#ifndef BLANK_INTERFACE_HPP_
-#define BLANK_INTERFACE_HPP_
-
-#include "block.hpp"
-#include "controller.hpp"
-#include "geometry.hpp"
-#include "hud.hpp"
-#include "model.hpp"
-#include "shader.hpp"
-#include "timer.hpp"
-
-#include <SDL.h>
-#include <glm/glm.hpp>
-
-
-namespace blank {
-
-class Chunk;
-class World;
-
-class Interface {
-
-public:
- struct Config {
- float move_velocity = 0.005f;
- float pitch_sensitivity = -0.0025f;
- float yaw_sensitivity = -0.001f;
-
- bool keyboard_disabled = false;
- bool mouse_disabled = false;
- bool visual_disabled = false;
- };
-
- Interface(const Config &, World &);
-
- void HandlePress(const SDL_KeyboardEvent &);
- void HandleRelease(const SDL_KeyboardEvent &);
- void Handle(const SDL_MouseMotionEvent &);
- void HandlePress(const SDL_MouseButtonEvent &);
- void HandleRelease(const SDL_MouseButtonEvent &);
- void Handle(const SDL_MouseWheelEvent &);
- void Handle(const SDL_WindowEvent &) noexcept;
-
- void FaceBlock();
- void TurnBlock();
-
- void PickBlock();
- void PlaceBlock();
- void RemoveBlock() noexcept;
-
- void PrintBlockInfo();
- void PrintChunkInfo();
- void PrintLightInfo();
- void PrintSelectionInfo();
- void Print(const Block &);
-
- void SelectNext();
- void SelectPrevious();
-
- void Update(int dt);
-
- void Render(DirectionalLighting &) noexcept;
-
-private:
- void CheckAim();
-
-private:
- World &world;
- FPSController ctrl;
- HUD hud;
-
- Ray aim;
- Chunk *aim_chunk;
- int aim_block;
- glm::vec3 aim_normal;
-
- OutlineModel outline;
- glm::mat4 outline_transform;
-
- Config config;
-
- IntervalTimer place_timer;
- IntervalTimer remove_timer;
-
- Block remove;
- Block selection;
-
- glm::tvec3<int> fwd, rev;
-
-};
-
-}
-
-#endif
+++ /dev/null
-#include "runtime.hpp"
-
-using namespace blank;
-
-int main(int argc, char *argv[]) {
- Runtime rt;
- rt.ReadArgs(argc, argv);
- return rt.Execute();
-}
+++ /dev/null
-#include "model.hpp"
-
-#include <iostream>
-
-
-namespace blank {
-
-Model::Model() noexcept
-: va(0)
-, handle{}
-, count(0) {
- glGenVertexArrays(1, &va);
- glGenBuffers(ATTRIB_COUNT, handle);
-}
-
-Model::~Model() noexcept {
- glDeleteBuffers(ATTRIB_COUNT, handle);
- glDeleteVertexArrays(1, &va);
-}
-
-Model::Model(Model &&other) noexcept
-: va(other.va)
-, count(other.count) {
- other.va = 0;
- for (int i = 0; i < ATTRIB_COUNT; ++i) {
- handle[i] = other.handle[i];
- other.handle[i] = 0;
- }
-}
-
-Model &Model::operator =(Model &&other) noexcept {
- std::swap(va, other.va);
- for (int i = 0; i < ATTRIB_COUNT; ++i) {
- std::swap(handle[i], other.handle[i]);
- }
- count = other.count;
- return *this;
-}
-
-void Model::Update(const Buffer &buf) noexcept {
- glBindVertexArray(va);
- glBindBuffer(GL_ARRAY_BUFFER, handle[ATTRIB_VERTEX]);
- glBufferData(GL_ARRAY_BUFFER, buf.vertices.size() * sizeof(glm::vec3), buf.vertices.data(), GL_STATIC_DRAW);
- glEnableVertexAttribArray(ATTRIB_VERTEX);
- glVertexAttribPointer(
- ATTRIB_VERTEX, // location (for shader)
- 3, // size
- GL_FLOAT, // type
- GL_FALSE, // normalized
- 0, // stride
- nullptr // offset
- );
-
-#ifndef NDEBUG
- if (buf.colors.size() < buf.vertices.size()) {
- std::cerr << "Model: not enough colors!" << std::endl;
- }
-#endif
- glBindBuffer(GL_ARRAY_BUFFER, handle[ATTRIB_COLOR]);
- glBufferData(GL_ARRAY_BUFFER, buf.colors.size() * sizeof(glm::vec3), buf.colors.data(), GL_STATIC_DRAW);
- glEnableVertexAttribArray(ATTRIB_COLOR);
- glVertexAttribPointer(
- ATTRIB_COLOR, // location (for shader)
- 3, // size
- GL_FLOAT, // type
- GL_FALSE, // normalized
- 0, // stride
- nullptr // offset
- );
-
-#ifndef NDEBUG
- if (buf.normals.size() < buf.vertices.size()) {
- std::cerr << "Model: not enough normals!" << std::endl;
- }
-#endif
- glBindBuffer(GL_ARRAY_BUFFER, handle[ATTRIB_NORMAL]);
- glBufferData(GL_ARRAY_BUFFER, buf.normals.size() * sizeof(glm::vec3), buf.normals.data(), GL_STATIC_DRAW);
- glEnableVertexAttribArray(ATTRIB_NORMAL);
- glVertexAttribPointer(
- ATTRIB_NORMAL, // location (for shader)
- 3, // size
- GL_FLOAT, // type
- GL_FALSE, // normalized
- 0, // stride
- nullptr // offset
- );
-
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, handle[ATTRIB_INDEX]);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, buf.indices.size() * sizeof(Index), buf.indices.data(), GL_STATIC_DRAW);
- count = buf.indices.size();
-}
-
-
-void Model::Draw() const noexcept {
- glBindVertexArray(va);
- glDrawElements(
- GL_TRIANGLES, // how
- count, // count
- GL_UNSIGNED_INT, // type
- nullptr // offset
- );
-}
-
-
-BlockModel::BlockModel() noexcept
-: va(0)
-, handle{}
-, count(0) {
- glGenVertexArrays(1, &va);
- glGenBuffers(ATTRIB_COUNT, handle);
-}
-
-BlockModel::~BlockModel() noexcept {
- glDeleteBuffers(ATTRIB_COUNT, handle);
- glDeleteVertexArrays(1, &va);
-}
-
-BlockModel::BlockModel(BlockModel &&other) noexcept
-: va(other.va)
-, count(other.count) {
- other.va = 0;
- for (int i = 0; i < ATTRIB_COUNT; ++i) {
- handle[i] = other.handle[i];
- other.handle[i] = 0;
- }
-}
-
-BlockModel &BlockModel::operator =(BlockModel &&other) noexcept {
- std::swap(va, other.va);
- for (int i = 0; i < ATTRIB_COUNT; ++i) {
- std::swap(handle[i], other.handle[i]);
- }
- count = other.count;
- return *this;
-}
-
-void BlockModel::Update(const Buffer &buf) noexcept {
- glBindVertexArray(va);
- glBindBuffer(GL_ARRAY_BUFFER, handle[ATTRIB_VERTEX]);
- glBufferData(GL_ARRAY_BUFFER, buf.vertices.size() * sizeof(glm::vec3), buf.vertices.data(), GL_STATIC_DRAW);
- glEnableVertexAttribArray(ATTRIB_VERTEX);
- glVertexAttribPointer(
- ATTRIB_VERTEX, // location (for shader)
- 3, // size
- GL_FLOAT, // type
- GL_FALSE, // normalized
- 0, // stride
- nullptr // offset
- );
-
-#ifndef NDEBUG
- if (buf.colors.size() < buf.vertices.size()) {
- std::cerr << "BlockModel: not enough colors!" << std::endl;
- }
-#endif
- glBindBuffer(GL_ARRAY_BUFFER, handle[ATTRIB_COLOR]);
- glBufferData(GL_ARRAY_BUFFER, buf.colors.size() * sizeof(glm::vec3), buf.colors.data(), GL_STATIC_DRAW);
- glEnableVertexAttribArray(ATTRIB_COLOR);
- glVertexAttribPointer(
- ATTRIB_COLOR, // location (for shader)
- 3, // size
- GL_FLOAT, // type
- GL_FALSE, // normalized
- 0, // stride
- nullptr // offset
- );
-
-#ifndef NDEBUG
- if (buf.lights.size() < buf.vertices.size()) {
- std::cerr << "BlockModel: not enough lights!" << std::endl;
- }
-#endif
- glBindBuffer(GL_ARRAY_BUFFER, handle[ATTRIB_LIGHT]);
- glBufferData(GL_ARRAY_BUFFER, buf.lights.size() * sizeof(float), buf.lights.data(), GL_STATIC_DRAW);
- glEnableVertexAttribArray(ATTRIB_LIGHT);
- glVertexAttribPointer(
- ATTRIB_LIGHT, // location (for shader)
- 1, // size
- GL_FLOAT, // type
- GL_FALSE, // normalized
- 0, // stride
- nullptr // offset
- );
-
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, handle[ATTRIB_INDEX]);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, buf.indices.size() * sizeof(Index), buf.indices.data(), GL_STATIC_DRAW);
- count = buf.indices.size();
-}
-
-
-void BlockModel::Draw() const noexcept {
- glBindVertexArray(va);
- glDrawElements(
- GL_TRIANGLES, // how
- count, // count
- GL_UNSIGNED_INT, // type
- nullptr // offset
- );
-}
-
-OutlineModel::OutlineModel() noexcept
-: vertices()
-, colors()
-, indices()
-, va(0)
-, handle{}
-, dirty(false) {
- glGenVertexArrays(1, &va);
- glGenBuffers(ATTRIB_COUNT, handle);
-}
-
-OutlineModel::~OutlineModel() noexcept {
- glDeleteBuffers(ATTRIB_COUNT, handle);
- glDeleteVertexArrays(1, &va);
-}
-
-
-void OutlineModel::Clear() noexcept {
- vertices.clear();
- colors.clear();
- indices.clear();
- Invalidate();
-}
-
-void OutlineModel::Reserve(int v, int i) {
- vertices.reserve(v);
- colors.reserve(v);
- indices.reserve(i);
-}
-
-
-void OutlineModel::Update() noexcept {
- glBindBuffer(GL_ARRAY_BUFFER, handle[ATTRIB_VERTEX]);
- glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(glm::vec3), vertices.data(), GL_STATIC_DRAW);
- glEnableVertexAttribArray(ATTRIB_VERTEX);
- glVertexAttribPointer(
- ATTRIB_VERTEX, // location (for shader)
- 3, // size
- GL_FLOAT, // type
- GL_FALSE, // normalized
- 0, // stride
- nullptr // offset
- );
-
-#ifndef NDEBUG
- if (colors.size() < vertices.size()) {
- std::cerr << "OutlineModel: not enough colors!" << std::endl;
- colors.resize(vertices.size(), { 1, 0, 1 });
- }
-#endif
- glBindBuffer(GL_ARRAY_BUFFER, handle[ATTRIB_COLOR]);
- glBufferData(GL_ARRAY_BUFFER, colors.size() * sizeof(glm::vec3), colors.data(), GL_STATIC_DRAW);
- glEnableVertexAttribArray(ATTRIB_COLOR);
- glVertexAttribPointer(
- ATTRIB_COLOR, // location (for shader)
- 3, // size
- GL_FLOAT, // type
- GL_FALSE, // normalized
- 0, // stride
- nullptr // offset
- );
-
- glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, handle[ATTRIB_INDEX]);
- glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(Index), indices.data(), GL_STATIC_DRAW);
-
- dirty = false;
-}
-
-
-void OutlineModel::Draw() noexcept {
- glBindVertexArray(va);
-
- if (dirty) {
- Update();
- }
-
- glEnable(GL_LINE_SMOOTH);
- glLineWidth(2.0f);
-
- glDrawElements(
- GL_LINES, // how
- indices.size(), // count
- GL_UNSIGNED_SHORT, // type
- nullptr // offset
- );
-}
-
-}
+++ /dev/null
-#ifndef BLANK_MODEL_HPP_
-#define BLANK_MODEL_HPP_
-
-#include <vector>
-#include <GL/glew.h>
-#include <glm/glm.hpp>
-
-
-namespace blank {
-
-class Model {
-
-public:
- using Position = glm::vec3;
- using Color = glm::vec3;
- using Normal = glm::vec3;
- using Index = unsigned int;
-
- using Positions = std::vector<Position>;
- using Colors = std::vector<Color>;
- using Normals = std::vector<Normal>;
- using Indices = std::vector<Index>;
-
-public:
- struct Buffer {
-
- Positions vertices;
- Colors colors;
- Normals normals;
- Indices indices;
-
- void Clear() noexcept {
- vertices.clear();
- colors.clear();
- normals.clear();
- indices.clear();
- }
-
- void Reserve(size_t p, size_t i) {
- vertices.reserve(p);
- colors.reserve(p);
- normals.reserve(p);
- indices.reserve(i);
- }
-
- };
-
-public:
- Model() noexcept;
- ~Model() noexcept;
-
- Model(const Model &) = delete;
- Model &operator =(const Model &) = delete;
-
- Model(Model &&) noexcept;
- Model &operator =(Model &&) noexcept;
-
- void Update(const Buffer &) noexcept;
-
- void Draw() const noexcept;
-
-private:
- enum Attribute {
- ATTRIB_VERTEX,
- ATTRIB_COLOR,
- ATTRIB_NORMAL,
- ATTRIB_INDEX,
- ATTRIB_COUNT,
- };
-
- GLuint va;
- GLuint handle[ATTRIB_COUNT];
- size_t count;
-
-};
-
-
-class BlockModel {
-
-public:
- using Position = glm::vec3;
- using Color = glm::vec3;
- using Light = float;
- using Index = unsigned int;
-
- using Positions = std::vector<Position>;
- using Colors = std::vector<Color>;
- using Lights = std::vector<Light>;
- using Indices = std::vector<Index>;
-
-public:
- struct Buffer {
-
- Positions vertices;
- Colors colors;
- Lights lights;
- Indices indices;
-
- void Clear() noexcept {
- vertices.clear();
- colors.clear();
- lights.clear();
- indices.clear();
- }
-
- void Reserve(size_t p, size_t i) {
- vertices.reserve(p);
- colors.reserve(p);
- lights.reserve(p);
- indices.reserve(i);
- }
-
- };
-
-public:
- BlockModel() noexcept;
- ~BlockModel() noexcept;
-
- BlockModel(const BlockModel &) = delete;
- BlockModel &operator =(const Model &) = delete;
-
- BlockModel(BlockModel &&) noexcept;
- BlockModel &operator =(BlockModel &&) noexcept;
-
- void Update(const Buffer &) noexcept;
-
- void Draw() const noexcept;
-
-private:
- enum Attribute {
- ATTRIB_VERTEX,
- ATTRIB_COLOR,
- ATTRIB_LIGHT,
- ATTRIB_INDEX,
- ATTRIB_COUNT,
- };
-
- GLuint va;
- GLuint handle[ATTRIB_COUNT];
- size_t count;
-
-};
-
-
-class OutlineModel {
-
-public:
- using Position = glm::vec3;
- using Color = glm::vec3;
- using Index = unsigned short;
-
- using Positions = std::vector<Position>;
- using Colors = std::vector<Color>;
- using Indices = std::vector<Index>;
-
-public:
- Positions vertices;
- Colors colors;
- Indices indices;
-
-public:
- OutlineModel() noexcept;
- ~OutlineModel() noexcept;
-
- OutlineModel(const OutlineModel &) = delete;
- OutlineModel &operator =(const OutlineModel &) = delete;
-
- void Invalidate() noexcept { dirty = true; }
-
- void Clear() noexcept;
- void Reserve(int vtx_count, int idx_count);
-
- void Draw() noexcept;
-
-private:
- void Update() noexcept;
-
-private:
- enum Attribute {
- ATTRIB_VERTEX,
- ATTRIB_COLOR,
- ATTRIB_INDEX,
- ATTRIB_COUNT,
- };
-
- GLuint va;
- GLuint handle[ATTRIB_COUNT];
- bool dirty;
-
-};
-
-}
-
-#endif
--- /dev/null
+#ifndef BLANK_MODEL_BLOCKMODEL_HPP_
+#define BLANK_MODEL_BLOCKMODEL_HPP_
+
+#include <vector>
+#include <GL/glew.h>
+#include <glm/glm.hpp>
+
+
+namespace blank {
+
+class BlockModel {
+
+public:
+ using Position = glm::vec3;
+ using Color = glm::vec3;
+ using Light = float;
+ using Index = unsigned int;
+
+ using Positions = std::vector<Position>;
+ using Colors = std::vector<Color>;
+ using Lights = std::vector<Light>;
+ using Indices = std::vector<Index>;
+
+public:
+ struct Buffer {
+
+ Positions vertices;
+ Colors colors;
+ Lights lights;
+ Indices indices;
+
+ void Clear() noexcept {
+ vertices.clear();
+ colors.clear();
+ lights.clear();
+ indices.clear();
+ }
+
+ void Reserve(size_t p, size_t i) {
+ vertices.reserve(p);
+ colors.reserve(p);
+ lights.reserve(p);
+ indices.reserve(i);
+ }
+
+ };
+
+public:
+ BlockModel() noexcept;
+ ~BlockModel() noexcept;
+
+ BlockModel(const BlockModel &) = delete;
+ BlockModel &operator =(const BlockModel &) = delete;
+
+ BlockModel(BlockModel &&) noexcept;
+ BlockModel &operator =(BlockModel &&) noexcept;
+
+ void Update(const Buffer &) noexcept;
+
+ void Draw() const noexcept;
+
+private:
+ enum Attribute {
+ ATTRIB_VERTEX,
+ ATTRIB_COLOR,
+ ATTRIB_LIGHT,
+ ATTRIB_INDEX,
+ ATTRIB_COUNT,
+ };
+
+ GLuint va;
+ GLuint handle[ATTRIB_COUNT];
+ size_t count;
+
+};
+
+}
+
+#endif
--- /dev/null
+#ifndef BLANK_MODEL_MODEL_HPP_
+#define BLANK_MODEL_MODEL_HPP_
+
+#include <vector>
+#include <GL/glew.h>
+#include <glm/glm.hpp>
+
+
+namespace blank {
+
+class Model {
+
+public:
+ using Position = glm::vec3;
+ using Color = glm::vec3;
+ using Normal = glm::vec3;
+ using Index = unsigned int;
+
+ using Positions = std::vector<Position>;
+ using Colors = std::vector<Color>;
+ using Normals = std::vector<Normal>;
+ using Indices = std::vector<Index>;
+
+public:
+ struct Buffer {
+
+ Positions vertices;
+ Colors colors;
+ Normals normals;
+ Indices indices;
+
+ void Clear() noexcept {
+ vertices.clear();
+ colors.clear();
+ normals.clear();
+ indices.clear();
+ }
+
+ void Reserve(size_t p, size_t i) {
+ vertices.reserve(p);
+ colors.reserve(p);
+ normals.reserve(p);
+ indices.reserve(i);
+ }
+
+ };
+
+public:
+ Model() noexcept;
+ ~Model() noexcept;
+
+ Model(const Model &) = delete;
+ Model &operator =(const Model &) = delete;
+
+ Model(Model &&) noexcept;
+ Model &operator =(Model &&) noexcept;
+
+ void Update(const Buffer &) noexcept;
+
+ void Draw() const noexcept;
+
+private:
+ enum Attribute {
+ ATTRIB_VERTEX,
+ ATTRIB_COLOR,
+ ATTRIB_NORMAL,
+ ATTRIB_INDEX,
+ ATTRIB_COUNT,
+ };
+
+ GLuint va;
+ GLuint handle[ATTRIB_COUNT];
+ size_t count;
+
+};
+
+}
+
+#endif
--- /dev/null
+#ifndef BLANK_MODEL_OUTLINEMODEL_HPP_
+#define BLANK_MODEL_OUTLINEMODEL_HPP_
+
+#include <vector>
+#include <GL/glew.h>
+#include <glm/glm.hpp>
+
+
+namespace blank {
+
+class OutlineModel {
+
+public:
+ using Position = glm::vec3;
+ using Color = glm::vec3;
+ using Index = unsigned short;
+
+ using Positions = std::vector<Position>;
+ using Colors = std::vector<Color>;
+ using Indices = std::vector<Index>;
+
+public:
+ Positions vertices;
+ Colors colors;
+ Indices indices;
+
+public:
+ OutlineModel() noexcept;
+ ~OutlineModel() noexcept;
+
+ OutlineModel(const OutlineModel &) = delete;
+ OutlineModel &operator =(const OutlineModel &) = delete;
+
+ void Invalidate() noexcept { dirty = true; }
+
+ void Clear() noexcept;
+ void Reserve(int vtx_count, int idx_count);
+
+ void Draw() noexcept;
+
+private:
+ void Update() noexcept;
+
+private:
+ enum Attribute {
+ ATTRIB_VERTEX,
+ ATTRIB_COLOR,
+ ATTRIB_INDEX,
+ ATTRIB_COUNT,
+ };
+
+ GLuint va;
+ GLuint handle[ATTRIB_COUNT];
+ bool dirty;
+
+};
+
+}
+
+#endif
--- /dev/null
+#ifndef BLANK_MODEL_SHAPE_HPP_
+#define BLANK_MODEL_SHAPE_HPP_
+
+#include "BlockModel.hpp"
+#include "Model.hpp"
+#include "OutlineModel.hpp"
+
+#include <vector>
+#include <glm/glm.hpp>
+
+
+namespace blank {
+
+class Ray;
+
+struct Shape {
+
+ /// the number of vertices (and normals) this shape has
+ size_t VertexCount() const noexcept { return vtx_pos.size(); }
+ /// the number of vertex indices this shape has
+ size_t VertexIndexCount() const noexcept { return vtx_idx.size(); }
+
+ const Model::Normal &VertexNormal(size_t idx) const noexcept { return vtx_nrm[idx]; }
+ Model::Normal VertexNormal(
+ size_t idx, const glm::mat4 &transform
+ ) const noexcept {
+ return Model::Normal(transform * glm::vec4(vtx_nrm[idx], 0.0f));
+ }
+
+ /// fill given buffers with this shape's elements with an
+ /// optional transform and offset
+ void Vertices(
+ Model::Positions &vertex,
+ Model::Normals &normal,
+ Model::Indices &index
+ ) const;
+ void Vertices(
+ Model::Positions &vertex,
+ Model::Normals &normal,
+ Model::Indices &index,
+ const glm::mat4 &transform,
+ Model::Index idx_offset = 0
+ ) const;
+ void Vertices(
+ BlockModel::Positions &vertex,
+ BlockModel::Indices &index,
+ const glm::mat4 &transform,
+ BlockModel::Index idx_offset = 0
+ ) const;
+
+ /// the number of vertices this shape's outline has
+ size_t OutlineCount() const { return out_pos.size(); }
+ /// the number of vertex indices this shape's outline has
+ size_t OutlineIndexCount() const { return out_idx.size(); }
+
+ /// fill given buffers with this shape's outline's elements with
+ /// an optional offset
+ void Outline(
+ OutlineModel::Positions &vertex,
+ OutlineModel::Indices &index,
+ const OutlineModel::Position &offset = { 0.0f, 0.0f, 0.0f },
+ OutlineModel::Index idx_offset = 0
+ ) const;
+
+ /// Check if given ray would pass though this shape if it were
+ /// transformed with given matrix.
+ /// If true, dist and normal hold the intersection distance and
+ /// normal, otherwise their content is undefined.
+ virtual bool Intersects(
+ const Ray &,
+ const glm::mat4 &,
+ float &dist,
+ glm::vec3 &normal
+ ) const noexcept = 0;
+
+protected:
+ void SetShape(const Model::Positions &pos, const Model::Normals &nrm, const Model::Indices &idx) {
+ vtx_pos = pos;
+ vtx_nrm = nrm;
+ vtx_idx = idx;
+ }
+ void SetOutline(const OutlineModel::Positions &pos, const OutlineModel::Indices &idx) {
+ out_pos = pos;
+ out_idx = idx;
+ }
+
+private:
+ Model::Positions vtx_pos;
+ Model::Normals vtx_nrm;
+ Model::Indices vtx_idx;
+
+ OutlineModel::Positions out_pos;
+ OutlineModel::Indices out_idx;
+
+};
+
+}
+
+#endif
--- /dev/null
+#include "geometry.hpp"
+
+#include <limits>
+
+
+namespace blank {
+
+bool Intersection(
+ const Ray &ray,
+ const AABB &aabb,
+ const glm::mat4 &M,
+ float *dist,
+ glm::vec3 *normal
+) noexcept {
+ float t_min = 0.0f;
+ float t_max = std::numeric_limits<float>::infinity();
+ const glm::vec3 aabb_pos(M[3].x, M[3].y, M[3].z);
+ const glm::vec3 delta = aabb_pos - ray.orig;
+
+ glm::vec3 t1(t_min, t_min, t_min), t2(t_max, t_max, t_max);
+
+ for (int i = 0; i < 3; ++i) {
+ const glm::vec3 axis(M[i].x, M[i].y, M[i].z);
+ const float e = glm::dot(axis, delta);
+ const float f = glm::dot(axis, ray.dir);
+
+ if (std::abs(f) > std::numeric_limits<float>::epsilon()) {
+ t1[i] = (e + aabb.min[i]) / f;
+ t2[i] = (e + aabb.max[i]) / f;
+
+ t_min = std::max(t_min, std::min(t1[i], t2[i]));
+ t_max = std::min(t_max, std::max(t1[i], t2[i]));
+
+ if (t_max < t_min) {
+ return false;
+ }
+ } else {
+ if (aabb.min[i] - e < 0.0f || -aabb.max[i] - e > 0.0f) {
+ return false;
+ }
+ }
+ }
+
+ glm::vec3 min_all(min(t1, t2));
+
+ if (dist) {
+ *dist = t_min;
+ }
+ if (normal) {
+ glm::vec4 norm(0.0f);
+ if (min_all.x > min_all.y) {
+ if (min_all.x > min_all.z) {
+ norm.x = t2.x < t1.x ? 1 : -1;
+ } else {
+ norm.z = t2.z < t1.z ? 1 : -1;
+ }
+ } else if (min_all.y > min_all.z) {
+ norm.y = t2.y < t1.y ? 1 : -1;
+ } else {
+ norm.z = t2.z < t1.z ? 1 : -1;
+ }
+ norm = M * norm;
+ *normal = glm::vec3(norm);
+ }
+ return true;
+}
+
+bool CullTest(const AABB &box, const glm::mat4 &MVP) noexcept {
+ // transform corners into clip space
+ glm::vec4 corners[8] = {
+ { box.min.x, box.min.y, box.min.z, 1.0f },
+ { box.min.x, box.min.y, box.max.z, 1.0f },
+ { box.min.x, box.max.y, box.min.z, 1.0f },
+ { box.min.x, box.max.y, box.max.z, 1.0f },
+ { box.max.x, box.min.y, box.min.z, 1.0f },
+ { box.max.x, box.min.y, box.max.z, 1.0f },
+ { box.max.x, box.max.y, box.min.z, 1.0f },
+ { box.max.x, box.max.y, box.max.z, 1.0f },
+ };
+ for (glm::vec4 &corner : corners) {
+ corner = MVP * corner;
+ corner /= corner.w;
+ }
+
+ int hits[6] = { 0, 0, 0, 0, 0, 0 };
+
+ // check how many corners lie outside
+ for (const glm::vec4 &corner : corners) {
+ if (corner.x > 1.0f) ++hits[0];
+ if (corner.x < -1.0f) ++hits[1];
+ if (corner.y > 1.0f) ++hits[2];
+ if (corner.y < -1.0f) ++hits[3];
+ if (corner.z > 1.0f) ++hits[4];
+ if (corner.z < -1.0f) ++hits[5];
+ }
+
+ // if all corners are outside any given clip plane, the test is true
+ for (int hit : hits) {
+ if (hit == 8) return true;
+ }
+
+ // otherwise the box might still get culled completely, but can't say for sure ;)
+ return false;
+}
+
+}
--- /dev/null
+#ifndef BLANK_MODEL_GEOMETRY_H_
+#define BLANK_MODEL_GEOMETRY_H_
+
+#include <algorithm>
+#include <glm/glm.hpp>
+
+
+namespace blank {
+
+constexpr float PI = 3.141592653589793238462643383279502884;
+constexpr float PI_0p25 = PI * 0.25f;
+constexpr float PI_0p5 = PI * 0.5f;
+constexpr float PI_1p5 = PI * 1.5f;
+constexpr float PI_2p0 = PI * 2.0f;
+
+struct AABB {
+ glm::vec3 min;
+ glm::vec3 max;
+
+ void Adjust() noexcept {
+ if (max.x < min.x) std::swap(max.x, min.x);
+ if (max.y < min.y) std::swap(max.y, min.y);
+ if (max.z < min.z) std::swap(max.z, min.z);
+ }
+};
+
+struct Ray {
+ glm::vec3 orig;
+ glm::vec3 dir;
+};
+
+bool Intersection(
+ const Ray &,
+ const AABB &,
+ const glm::mat4 &M,
+ float *dist = nullptr,
+ glm::vec3 *normal = nullptr) noexcept;
+
+bool CullTest(const AABB &box, const glm::mat4 &MVP) noexcept;
+
+}
+
+#endif
--- /dev/null
+#include "BlockModel.hpp"
+#include "Model.hpp"
+#include "OutlineModel.hpp"
+
+#include <iostream>
+
+
+namespace blank {
+
+Model::Model() noexcept
+: va(0)
+, handle{}
+, count(0) {
+ glGenVertexArrays(1, &va);
+ glGenBuffers(ATTRIB_COUNT, handle);
+}
+
+Model::~Model() noexcept {
+ glDeleteBuffers(ATTRIB_COUNT, handle);
+ glDeleteVertexArrays(1, &va);
+}
+
+Model::Model(Model &&other) noexcept
+: va(other.va)
+, count(other.count) {
+ other.va = 0;
+ for (int i = 0; i < ATTRIB_COUNT; ++i) {
+ handle[i] = other.handle[i];
+ other.handle[i] = 0;
+ }
+}
+
+Model &Model::operator =(Model &&other) noexcept {
+ std::swap(va, other.va);
+ for (int i = 0; i < ATTRIB_COUNT; ++i) {
+ std::swap(handle[i], other.handle[i]);
+ }
+ count = other.count;
+ return *this;
+}
+
+void Model::Update(const Buffer &buf) noexcept {
+ glBindVertexArray(va);
+ glBindBuffer(GL_ARRAY_BUFFER, handle[ATTRIB_VERTEX]);
+ glBufferData(GL_ARRAY_BUFFER, buf.vertices.size() * sizeof(glm::vec3), buf.vertices.data(), GL_STATIC_DRAW);
+ glEnableVertexAttribArray(ATTRIB_VERTEX);
+ glVertexAttribPointer(
+ ATTRIB_VERTEX, // location (for shader)
+ 3, // size
+ GL_FLOAT, // type
+ GL_FALSE, // normalized
+ 0, // stride
+ nullptr // offset
+ );
+
+#ifndef NDEBUG
+ if (buf.colors.size() < buf.vertices.size()) {
+ std::cerr << "Model: not enough colors!" << std::endl;
+ }
+#endif
+ glBindBuffer(GL_ARRAY_BUFFER, handle[ATTRIB_COLOR]);
+ glBufferData(GL_ARRAY_BUFFER, buf.colors.size() * sizeof(glm::vec3), buf.colors.data(), GL_STATIC_DRAW);
+ glEnableVertexAttribArray(ATTRIB_COLOR);
+ glVertexAttribPointer(
+ ATTRIB_COLOR, // location (for shader)
+ 3, // size
+ GL_FLOAT, // type
+ GL_FALSE, // normalized
+ 0, // stride
+ nullptr // offset
+ );
+
+#ifndef NDEBUG
+ if (buf.normals.size() < buf.vertices.size()) {
+ std::cerr << "Model: not enough normals!" << std::endl;
+ }
+#endif
+ glBindBuffer(GL_ARRAY_BUFFER, handle[ATTRIB_NORMAL]);
+ glBufferData(GL_ARRAY_BUFFER, buf.normals.size() * sizeof(glm::vec3), buf.normals.data(), GL_STATIC_DRAW);
+ glEnableVertexAttribArray(ATTRIB_NORMAL);
+ glVertexAttribPointer(
+ ATTRIB_NORMAL, // location (for shader)
+ 3, // size
+ GL_FLOAT, // type
+ GL_FALSE, // normalized
+ 0, // stride
+ nullptr // offset
+ );
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, handle[ATTRIB_INDEX]);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, buf.indices.size() * sizeof(Index), buf.indices.data(), GL_STATIC_DRAW);
+ count = buf.indices.size();
+}
+
+
+void Model::Draw() const noexcept {
+ glBindVertexArray(va);
+ glDrawElements(
+ GL_TRIANGLES, // how
+ count, // count
+ GL_UNSIGNED_INT, // type
+ nullptr // offset
+ );
+}
+
+
+BlockModel::BlockModel() noexcept
+: va(0)
+, handle{}
+, count(0) {
+ glGenVertexArrays(1, &va);
+ glGenBuffers(ATTRIB_COUNT, handle);
+}
+
+BlockModel::~BlockModel() noexcept {
+ glDeleteBuffers(ATTRIB_COUNT, handle);
+ glDeleteVertexArrays(1, &va);
+}
+
+BlockModel::BlockModel(BlockModel &&other) noexcept
+: va(other.va)
+, count(other.count) {
+ other.va = 0;
+ for (int i = 0; i < ATTRIB_COUNT; ++i) {
+ handle[i] = other.handle[i];
+ other.handle[i] = 0;
+ }
+}
+
+BlockModel &BlockModel::operator =(BlockModel &&other) noexcept {
+ std::swap(va, other.va);
+ for (int i = 0; i < ATTRIB_COUNT; ++i) {
+ std::swap(handle[i], other.handle[i]);
+ }
+ count = other.count;
+ return *this;
+}
+
+void BlockModel::Update(const Buffer &buf) noexcept {
+ glBindVertexArray(va);
+ glBindBuffer(GL_ARRAY_BUFFER, handle[ATTRIB_VERTEX]);
+ glBufferData(GL_ARRAY_BUFFER, buf.vertices.size() * sizeof(glm::vec3), buf.vertices.data(), GL_STATIC_DRAW);
+ glEnableVertexAttribArray(ATTRIB_VERTEX);
+ glVertexAttribPointer(
+ ATTRIB_VERTEX, // location (for shader)
+ 3, // size
+ GL_FLOAT, // type
+ GL_FALSE, // normalized
+ 0, // stride
+ nullptr // offset
+ );
+
+#ifndef NDEBUG
+ if (buf.colors.size() < buf.vertices.size()) {
+ std::cerr << "BlockModel: not enough colors!" << std::endl;
+ }
+#endif
+ glBindBuffer(GL_ARRAY_BUFFER, handle[ATTRIB_COLOR]);
+ glBufferData(GL_ARRAY_BUFFER, buf.colors.size() * sizeof(glm::vec3), buf.colors.data(), GL_STATIC_DRAW);
+ glEnableVertexAttribArray(ATTRIB_COLOR);
+ glVertexAttribPointer(
+ ATTRIB_COLOR, // location (for shader)
+ 3, // size
+ GL_FLOAT, // type
+ GL_FALSE, // normalized
+ 0, // stride
+ nullptr // offset
+ );
+
+#ifndef NDEBUG
+ if (buf.lights.size() < buf.vertices.size()) {
+ std::cerr << "BlockModel: not enough lights!" << std::endl;
+ }
+#endif
+ glBindBuffer(GL_ARRAY_BUFFER, handle[ATTRIB_LIGHT]);
+ glBufferData(GL_ARRAY_BUFFER, buf.lights.size() * sizeof(float), buf.lights.data(), GL_STATIC_DRAW);
+ glEnableVertexAttribArray(ATTRIB_LIGHT);
+ glVertexAttribPointer(
+ ATTRIB_LIGHT, // location (for shader)
+ 1, // size
+ GL_FLOAT, // type
+ GL_FALSE, // normalized
+ 0, // stride
+ nullptr // offset
+ );
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, handle[ATTRIB_INDEX]);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, buf.indices.size() * sizeof(Index), buf.indices.data(), GL_STATIC_DRAW);
+ count = buf.indices.size();
+}
+
+
+void BlockModel::Draw() const noexcept {
+ glBindVertexArray(va);
+ glDrawElements(
+ GL_TRIANGLES, // how
+ count, // count
+ GL_UNSIGNED_INT, // type
+ nullptr // offset
+ );
+}
+
+OutlineModel::OutlineModel() noexcept
+: vertices()
+, colors()
+, indices()
+, va(0)
+, handle{}
+, dirty(false) {
+ glGenVertexArrays(1, &va);
+ glGenBuffers(ATTRIB_COUNT, handle);
+}
+
+OutlineModel::~OutlineModel() noexcept {
+ glDeleteBuffers(ATTRIB_COUNT, handle);
+ glDeleteVertexArrays(1, &va);
+}
+
+
+void OutlineModel::Clear() noexcept {
+ vertices.clear();
+ colors.clear();
+ indices.clear();
+ Invalidate();
+}
+
+void OutlineModel::Reserve(int v, int i) {
+ vertices.reserve(v);
+ colors.reserve(v);
+ indices.reserve(i);
+}
+
+
+void OutlineModel::Update() noexcept {
+ glBindBuffer(GL_ARRAY_BUFFER, handle[ATTRIB_VERTEX]);
+ glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(glm::vec3), vertices.data(), GL_STATIC_DRAW);
+ glEnableVertexAttribArray(ATTRIB_VERTEX);
+ glVertexAttribPointer(
+ ATTRIB_VERTEX, // location (for shader)
+ 3, // size
+ GL_FLOAT, // type
+ GL_FALSE, // normalized
+ 0, // stride
+ nullptr // offset
+ );
+
+#ifndef NDEBUG
+ if (colors.size() < vertices.size()) {
+ std::cerr << "OutlineModel: not enough colors!" << std::endl;
+ colors.resize(vertices.size(), { 1, 0, 1 });
+ }
+#endif
+ glBindBuffer(GL_ARRAY_BUFFER, handle[ATTRIB_COLOR]);
+ glBufferData(GL_ARRAY_BUFFER, colors.size() * sizeof(glm::vec3), colors.data(), GL_STATIC_DRAW);
+ glEnableVertexAttribArray(ATTRIB_COLOR);
+ glVertexAttribPointer(
+ ATTRIB_COLOR, // location (for shader)
+ 3, // size
+ GL_FLOAT, // type
+ GL_FALSE, // normalized
+ 0, // stride
+ nullptr // offset
+ );
+
+ glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, handle[ATTRIB_INDEX]);
+ glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(Index), indices.data(), GL_STATIC_DRAW);
+
+ dirty = false;
+}
+
+
+void OutlineModel::Draw() noexcept {
+ glBindVertexArray(va);
+
+ if (dirty) {
+ Update();
+ }
+
+ glEnable(GL_LINE_SMOOTH);
+ glLineWidth(2.0f);
+
+ glDrawElements(
+ GL_LINES, // how
+ indices.size(), // count
+ GL_UNSIGNED_SHORT, // type
+ nullptr // offset
+ );
+}
+
+}
--- /dev/null
+#include "Shape.hpp"
+#include "shapes.hpp"
+
+
+namespace blank {
+
+void Shape::Vertices(
+ Model::Positions &vertex,
+ Model::Normals &normal,
+ Model::Indices &index
+) const {
+ for (const auto &pos : vtx_pos) {
+ vertex.emplace_back(pos);
+ }
+ for (const auto &nrm : vtx_nrm) {
+ normal.emplace_back(nrm);
+ }
+ for (auto idx : vtx_idx) {
+ index.emplace_back(idx);
+ }
+}
+
+void Shape::Vertices(
+ Model::Positions &vertex,
+ Model::Normals &normal,
+ Model::Indices &index,
+ const glm::mat4 &transform,
+ Model::Index idx_offset
+) const {
+ for (const auto &pos : vtx_pos) {
+ vertex.emplace_back(transform * glm::vec4(pos, 1.0f));
+ }
+ for (const auto &nrm : vtx_nrm) {
+ normal.emplace_back(transform * glm::vec4(nrm, 0.0f));
+ }
+ for (auto idx : vtx_idx) {
+ index.emplace_back(idx_offset + idx);
+ }
+}
+
+void Shape::Vertices(
+ BlockModel::Positions &vertex,
+ BlockModel::Indices &index,
+ const glm::mat4 &transform,
+ BlockModel::Index idx_offset
+) const {
+ for (const auto &pos : vtx_pos) {
+ vertex.emplace_back(transform * glm::vec4(pos, 1.0f));
+ }
+ for (auto idx : vtx_idx) {
+ index.emplace_back(idx_offset + idx);
+ }
+}
+
+void Shape::Outline(
+ OutlineModel::Positions &vertex,
+ OutlineModel::Indices &index,
+ const OutlineModel::Position &elem_offset,
+ OutlineModel::Index idx_offset
+) const {
+ for (const auto &pos : out_pos) {
+ vertex.emplace_back(elem_offset + pos);
+ }
+ for (auto idx : out_idx) {
+ index.emplace_back(idx_offset + idx);
+ }
+}
+
+
+NullShape::NullShape()
+: Shape() {
+
+}
+
+bool NullShape::Intersects(
+ const Ray &,
+ const glm::mat4 &,
+ float &, glm::vec3 &
+) const noexcept {
+ return false;
+}
+
+
+CuboidShape::CuboidShape(const AABB &b)
+: Shape()
+, bb(b) {
+ bb.Adjust();
+ SetShape({
+ { bb.min.x, bb.min.y, bb.max.z }, // front
+ { bb.max.x, bb.min.y, bb.max.z },
+ { bb.min.x, bb.max.y, bb.max.z },
+ { bb.max.x, bb.max.y, bb.max.z },
+ { bb.min.x, bb.min.y, bb.min.z }, // back
+ { bb.min.x, bb.max.y, bb.min.z },
+ { bb.max.x, bb.min.y, bb.min.z },
+ { bb.max.x, bb.max.y, bb.min.z },
+ { bb.min.x, bb.max.y, bb.min.z }, // top
+ { bb.min.x, bb.max.y, bb.max.z },
+ { bb.max.x, bb.max.y, bb.min.z },
+ { bb.max.x, bb.max.y, bb.max.z },
+ { bb.min.x, bb.min.y, bb.min.z }, // bottom
+ { bb.max.x, bb.min.y, bb.min.z },
+ { bb.min.x, bb.min.y, bb.max.z },
+ { bb.max.x, bb.min.y, bb.max.z },
+ { bb.min.x, bb.min.y, bb.min.z }, // left
+ { bb.min.x, bb.min.y, bb.max.z },
+ { bb.min.x, bb.max.y, bb.min.z },
+ { bb.min.x, bb.max.y, bb.max.z },
+ { bb.max.x, bb.min.y, bb.min.z }, // right
+ { bb.max.x, bb.max.y, bb.min.z },
+ { bb.max.x, bb.min.y, bb.max.z },
+ { bb.max.x, bb.max.y, bb.max.z },
+ }, {
+ { 0.0f, 0.0f, 1.0f }, // front
+ { 0.0f, 0.0f, 1.0f },
+ { 0.0f, 0.0f, 1.0f },
+ { 0.0f, 0.0f, 1.0f },
+ { 0.0f, 0.0f, -1.0f }, // back
+ { 0.0f, 0.0f, -1.0f },
+ { 0.0f, 0.0f, -1.0f },
+ { 0.0f, 0.0f, -1.0f },
+ { 0.0f, 1.0f, 0.0f }, // top
+ { 0.0f, 1.0f, 0.0f },
+ { 0.0f, 1.0f, 0.0f },
+ { 0.0f, 1.0f, 0.0f },
+ { 0.0f, -1.0f, 0.0f }, // bottom
+ { 0.0f, -1.0f, 0.0f },
+ { 0.0f, -1.0f, 0.0f },
+ { 0.0f, -1.0f, 0.0f },
+ { -1.0f, 0.0f, 0.0f }, // left
+ { -1.0f, 0.0f, 0.0f },
+ { -1.0f, 0.0f, 0.0f },
+ { -1.0f, 0.0f, 0.0f },
+ { 1.0f, 0.0f, 0.0f }, // right
+ { 1.0f, 0.0f, 0.0f },
+ { 1.0f, 0.0f, 0.0f },
+ { 1.0f, 0.0f, 0.0f },
+ }, {
+ 0, 1, 2, 2, 1, 3, // front
+ 4, 5, 6, 6, 5, 7, // back
+ 8, 9, 10, 10, 9, 11, // top
+ 12, 13, 14, 14, 13, 15, // bottom
+ 16, 17, 18, 18, 17, 19, // left
+ 20, 21, 22, 22, 21, 23, // right
+ });
+ SetOutline({
+ { bb.min.x, bb.min.y, bb.min.z }, // back
+ { bb.max.x, bb.min.y, bb.min.z },
+ { bb.min.x, bb.max.y, bb.min.z },
+ { bb.max.x, bb.max.y, bb.min.z },
+ { bb.min.x, bb.min.y, bb.max.z }, // front
+ { bb.max.x, bb.min.y, bb.max.z },
+ { bb.min.x, bb.max.y, bb.max.z },
+ { bb.max.x, bb.max.y, bb.max.z },
+ }, {
+ 0, 1, 1, 3, 3, 2, 2, 0, // back
+ 4, 5, 5, 7, 7, 6, 6, 4, // front
+ 0, 4, 1, 5, 2, 6, 3, 7, // sides
+ });
+}
+
+bool CuboidShape::Intersects(
+ const Ray &ray,
+ const glm::mat4 &M,
+ float &dist, glm::vec3 &normal
+) const noexcept {
+ return Intersection(ray, bb, M, &dist, &normal);
+}
+
+
+StairShape::StairShape(const AABB &bb, const glm::vec2 &clip)
+: Shape()
+, top({ { bb.min.x, clip.y, bb.min.z }, { bb.max.x, bb.max.y, clip.x } })
+, bot({ bb.min, { bb.max.x, clip.y, bb.max.z } }) {
+ SetShape({
+ { top.min.x, top.min.y, top.max.z }, // front, upper
+ { top.max.x, top.min.y, top.max.z },
+ { top.min.x, top.max.y, top.max.z },
+ { top.max.x, top.max.y, top.max.z },
+ { bot.min.x, bot.min.y, bot.max.z }, // front, lower
+ { bot.max.x, bot.min.y, bot.max.z },
+ { bot.min.x, bot.max.y, bot.max.z },
+ { bot.max.x, bot.max.y, bot.max.z },
+ { bot.min.x, bot.min.y, bot.min.z }, // back
+ { bot.min.x, top.max.y, bot.min.z },
+ { top.max.x, bot.min.y, bot.min.z },
+ { top.max.x, top.max.y, bot.min.z },
+ { top.min.x, top.max.y, top.min.z }, // top, upper
+ { top.min.x, top.max.y, top.max.z },
+ { top.max.x, top.max.y, top.min.z },
+ { top.max.x, top.max.y, top.max.z },
+ { bot.min.x, bot.max.y, top.max.z }, // top, lower
+ { bot.min.x, bot.max.y, bot.max.z },
+ { bot.max.x, bot.max.y, top.max.z },
+ { bot.max.x, bot.max.y, bot.max.z },
+ { bot.min.x, bot.min.y, bot.min.z }, // bottom
+ { bot.max.x, bot.min.y, bot.min.z },
+ { bot.min.x, bot.min.y, bot.max.z },
+ { bot.max.x, bot.min.y, bot.max.z },
+ { top.min.x, top.min.y, top.min.z }, // left, upper
+ { top.min.x, top.min.y, top.max.z },
+ { top.min.x, top.max.y, top.min.z },
+ { top.min.x, top.max.y, top.max.z },
+ { bot.min.x, bot.min.y, bot.min.z }, // left, lower
+ { bot.min.x, bot.min.y, bot.max.z },
+ { bot.min.x, bot.max.y, bot.min.z },
+ { bot.min.x, bot.max.y, bot.max.z },
+ { top.max.x, top.min.y, top.min.z }, // right, upper
+ { top.max.x, top.max.y, top.min.z },
+ { top.max.x, top.min.y, top.max.z },
+ { top.max.x, top.max.y, top.max.z },
+ { bot.max.x, bot.min.y, bot.min.z }, // right, lower
+ { bot.max.x, bot.max.y, bot.min.z },
+ { bot.max.x, bot.min.y, bot.max.z },
+ { bot.max.x, bot.max.y, bot.max.z },
+ }, {
+ { 0.0f, 0.0f, 1.0f }, // front x2
+ { 0.0f, 0.0f, 1.0f },
+ { 0.0f, 0.0f, 1.0f },
+ { 0.0f, 0.0f, 1.0f },
+ { 0.0f, 0.0f, 1.0f },
+ { 0.0f, 0.0f, 1.0f },
+ { 0.0f, 0.0f, 1.0f },
+ { 0.0f, 0.0f, 1.0f },
+ { 0.0f, 0.0f, -1.0f }, // back
+ { 0.0f, 0.0f, -1.0f },
+ { 0.0f, 0.0f, -1.0f },
+ { 0.0f, 0.0f, -1.0f },
+ { 0.0f, 1.0f, 0.0f }, // top x2
+ { 0.0f, 1.0f, 0.0f },
+ { 0.0f, 1.0f, 0.0f },
+ { 0.0f, 1.0f, 0.0f },
+ { 0.0f, 1.0f, 0.0f },
+ { 0.0f, 1.0f, 0.0f },
+ { 0.0f, 1.0f, 0.0f },
+ { 0.0f, 1.0f, 0.0f },
+ { 0.0f, -1.0f, 0.0f }, // bottom
+ { 0.0f, -1.0f, 0.0f },
+ { 0.0f, -1.0f, 0.0f },
+ { 0.0f, -1.0f, 0.0f },
+ { -1.0f, 0.0f, 0.0f }, // left x2
+ { -1.0f, 0.0f, 0.0f },
+ { -1.0f, 0.0f, 0.0f },
+ { -1.0f, 0.0f, 0.0f },
+ { -1.0f, 0.0f, 0.0f },
+ { -1.0f, 0.0f, 0.0f },
+ { -1.0f, 0.0f, 0.0f },
+ { -1.0f, 0.0f, 0.0f },
+ { 1.0f, 0.0f, 0.0f }, // right x2
+ { 1.0f, 0.0f, 0.0f },
+ { 1.0f, 0.0f, 0.0f },
+ { 1.0f, 0.0f, 0.0f },
+ { 1.0f, 0.0f, 0.0f },
+ { 1.0f, 0.0f, 0.0f },
+ { 1.0f, 0.0f, 0.0f },
+ { 1.0f, 0.0f, 0.0f },
+ }, {
+ 0, 1, 2, 2, 1, 3, // front, upper
+ 4, 5, 6, 6, 5, 7, // front, lower
+ 8, 9, 10, 10, 9, 11, // back
+ 12, 13, 14, 14, 13, 15, // top, upper
+ 16, 17, 18, 18, 17, 19, // top, lower
+ 20, 21, 22, 22, 21, 23, // bottom
+ 24, 25, 26, 26, 25, 27, // left, upper
+ 28, 29, 30, 30, 29, 31, // left, lower
+ 32, 33, 34, 34, 33, 35, // right, upper
+ 36, 37, 38, 38, 37, 39, // right, lower
+ });
+ SetOutline({
+ { bot.min.x, bot.min.y, bot.min.z }, // bottom
+ { bot.max.x, bot.min.y, bot.min.z },
+ { bot.min.x, bot.min.y, bot.max.z },
+ { bot.max.x, bot.min.y, bot.max.z },
+ { bot.min.x, bot.max.y, top.max.z }, // middle
+ { bot.max.x, bot.max.y, top.max.z },
+ { bot.min.x, bot.max.y, bot.max.z },
+ { bot.max.x, bot.max.y, bot.max.z },
+ { top.min.x, top.max.y, top.min.z }, // top
+ { top.max.x, top.max.y, top.min.z },
+ { top.min.x, top.max.y, top.max.z },
+ { top.max.x, top.max.y, top.max.z },
+ }, {
+ 0, 1, 1, 3, 3, 2, 2, 0, // bottom
+ 4, 5, 5, 7, 7, 6, 6, 4, // middle
+ 8, 9, 9, 11, 11, 10, 10 , 8, // top
+ 0, 8, 4, 10, 2, 6, // verticals, btf
+ 1, 9, 5, 11, 3, 7,
+ // 5, 8, 7, 10,
+ // 1, 9, 3, 11,
+ });
+}
+
+bool StairShape::Intersects(
+ const Ray &ray,
+ const glm::mat4 &M,
+ float &dist,
+ glm::vec3 &norm
+) const noexcept {
+ float top_dist, bot_dist;
+ glm::vec3 top_norm, bot_norm;
+ bool top_hit = Intersection(ray, top, M, &top_dist, &top_norm);
+ bool bot_hit = Intersection(ray, bot, M, &bot_dist, &bot_norm);
+
+ if (top_hit) {
+ if (bot_hit) {
+ if (top_dist < bot_dist) {
+ dist = top_dist;
+ norm = top_norm;
+ return true;
+ } else {
+ dist = bot_dist;
+ norm = bot_norm;
+ return true;
+ }
+ } else {
+ dist = top_dist;
+ norm = top_norm;
+ return true;
+ }
+ } else if (bot_hit) {
+ dist = bot_dist;
+ norm = bot_norm;
+ return true;
+ } else {
+ return false;
+ }
+}
+
+}
--- /dev/null
+#ifndef BLANK_MODEL_SHAPES_HPP_
+#define BLANK_MODEL_SHAPES_HPP_
+
+#include "geometry.hpp"
+#include "Shape.hpp"
+
+#include <vector>
+#include <glm/glm.hpp>
+
+
+namespace blank {
+
+class NullShape
+: public Shape {
+
+public:
+ NullShape();
+
+ bool Intersects(const Ray &, const glm::mat4 &, float &, glm::vec3 &) const noexcept override;
+
+};
+
+
+class CuboidShape
+: public Shape {
+
+public:
+ CuboidShape(const AABB &bounds);
+
+ bool Intersects(const Ray &, const glm::mat4 &, float &, glm::vec3 &) const noexcept override;
+
+private:
+ AABB bb;
+
+};
+
+
+class StairShape
+: public Shape {
+
+public:
+ StairShape(const AABB &bounds, const glm::vec2 &clip);
+
+ bool Intersects(const Ray &, const glm::mat4 &, float &, glm::vec3 &) const noexcept override;
+
+private:
+ AABB top, bot;
+
+};
+
+}
+
+#endif
+++ /dev/null
-#include "noise.hpp"
-
-#include <cmath>
-
-
-namespace {
-
-constexpr float one_third = 1.0f/3.0f;
-constexpr float one_sixth = 1.0f/6.0f;
-
-}
-
-namespace blank {
-
-GaloisLFSR::GaloisLFSR(std::uint64_t seed) noexcept
-: state(seed) {
-
-}
-
-bool GaloisLFSR::operator ()() noexcept {
- bool result = state & 1;
- state >>= 1;
- if (result) {
- state |= 0x8000000000000000;
- state ^= mask;
- } else {
- state &= 0x7FFFFFFFFFFFFFFF;
- }
- return result;
-}
-
-
-SimplexNoise::SimplexNoise(unsigned int 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 },
-}) {
- 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);
-
- glm::vec3 second, third;
-
- if (relative.x >= relative.y) {
- if (relative.y >= relative.z) {
- second = { 1, 0, 0 };
- third = { 1, 1, 0 };
- } else if (relative.x >= relative.z) {
- second = { 1, 0, 0 };
- third = { 1, 0, 1 };
- } else {
- second = { 0, 0, 1 };
- third = { 1, 0, 1 };
- }
- } else if (relative.y < relative.z) {
- second = { 0, 0, 1 };
- third = { 0, 1, 1 };
- } else if (relative.x < relative.z) {
- second = { 0, 1, 0 };
- third = { 0, 1, 1 };
- } else {
- second = { 0, 1, 0 };
- third = { 1, 1, 0 };
- }
-
- glm::vec3 offset[4] = {
- in - unskewed,
- relative - second + one_sixth,
- relative - third + 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 = 0.6f - dot(offset[0], offset[0]);
- if (t > 0.0f) {
- t *= t;
- int corner = Perm12(index[0] + Perm(index[1] + Perm(index[2])));
- n += t * t * dot(Grad(corner), offset[0]);
- }
-
- // 1
- t = 0.6f - dot(offset[1], offset[1]);
- if (t > 0.0f) {
- t *= t;
- int corner = Perm12(index[0] + int(second.x) + Perm(index[1] + int(second.y) + Perm(index[2] + int(second.z))));
- n += t * t * dot(Grad(corner), offset[1]);
- }
-
- // 2
- t = 0.6f - dot(offset[2], offset[2]);
- if (t > 0.0f) {
- t *= t;
- int corner = Perm12(index[0] + int(third.x) + Perm(index[1] + int(third.y) + Perm(index[2] + int(third.z))));
- n += t * t * dot(Grad(corner), offset[2]);
- }
-
- // 3
- t = 0.6f - dot(offset[3], offset[3]);
- if (t > 0.0f) {
- t *= t;
- int 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];
-}
-
-
-WorleyNoise::WorleyNoise(unsigned int seed) noexcept
-: seed(seed)
-, num_points(8) {
-
-}
-
-float WorleyNoise::operator ()(const glm::vec3 &in) const noexcept {
- glm::vec3 center = floor(in);
-
- float closest = 1.0f; // cannot be farther away than 1.0
-
- for (int z = -1; z <= 1; ++z) {
- for (int y = -1; y <= 1; ++y) {
- for (int x = -1; x <= 1; ++x) {
- glm::vec3 cube(center.x + x, center.y + y, center.z + z);
- unsigned int cube_rand =
- (unsigned(cube.x) * 130223) ^
- (unsigned(cube.y) * 159899) ^
- (unsigned(cube.z) * 190717) ^
- seed;
-
- for (int i = 0; i < num_points; ++i) {
- glm::vec3 point(cube);
- cube_rand = 190667 * cube_rand + 109807;
- point.x += float(cube_rand % 262144) / 262144.0f;
- cube_rand = 135899 * cube_rand + 189169;
- point.y += float(cube_rand % 262144) / 262144.0f;
- cube_rand = 159739 * cube_rand + 112139;
- point.z += float(cube_rand % 262144) / 262144.0f;
-
- glm::vec3 diff(in - point);
- float distance = sqrt(dot(diff, diff));
- if (distance < closest) {
- closest = distance;
- }
- }
- }
- }
- }
-
- // closest ranges (0, 1), so normalizing to (-1,1) is trivial
- // though heavily biased towards lower numbers
- return 2.0f * closest - 1.0f;
-}
-
-}
+++ /dev/null
-#ifndef BLANK_NOISE_HPP_
-#define BLANK_NOISE_HPP_
-
-#include <cstdint>
-#include <limits>
-#include <glm/glm.hpp>
-
-
-namespace blank {
-
-class GaloisLFSR {
-
-public:
- // seed should be non-zero
- explicit GaloisLFSR(std::uint64_t seed) noexcept;
-
- // get the next bit
- bool operator ()() noexcept;
-
- 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);
- }
-
-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;
-
-};
-
-
-/// (3D only) adaptation of Stefan Gustavson's SimplexNoise java class
-class SimplexNoise {
-
-public:
- explicit SimplexNoise(unsigned int 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];
-
-};
-
-
-/// implementation of Worley noise (aka Cell or Voroni noise)
-class WorleyNoise {
-
-public:
- explicit WorleyNoise(unsigned int seed) noexcept;
-
- float operator ()(const glm::vec3 &) const noexcept;
-
-private:
- const unsigned int seed;
- const int num_points;
-
-};
-
-
-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
--- /dev/null
+#ifndef BLANK_RAND_GALOISLFSR_HPP_
+#define BLANK_RAND_GALOISLFSR_HPP_
+
+#include <cstdint>
+#include <limits>
+
+
+namespace blank {
+
+class GaloisLFSR {
+
+public:
+ // seed should be non-zero
+ explicit GaloisLFSR(std::uint64_t seed) noexcept;
+
+ // get the next bit
+ bool operator ()() noexcept;
+
+ 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);
+ }
+
+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
--- /dev/null
+#ifndef BLANK_RAND_OCTAVENOISE_HPP_
+#define BLANK_RAND_OCTAVENOISE_HPP_
+
+#include <glm/glm.hpp>
+
+
+namespace blank {
+
+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
--- /dev/null
+#ifndef BLANK_RAND_SIMPLEXNOISE_HPP_
+#define BLANK_RAND_SIMPLEXNOISE_HPP_
+
+#include <glm/glm.hpp>
+
+
+namespace blank {
+
+/// (3D only) adaptation of Stefan Gustavson's SimplexNoise java class
+class SimplexNoise {
+
+public:
+ explicit SimplexNoise(unsigned int 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];
+
+};
+
+}
+
+#endif
--- /dev/null
+#ifndef BLANK_RAND_WORLEYNOISE_HPP_
+#define BLANK_RAND_WORLEYNOISE_HPP_
+
+#include <glm/glm.hpp>
+
+
+namespace blank {
+
+/// implementation of Worley noise (aka Cell or Voroni noise)
+class WorleyNoise {
+
+public:
+ explicit WorleyNoise(unsigned int seed) noexcept;
+
+ float operator ()(const glm::vec3 &) const noexcept;
+
+private:
+ const unsigned int seed;
+ const int num_points;
+
+};
+
+}
+
+#endif
--- /dev/null
+#include "GaloisLFSR.hpp"
+#include "SimplexNoise.hpp"
+#include "WorleyNoise.hpp"
+
+#include <cmath>
+
+
+namespace {
+
+constexpr float one_third = 1.0f/3.0f;
+constexpr float one_sixth = 1.0f/6.0f;
+
+}
+
+namespace blank {
+
+GaloisLFSR::GaloisLFSR(std::uint64_t seed) noexcept
+: state(seed) {
+
+}
+
+bool GaloisLFSR::operator ()() noexcept {
+ bool result = state & 1;
+ state >>= 1;
+ if (result) {
+ state |= 0x8000000000000000;
+ state ^= mask;
+ } else {
+ state &= 0x7FFFFFFFFFFFFFFF;
+ }
+ return result;
+}
+
+
+SimplexNoise::SimplexNoise(unsigned int 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 },
+}) {
+ 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);
+
+ glm::vec3 second, third;
+
+ if (relative.x >= relative.y) {
+ if (relative.y >= relative.z) {
+ second = { 1, 0, 0 };
+ third = { 1, 1, 0 };
+ } else if (relative.x >= relative.z) {
+ second = { 1, 0, 0 };
+ third = { 1, 0, 1 };
+ } else {
+ second = { 0, 0, 1 };
+ third = { 1, 0, 1 };
+ }
+ } else if (relative.y < relative.z) {
+ second = { 0, 0, 1 };
+ third = { 0, 1, 1 };
+ } else if (relative.x < relative.z) {
+ second = { 0, 1, 0 };
+ third = { 0, 1, 1 };
+ } else {
+ second = { 0, 1, 0 };
+ third = { 1, 1, 0 };
+ }
+
+ glm::vec3 offset[4] = {
+ in - unskewed,
+ relative - second + one_sixth,
+ relative - third + 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 = 0.6f - dot(offset[0], offset[0]);
+ if (t > 0.0f) {
+ t *= t;
+ int corner = Perm12(index[0] + Perm(index[1] + Perm(index[2])));
+ n += t * t * dot(Grad(corner), offset[0]);
+ }
+
+ // 1
+ t = 0.6f - dot(offset[1], offset[1]);
+ if (t > 0.0f) {
+ t *= t;
+ int corner = Perm12(index[0] + int(second.x) + Perm(index[1] + int(second.y) + Perm(index[2] + int(second.z))));
+ n += t * t * dot(Grad(corner), offset[1]);
+ }
+
+ // 2
+ t = 0.6f - dot(offset[2], offset[2]);
+ if (t > 0.0f) {
+ t *= t;
+ int corner = Perm12(index[0] + int(third.x) + Perm(index[1] + int(third.y) + Perm(index[2] + int(third.z))));
+ n += t * t * dot(Grad(corner), offset[2]);
+ }
+
+ // 3
+ t = 0.6f - dot(offset[3], offset[3]);
+ if (t > 0.0f) {
+ t *= t;
+ int 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];
+}
+
+
+WorleyNoise::WorleyNoise(unsigned int seed) noexcept
+: seed(seed)
+, num_points(8) {
+
+}
+
+float WorleyNoise::operator ()(const glm::vec3 &in) const noexcept {
+ glm::vec3 center = floor(in);
+
+ float closest = 1.0f; // cannot be farther away than 1.0
+
+ for (int z = -1; z <= 1; ++z) {
+ for (int y = -1; y <= 1; ++y) {
+ for (int x = -1; x <= 1; ++x) {
+ glm::vec3 cube(center.x + x, center.y + y, center.z + z);
+ unsigned int cube_rand =
+ (unsigned(cube.x) * 130223) ^
+ (unsigned(cube.y) * 159899) ^
+ (unsigned(cube.z) * 190717) ^
+ seed;
+
+ for (int i = 0; i < num_points; ++i) {
+ glm::vec3 point(cube);
+ cube_rand = 190667 * cube_rand + 109807;
+ point.x += float(cube_rand % 262144) / 262144.0f;
+ cube_rand = 135899 * cube_rand + 189169;
+ point.y += float(cube_rand % 262144) / 262144.0f;
+ cube_rand = 159739 * cube_rand + 112139;
+ point.z += float(cube_rand % 262144) / 262144.0f;
+
+ glm::vec3 diff(in - point);
+ float distance = sqrt(dot(diff, diff));
+ if (distance < closest) {
+ closest = distance;
+ }
+ }
+ }
+ }
+ }
+
+ // closest ranges (0, 1), so normalizing to (-1,1) is trivial
+ // though heavily biased towards lower numbers
+ return 2.0f * closest - 1.0f;
+}
+
+}
+++ /dev/null
-#include "runtime.hpp"
-
-#include "app.hpp"
-
-#include <cctype>
-#include <cstdlib>
-#include <iostream>
-
-using namespace std;
-
-
-namespace blank {
-
-Runtime::Runtime() noexcept
-: name("blank")
-, mode(NORMAL)
-, n(0)
-, t(0)
-, config() {
-
-}
-
-
-void Runtime::ReadArgs(int argc, const char *const *argv) {
- if (argc <= 0) return;
- name = argv[0];
-
- bool options = true;
- bool error = false;
-
- for (int i = 1; i < argc; ++i) {
- const char *arg = argv[i];
- if (!arg || arg[0] == '\0') {
- cerr << "warning: found empty argument at position " << i << endl;
- continue;
- }
- if (options && arg[0] == '-') {
- if (arg[1] == '\0') {
- cerr << "warning: incomplete option list at position " << i << endl;
- } else if (arg[1] == '-') {
- if (arg[2] == '\0') {
- // stopper
- options = false;
- } else {
- // long option
- if (strcmp(arg + 2, "no-vsync") == 0) {
- config.vsync = false;
- } else if (strcmp(arg + 2, "no-keyboard") == 0) {
- config.interface.keyboard_disabled = true;
- } else if (strcmp(arg + 2, "no-mouse") == 0) {
- config.interface.mouse_disabled = true;
- } else if (strcmp(arg + 2, "no-hud") == 0) {
- config.interface.visual_disabled = true;
- } else {
- cerr << "unknown option " << arg << endl;
- error = true;
- }
- }
- } else {
- // short options
- for (int j = 1; arg[j] != '\0'; ++j) {
- switch (arg[j]) {
- case 'd':
- config.doublebuf = false;
- break;
- case 'm':
- ++i;
- if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
- cerr << "missing argument to -m" << endl;
- error = true;
- } else {
- config.multisampling = strtoul(argv[i], nullptr, 10);
- }
- break;
- case 'n':
- ++i;
- if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
- cerr << "missing argument to -n" << endl;
- error = true;
- } else {
- n = strtoul(argv[i], nullptr, 10);
- }
- break;
- case 's':
- ++i;
- if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
- cerr << "missing argument to -s" << endl;
- error = true;
- } else {
- config.world.gen.solid_seed = strtoul(argv[i], nullptr, 10);
- config.world.gen.type_seed = config.world.gen.solid_seed;
- }
- break;
- case 't':
- ++i;
- if (i >= argc || argv[i] == nullptr || argv[i][0] == '\0') {
- cerr << "missing argument to -t" << endl;
- error = true;
- } else {
- t = strtoul(argv[i], nullptr, 10);
- }
- break;
- case '-':
- // stopper
- options = false;
- break;
- default:
- cerr << "unknown option " << arg[j] << endl;
- error = true;
- break;
- }
- }
- }
- } else if (isdigit(arg[0])) {
- // positional number interpreted as -n
- n = strtoul(arg, nullptr, 10);
- } else {
- cerr << "unable to interpret argument "
- << i << " (" << arg << ")" << endl;
- error = true;
- }
- }
-
- if (error) {
- mode = ERROR;
- return;
- }
-
- if (n > 0) {
- if (t > 0) {
- mode = FIXED_FRAME_LIMIT;
- } else {
- mode = FRAME_LIMIT;
- }
- } else if (t > 0) {
- mode = TIME_LIMIT;
- } else {
- mode = NORMAL;
- }
-}
-
-int Runtime::Execute() {
- if (mode == ERROR) {
- return 1;
- }
-
- Application app(config);
- switch (mode) {
- default:
- case NORMAL:
- app.Run();
- break;
- case FRAME_LIMIT:
- app.RunN(n);
- break;
- case TIME_LIMIT:
- app.RunT(t);
- break;
- case FIXED_FRAME_LIMIT:
- app.RunS(n, t);
- break;
- }
- return 0;
-}
-
-}
+++ /dev/null
-#ifndef BLANK_RUNTIME_HPP_
-#define BLANK_RUNTIME_HPP_
-
-#include "app.hpp"
-
-#include <cstddef>
-
-
-namespace blank {
-
-class Runtime {
-
-public:
- enum Mode {
- NORMAL,
- FRAME_LIMIT,
- TIME_LIMIT,
- FIXED_FRAME_LIMIT,
- ERROR,
- };
-
- Runtime() noexcept;
-
- void ReadArgs(int argc, const char *const *argv);
-
- int Execute();
-
-private:
- const char *name;
- Mode mode;
- std::size_t n;
- std::size_t t;
- Application::Config config;
-
-};
-
-}
-
-#endif
+++ /dev/null
-#include "shader.hpp"
-
-#include "init.hpp"
-
-#include <algorithm>
-#include <iostream>
-#include <memory>
-#include <ostream>
-#include <stdexcept>
-#include <string>
-
-
-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 blank {
-
-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);
-}
-
-
-DirectionalLighting::DirectionalLighting()
-: program()
-, light_direction(1.0f, 3.0f, 2.0f)
-, light_color(0.9f, 0.9f, 0.9f)
-, vp(1.0f)
-, m_handle(0)
-, mv_handle(0)
-, mvp_handle(0)
-, light_direction_handle(0)
-, light_color_handle(0)
-, fog_density_handle(0) {
- program.LoadShader(
- GL_VERTEX_SHADER,
- "#version 330 core\n"
- "layout(location = 0) in vec3 vtx_position;\n"
- "layout(location = 1) in vec3 vtx_color;\n"
- "layout(location = 2) in vec3 vtx_normal;\n"
- "uniform mat4 M;\n"
- "uniform mat4 MV;\n"
- "uniform mat4 MVP;\n"
- "out vec3 frag_color;\n"
- "out vec3 vtx_viewspace;\n"
- "out vec3 normal;\n"
- "void main() {\n"
- "gl_Position = MVP * vec4(vtx_position, 1);\n"
- "vtx_viewspace = (MV * vec4(vtx_position, 1)).xyz;\n"
- "normal = (M * vec4(vtx_normal, 0)).xyz;\n"
- "frag_color = vtx_color;\n"
- "}\n"
- );
- program.LoadShader(
- GL_FRAGMENT_SHADER,
- "#version 330 core\n"
- "in vec3 frag_color;\n"
- "in vec3 vtx_viewspace;\n"
- "in vec3 normal;\n"
- "uniform vec3 light_direction;\n"
- "uniform vec3 light_color;\n"
- "uniform float fog_density;\n"
- "out vec3 color;\n"
- "void main() {\n"
- "vec3 ambient = vec3(0.1, 0.1, 0.1) * frag_color;\n"
- // this should be the same as the clear color, otherwise looks really weird
- "vec3 fog_color = vec3(0, 0, 0);\n"
- "float e = 2.718281828;\n"
- "vec3 n = normalize(normal);\n"
- "vec3 l = normalize(light_direction);\n"
- "float cos_theta = clamp(dot(n, l), 0, 1);\n"
- "vec3 reflect_color = ambient + frag_color * light_color * cos_theta;\n"
- "float value = pow(e, -pow(fog_density * length(vtx_viewspace), 5));"
- "color = mix(fog_color, reflect_color, value);\n"
- "}\n"
- );
- program.Link();
- if (!program.Linked()) {
- program.Log(std::cerr);
- throw std::runtime_error("link program");
- }
-
- m_handle = program.UniformLocation("M");
- mv_handle = program.UniformLocation("MV");
- mvp_handle = program.UniformLocation("MVP");
- light_direction_handle = program.UniformLocation("light_direction");
- light_color_handle = program.UniformLocation("light_color");
- fog_density_handle = program.UniformLocation("fog_density");
-}
-
-
-void DirectionalLighting::Activate() noexcept {
- GLContext::EnableDepthTest();
- GLContext::EnableBackfaceCulling();
- program.Use();
-
- glUniform3f(light_direction_handle, light_direction.x, light_direction.y, light_direction.z);
- glUniform3f(light_color_handle, light_color.x, light_color.y, light_color.z);
-}
-
-void DirectionalLighting::SetM(const glm::mat4 &m) noexcept {
- glm::mat4 mv(view * m);
- glm::mat4 mvp(vp * m);
- glUniformMatrix4fv(m_handle, 1, GL_FALSE, &m[0][0]);
- glUniformMatrix4fv(mv_handle, 1, GL_FALSE, &mv[0][0]);
- glUniformMatrix4fv(mvp_handle, 1, GL_FALSE, &mvp[0][0]);
-}
-
-void DirectionalLighting::SetLightDirection(const glm::vec3 &dir) noexcept {
- light_direction = -dir;
- glUniform3f(light_direction_handle, light_direction.x, light_direction.y, light_direction.z);
-}
-
-void DirectionalLighting::SetFogDensity(float f) noexcept {
- fog_density = f;
- glUniform1f(fog_density_handle, fog_density);
-}
-
-void DirectionalLighting::SetProjection(const glm::mat4 &p) noexcept {
- projection = p;
- vp = p * view;
-}
-
-void DirectionalLighting::SetView(const glm::mat4 &v) noexcept {
- view = v;
- vp = projection * v;
-}
-
-void DirectionalLighting::SetVP(const glm::mat4 &v, const glm::mat4 &p) noexcept {
- projection = p;
- view = v;
- vp = p * v;
-}
-
-void DirectionalLighting::SetMVP(const glm::mat4 &m, const glm::mat4 &v, const glm::mat4 &p) noexcept {
- SetVP(v, p);
- SetM(m);
-}
-
-
-BlockLighting::BlockLighting()
-: program()
-, vp(1.0f)
-, mv_handle(0)
-, mvp_handle(0)
-, fog_density_handle(0) {
- program.LoadShader(
- GL_VERTEX_SHADER,
- "#version 330 core\n"
- "layout(location = 0) in vec3 vtx_position;\n"
- "layout(location = 1) in vec3 vtx_color;\n"
- "layout(location = 2) in float vtx_light;\n"
- "uniform mat4 MV;\n"
- "uniform mat4 MVP;\n"
- "out vec3 frag_color;\n"
- "out vec3 vtx_viewspace;\n"
- "out float frag_light;\n"
- "void main() {\n"
- "gl_Position = MVP * vec4(vtx_position, 1);\n"
- "frag_color = vtx_color;\n"
- "vtx_viewspace = (MV * vec4(vtx_position, 1)).xyz;\n"
- "frag_light = vtx_light;\n"
- "}\n"
- );
- program.LoadShader(
- GL_FRAGMENT_SHADER,
- "#version 330 core\n"
- "in vec3 frag_color;\n"
- "in vec3 vtx_viewspace;\n"
- "in float frag_light;\n"
- "uniform float fog_density;\n"
- "out vec3 color;\n"
- "void main() {\n"
- "vec3 ambient = vec3(0.1, 0.1, 0.1) * frag_color;\n"
- "float light_power = clamp(pow(0.8, 15 - frag_light), 0, 1);\n"
- "vec3 fog_color = vec3(0, 0, 0);\n"
- "float e = 2.718281828;\n"
- //"vec3 reflect_color = ambient + frag_color * light_power;\n"
- "vec3 reflect_color = frag_color * light_power;\n"
- "float value = pow(e, -pow(fog_density * length(vtx_viewspace), 5));"
- "color = mix(fog_color, reflect_color, value);\n"
- "}\n"
- );
- program.Link();
- if (!program.Linked()) {
- program.Log(std::cerr);
- throw std::runtime_error("link program");
- }
-
- mv_handle = program.UniformLocation("MV");
- mvp_handle = program.UniformLocation("MVP");
- fog_density_handle = program.UniformLocation("fog_density");
-}
-
-
-void BlockLighting::Activate() noexcept {
- GLContext::EnableDepthTest();
- GLContext::EnableBackfaceCulling();
- program.Use();
-}
-
-void BlockLighting::SetM(const glm::mat4 &m) noexcept {
- glm::mat4 mv(view * m);
- glm::mat4 mvp(vp * m);
- glUniformMatrix4fv(mv_handle, 1, GL_FALSE, &mv[0][0]);
- glUniformMatrix4fv(mvp_handle, 1, GL_FALSE, &mvp[0][0]);
-}
-
-void BlockLighting::SetFogDensity(float f) noexcept {
- fog_density = f;
- glUniform1f(fog_density_handle, fog_density);
-}
-
-void BlockLighting::SetProjection(const glm::mat4 &p) noexcept {
- projection = p;
- vp = p * view;
-}
-
-void BlockLighting::SetView(const glm::mat4 &v) noexcept {
- view = v;
- vp = projection * v;
-}
-
-void BlockLighting::SetVP(const glm::mat4 &v, const glm::mat4 &p) noexcept {
- projection = p;
- view = v;
- vp = p * v;
-}
-
-void BlockLighting::SetMVP(const glm::mat4 &m, const glm::mat4 &v, const glm::mat4 &p) noexcept {
- SetVP(v, p);
- SetM(m);
-}
-
-}
+++ /dev/null
-#ifndef BLANK_SHADER_HPP_
-#define BLANK_SHADER_HPP_
-
-#include <iosfwd>
-#include <list>
-#include <GL/glew.h>
-#include <glm/glm.hpp>
-
-
-namespace blank {
-
-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;
-
-};
-
-
-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 Use() const noexcept { glUseProgram(handle); }
-
-private:
- GLuint handle;
- std::list<Shader> shaders;
-
-};
-
-
-class DirectionalLighting {
-
-public:
- DirectionalLighting();
-
- void Activate() noexcept;
-
- void SetLightDirection(const glm::vec3 &) noexcept;
-
- void SetFogDensity(float) noexcept;
-
- void SetM(const glm::mat4 &m) noexcept;
- void SetProjection(const glm::mat4 &p) noexcept;
- void SetView(const glm::mat4 &v) noexcept;
- void SetVP(const glm::mat4 &v, const glm::mat4 &p) noexcept;
- void SetMVP(const glm::mat4 &m, const glm::mat4 &v, const glm::mat4 &p) noexcept;
-
- const glm::mat4 &Projection() const noexcept { return projection; }
- const glm::mat4 &View() const noexcept { return view; }
- const glm::mat4 &GetVP() const noexcept { return vp; }
-
-private:
- Program program;
-
- glm::vec3 light_direction;
- glm::vec3 light_color;
-
- float fog_density;
-
- glm::mat4 projection;
- glm::mat4 view;
- glm::mat4 vp;
-
- GLuint m_handle;
- GLuint mv_handle;
- GLuint mvp_handle;
- GLuint light_direction_handle;
- GLuint light_color_handle;
- GLuint fog_density_handle;
-
-};
-
-class BlockLighting {
-
-public:
- BlockLighting();
-
- void Activate() noexcept;
-
- void SetFogDensity(float) noexcept;
-
- void SetM(const glm::mat4 &m) noexcept;
- void SetProjection(const glm::mat4 &p) noexcept;
- void SetView(const glm::mat4 &v) noexcept;
- void SetVP(const glm::mat4 &v, const glm::mat4 &p) noexcept;
- void SetMVP(const glm::mat4 &m, const glm::mat4 &v, const glm::mat4 &p) noexcept;
-
- const glm::mat4 &Projection() const noexcept { return projection; }
- const glm::mat4 &View() const noexcept { return view; }
- const glm::mat4 &GetVP() const noexcept { return vp; }
-
-private:
- Program program;
-
- float fog_density;
-
- glm::mat4 projection;
- glm::mat4 view;
- glm::mat4 vp;
-
- GLuint mv_handle;
- GLuint mvp_handle;
- GLuint light_direction_handle;
- GLuint light_color_handle;
- GLuint fog_density_handle;
-
-};
-
-}
-
-#endif
+++ /dev/null
-#include "shape.hpp"
-
-
-namespace blank {
-
-void Shape::Vertices(
- Model::Positions &vertex,
- Model::Normals &normal,
- Model::Indices &index
-) const {
- for (const auto &pos : vtx_pos) {
- vertex.emplace_back(pos);
- }
- for (const auto &nrm : vtx_nrm) {
- normal.emplace_back(nrm);
- }
- for (auto idx : vtx_idx) {
- index.emplace_back(idx);
- }
-}
-
-void Shape::Vertices(
- Model::Positions &vertex,
- Model::Normals &normal,
- Model::Indices &index,
- const glm::mat4 &transform,
- Model::Index idx_offset
-) const {
- for (const auto &pos : vtx_pos) {
- vertex.emplace_back(transform * glm::vec4(pos, 1.0f));
- }
- for (const auto &nrm : vtx_nrm) {
- normal.emplace_back(transform * glm::vec4(nrm, 0.0f));
- }
- for (auto idx : vtx_idx) {
- index.emplace_back(idx_offset + idx);
- }
-}
-
-void Shape::Vertices(
- BlockModel::Positions &vertex,
- BlockModel::Indices &index,
- const glm::mat4 &transform,
- BlockModel::Index idx_offset
-) const {
- for (const auto &pos : vtx_pos) {
- vertex.emplace_back(transform * glm::vec4(pos, 1.0f));
- }
- for (auto idx : vtx_idx) {
- index.emplace_back(idx_offset + idx);
- }
-}
-
-void Shape::Outline(
- OutlineModel::Positions &vertex,
- OutlineModel::Indices &index,
- const OutlineModel::Position &elem_offset,
- OutlineModel::Index idx_offset
-) const {
- for (const auto &pos : out_pos) {
- vertex.emplace_back(elem_offset + pos);
- }
- for (auto idx : out_idx) {
- index.emplace_back(idx_offset + idx);
- }
-}
-
-
-NullShape::NullShape()
-: Shape() {
-
-}
-
-bool NullShape::Intersects(
- const Ray &,
- const glm::mat4 &,
- float &, glm::vec3 &
-) const noexcept {
- return false;
-}
-
-
-CuboidShape::CuboidShape(const AABB &b)
-: Shape()
-, bb(b) {
- bb.Adjust();
- SetShape({
- { bb.min.x, bb.min.y, bb.max.z }, // front
- { bb.max.x, bb.min.y, bb.max.z },
- { bb.min.x, bb.max.y, bb.max.z },
- { bb.max.x, bb.max.y, bb.max.z },
- { bb.min.x, bb.min.y, bb.min.z }, // back
- { bb.min.x, bb.max.y, bb.min.z },
- { bb.max.x, bb.min.y, bb.min.z },
- { bb.max.x, bb.max.y, bb.min.z },
- { bb.min.x, bb.max.y, bb.min.z }, // top
- { bb.min.x, bb.max.y, bb.max.z },
- { bb.max.x, bb.max.y, bb.min.z },
- { bb.max.x, bb.max.y, bb.max.z },
- { bb.min.x, bb.min.y, bb.min.z }, // bottom
- { bb.max.x, bb.min.y, bb.min.z },
- { bb.min.x, bb.min.y, bb.max.z },
- { bb.max.x, bb.min.y, bb.max.z },
- { bb.min.x, bb.min.y, bb.min.z }, // left
- { bb.min.x, bb.min.y, bb.max.z },
- { bb.min.x, bb.max.y, bb.min.z },
- { bb.min.x, bb.max.y, bb.max.z },
- { bb.max.x, bb.min.y, bb.min.z }, // right
- { bb.max.x, bb.max.y, bb.min.z },
- { bb.max.x, bb.min.y, bb.max.z },
- { bb.max.x, bb.max.y, bb.max.z },
- }, {
- { 0.0f, 0.0f, 1.0f }, // front
- { 0.0f, 0.0f, 1.0f },
- { 0.0f, 0.0f, 1.0f },
- { 0.0f, 0.0f, 1.0f },
- { 0.0f, 0.0f, -1.0f }, // back
- { 0.0f, 0.0f, -1.0f },
- { 0.0f, 0.0f, -1.0f },
- { 0.0f, 0.0f, -1.0f },
- { 0.0f, 1.0f, 0.0f }, // top
- { 0.0f, 1.0f, 0.0f },
- { 0.0f, 1.0f, 0.0f },
- { 0.0f, 1.0f, 0.0f },
- { 0.0f, -1.0f, 0.0f }, // bottom
- { 0.0f, -1.0f, 0.0f },
- { 0.0f, -1.0f, 0.0f },
- { 0.0f, -1.0f, 0.0f },
- { -1.0f, 0.0f, 0.0f }, // left
- { -1.0f, 0.0f, 0.0f },
- { -1.0f, 0.0f, 0.0f },
- { -1.0f, 0.0f, 0.0f },
- { 1.0f, 0.0f, 0.0f }, // right
- { 1.0f, 0.0f, 0.0f },
- { 1.0f, 0.0f, 0.0f },
- { 1.0f, 0.0f, 0.0f },
- }, {
- 0, 1, 2, 2, 1, 3, // front
- 4, 5, 6, 6, 5, 7, // back
- 8, 9, 10, 10, 9, 11, // top
- 12, 13, 14, 14, 13, 15, // bottom
- 16, 17, 18, 18, 17, 19, // left
- 20, 21, 22, 22, 21, 23, // right
- });
- SetOutline({
- { bb.min.x, bb.min.y, bb.min.z }, // back
- { bb.max.x, bb.min.y, bb.min.z },
- { bb.min.x, bb.max.y, bb.min.z },
- { bb.max.x, bb.max.y, bb.min.z },
- { bb.min.x, bb.min.y, bb.max.z }, // front
- { bb.max.x, bb.min.y, bb.max.z },
- { bb.min.x, bb.max.y, bb.max.z },
- { bb.max.x, bb.max.y, bb.max.z },
- }, {
- 0, 1, 1, 3, 3, 2, 2, 0, // back
- 4, 5, 5, 7, 7, 6, 6, 4, // front
- 0, 4, 1, 5, 2, 6, 3, 7, // sides
- });
-}
-
-bool CuboidShape::Intersects(
- const Ray &ray,
- const glm::mat4 &M,
- float &dist, glm::vec3 &normal
-) const noexcept {
- return Intersection(ray, bb, M, &dist, &normal);
-}
-
-
-StairShape::StairShape(const AABB &bb, const glm::vec2 &clip)
-: Shape()
-, top({ { bb.min.x, clip.y, bb.min.z }, { bb.max.x, bb.max.y, clip.x } })
-, bot({ bb.min, { bb.max.x, clip.y, bb.max.z } }) {
- SetShape({
- { top.min.x, top.min.y, top.max.z }, // front, upper
- { top.max.x, top.min.y, top.max.z },
- { top.min.x, top.max.y, top.max.z },
- { top.max.x, top.max.y, top.max.z },
- { bot.min.x, bot.min.y, bot.max.z }, // front, lower
- { bot.max.x, bot.min.y, bot.max.z },
- { bot.min.x, bot.max.y, bot.max.z },
- { bot.max.x, bot.max.y, bot.max.z },
- { bot.min.x, bot.min.y, bot.min.z }, // back
- { bot.min.x, top.max.y, bot.min.z },
- { top.max.x, bot.min.y, bot.min.z },
- { top.max.x, top.max.y, bot.min.z },
- { top.min.x, top.max.y, top.min.z }, // top, upper
- { top.min.x, top.max.y, top.max.z },
- { top.max.x, top.max.y, top.min.z },
- { top.max.x, top.max.y, top.max.z },
- { bot.min.x, bot.max.y, top.max.z }, // top, lower
- { bot.min.x, bot.max.y, bot.max.z },
- { bot.max.x, bot.max.y, top.max.z },
- { bot.max.x, bot.max.y, bot.max.z },
- { bot.min.x, bot.min.y, bot.min.z }, // bottom
- { bot.max.x, bot.min.y, bot.min.z },
- { bot.min.x, bot.min.y, bot.max.z },
- { bot.max.x, bot.min.y, bot.max.z },
- { top.min.x, top.min.y, top.min.z }, // left, upper
- { top.min.x, top.min.y, top.max.z },
- { top.min.x, top.max.y, top.min.z },
- { top.min.x, top.max.y, top.max.z },
- { bot.min.x, bot.min.y, bot.min.z }, // left, lower
- { bot.min.x, bot.min.y, bot.max.z },
- { bot.min.x, bot.max.y, bot.min.z },
- { bot.min.x, bot.max.y, bot.max.z },
- { top.max.x, top.min.y, top.min.z }, // right, upper
- { top.max.x, top.max.y, top.min.z },
- { top.max.x, top.min.y, top.max.z },
- { top.max.x, top.max.y, top.max.z },
- { bot.max.x, bot.min.y, bot.min.z }, // right, lower
- { bot.max.x, bot.max.y, bot.min.z },
- { bot.max.x, bot.min.y, bot.max.z },
- { bot.max.x, bot.max.y, bot.max.z },
- }, {
- { 0.0f, 0.0f, 1.0f }, // front x2
- { 0.0f, 0.0f, 1.0f },
- { 0.0f, 0.0f, 1.0f },
- { 0.0f, 0.0f, 1.0f },
- { 0.0f, 0.0f, 1.0f },
- { 0.0f, 0.0f, 1.0f },
- { 0.0f, 0.0f, 1.0f },
- { 0.0f, 0.0f, 1.0f },
- { 0.0f, 0.0f, -1.0f }, // back
- { 0.0f, 0.0f, -1.0f },
- { 0.0f, 0.0f, -1.0f },
- { 0.0f, 0.0f, -1.0f },
- { 0.0f, 1.0f, 0.0f }, // top x2
- { 0.0f, 1.0f, 0.0f },
- { 0.0f, 1.0f, 0.0f },
- { 0.0f, 1.0f, 0.0f },
- { 0.0f, 1.0f, 0.0f },
- { 0.0f, 1.0f, 0.0f },
- { 0.0f, 1.0f, 0.0f },
- { 0.0f, 1.0f, 0.0f },
- { 0.0f, -1.0f, 0.0f }, // bottom
- { 0.0f, -1.0f, 0.0f },
- { 0.0f, -1.0f, 0.0f },
- { 0.0f, -1.0f, 0.0f },
- { -1.0f, 0.0f, 0.0f }, // left x2
- { -1.0f, 0.0f, 0.0f },
- { -1.0f, 0.0f, 0.0f },
- { -1.0f, 0.0f, 0.0f },
- { -1.0f, 0.0f, 0.0f },
- { -1.0f, 0.0f, 0.0f },
- { -1.0f, 0.0f, 0.0f },
- { -1.0f, 0.0f, 0.0f },
- { 1.0f, 0.0f, 0.0f }, // right x2
- { 1.0f, 0.0f, 0.0f },
- { 1.0f, 0.0f, 0.0f },
- { 1.0f, 0.0f, 0.0f },
- { 1.0f, 0.0f, 0.0f },
- { 1.0f, 0.0f, 0.0f },
- { 1.0f, 0.0f, 0.0f },
- { 1.0f, 0.0f, 0.0f },
- }, {
- 0, 1, 2, 2, 1, 3, // front, upper
- 4, 5, 6, 6, 5, 7, // front, lower
- 8, 9, 10, 10, 9, 11, // back
- 12, 13, 14, 14, 13, 15, // top, upper
- 16, 17, 18, 18, 17, 19, // top, lower
- 20, 21, 22, 22, 21, 23, // bottom
- 24, 25, 26, 26, 25, 27, // left, upper
- 28, 29, 30, 30, 29, 31, // left, lower
- 32, 33, 34, 34, 33, 35, // right, upper
- 36, 37, 38, 38, 37, 39, // right, lower
- });
- SetOutline({
- { bot.min.x, bot.min.y, bot.min.z }, // bottom
- { bot.max.x, bot.min.y, bot.min.z },
- { bot.min.x, bot.min.y, bot.max.z },
- { bot.max.x, bot.min.y, bot.max.z },
- { bot.min.x, bot.max.y, top.max.z }, // middle
- { bot.max.x, bot.max.y, top.max.z },
- { bot.min.x, bot.max.y, bot.max.z },
- { bot.max.x, bot.max.y, bot.max.z },
- { top.min.x, top.max.y, top.min.z }, // top
- { top.max.x, top.max.y, top.min.z },
- { top.min.x, top.max.y, top.max.z },
- { top.max.x, top.max.y, top.max.z },
- }, {
- 0, 1, 1, 3, 3, 2, 2, 0, // bottom
- 4, 5, 5, 7, 7, 6, 6, 4, // middle
- 8, 9, 9, 11, 11, 10, 10 , 8, // top
- 0, 8, 4, 10, 2, 6, // verticals, btf
- 1, 9, 5, 11, 3, 7,
- // 5, 8, 7, 10,
- // 1, 9, 3, 11,
- });
-}
-
-bool StairShape::Intersects(
- const Ray &ray,
- const glm::mat4 &M,
- float &dist,
- glm::vec3 &norm
-) const noexcept {
- float top_dist, bot_dist;
- glm::vec3 top_norm, bot_norm;
- bool top_hit = Intersection(ray, top, M, &top_dist, &top_norm);
- bool bot_hit = Intersection(ray, bot, M, &bot_dist, &bot_norm);
-
- if (top_hit) {
- if (bot_hit) {
- if (top_dist < bot_dist) {
- dist = top_dist;
- norm = top_norm;
- return true;
- } else {
- dist = bot_dist;
- norm = bot_norm;
- return true;
- }
- } else {
- dist = top_dist;
- norm = top_norm;
- return true;
- }
- } else if (bot_hit) {
- dist = bot_dist;
- norm = bot_norm;
- return true;
- } else {
- return false;
- }
-}
-
-}
+++ /dev/null
-#ifndef BLANK_SHAPE_HPP_
-#define BLANK_SHAPE_HPP_
-
-#include "geometry.hpp"
-#include "model.hpp"
-
-#include <vector>
-#include <glm/glm.hpp>
-
-
-namespace blank {
-
-struct Shape {
-
- /// the number of vertices (and normals) this shape has
- size_t VertexCount() const noexcept { return vtx_pos.size(); }
- /// the number of vertex indices this shape has
- size_t VertexIndexCount() const noexcept { return vtx_idx.size(); }
-
- const Model::Normal &VertexNormal(size_t idx) const noexcept { return vtx_nrm[idx]; }
- Model::Normal VertexNormal(
- size_t idx, const glm::mat4 &transform
- ) const noexcept {
- return Model::Normal(transform * glm::vec4(vtx_nrm[idx], 0.0f));
- }
-
- /// fill given buffers with this shape's elements with an
- /// optional transform and offset
- void Vertices(
- Model::Positions &vertex,
- Model::Normals &normal,
- Model::Indices &index
- ) const;
- void Vertices(
- Model::Positions &vertex,
- Model::Normals &normal,
- Model::Indices &index,
- const glm::mat4 &transform,
- Model::Index idx_offset = 0
- ) const;
- void Vertices(
- BlockModel::Positions &vertex,
- BlockModel::Indices &index,
- const glm::mat4 &transform,
- BlockModel::Index idx_offset = 0
- ) const;
-
- /// the number of vertices this shape's outline has
- size_t OutlineCount() const { return out_pos.size(); }
- /// the number of vertex indices this shape's outline has
- size_t OutlineIndexCount() const { return out_idx.size(); }
-
- /// fill given buffers with this shape's outline's elements with
- /// an optional offset
- void Outline(
- OutlineModel::Positions &vertex,
- OutlineModel::Indices &index,
- const OutlineModel::Position &offset = { 0.0f, 0.0f, 0.0f },
- OutlineModel::Index idx_offset = 0
- ) const;
-
- /// Check if given ray would pass though this shape if it were
- /// transformed with given matrix.
- /// If true, dist and normal hold the intersection distance and
- /// normal, otherwise their content is undefined.
- virtual bool Intersects(
- const Ray &,
- const glm::mat4 &,
- float &dist,
- glm::vec3 &normal
- ) const noexcept = 0;
-
-protected:
- void SetShape(const Model::Positions &pos, const Model::Normals &nrm, const Model::Indices &idx) {
- vtx_pos = pos;
- vtx_nrm = nrm;
- vtx_idx = idx;
- }
- void SetOutline(const OutlineModel::Positions &pos, const OutlineModel::Indices &idx) {
- out_pos = pos;
- out_idx = idx;
- }
-
-private:
- Model::Positions vtx_pos;
- Model::Normals vtx_nrm;
- Model::Indices vtx_idx;
-
- OutlineModel::Positions out_pos;
- OutlineModel::Indices out_idx;
-
-};
-
-
-class NullShape
-: public Shape {
-
-public:
- NullShape();
-
- bool Intersects(const Ray &, const glm::mat4 &, float &, glm::vec3 &) const noexcept override;
-
-};
-
-
-class CuboidShape
-: public Shape {
-
-public:
- CuboidShape(const AABB &bounds);
-
- bool Intersects(const Ray &, const glm::mat4 &, float &, glm::vec3 &) const noexcept override;
-
-private:
- AABB bb;
-
-};
-
-
-class StairShape
-: public Shape {
-
-public:
- StairShape(const AABB &bounds, const glm::vec2 &clip);
-
- bool Intersects(const Ray &, const glm::mat4 &, float &, glm::vec3 &) const noexcept override;
-
-private:
- AABB top, bot;
-
-};
-
-}
-
-#endif
+++ /dev/null
-#ifndef BLANK_TIMER_HPP
-#define BLANK_TIMER_HPP
-
-
-namespace blank {
-
-class IntervalTimer {
-
-public:
- explicit IntervalTimer(int interval_ms) noexcept
- : intv(interval_ms) { }
-
- void Start() noexcept {
- speed = 1;
- }
- void Stop() noexcept {
- value = 0;
- speed = 0;
- }
-
- bool Running() const noexcept {
- return speed != 0;
- }
- bool Hit() const noexcept {
- return Running() && value % intv < last_dt;
- }
- int Elapsed() const noexcept {
- return value;
- }
- int Iteration() const noexcept {
- return value / intv;
- }
-
- void Update(int dt) noexcept {
- value += dt * speed;
- last_dt = dt;
- }
-
-private:
- int intv;
- int value = 0;
- int speed = 0;
- int last_dt = 0;
-
-};
-
-}
-
-#endif
--- /dev/null
+#ifndef BLANK_UI_HUD_H_
+#define BLANK_UI_HUD_H_
+
+#include "../model/Model.hpp"
+#include "../model/OutlineModel.hpp"
+
+#include <glm/glm.hpp>
+
+
+namespace blank {
+
+class Block;
+class BlockTypeRegistry;
+class DirectionalLighting;
+
+class HUD {
+
+public:
+ explicit HUD(const BlockTypeRegistry &);
+
+ HUD(const HUD &) = delete;
+ HUD &operator =(const HUD &) = delete;
+
+ void Viewport(float width, float height) noexcept;
+ void Viewport(float x, float y, float width, float height) noexcept;
+
+ void Display(const Block &);
+
+ void Render(DirectionalLighting &) noexcept;
+
+private:
+ const BlockTypeRegistry &types;
+
+ Model block;
+ Model::Buffer block_buf;
+ glm::mat4 block_transform;
+ bool block_visible;
+
+ OutlineModel crosshair;
+ glm::mat4 crosshair_transform;
+
+ float near, far;
+ glm::mat4 projection;
+ glm::mat4 view;
+
+};
+
+}
+
+#endif
--- /dev/null
+#ifndef BLANK_UI_INTERFACE_HPP_
+#define BLANK_UI_INTERFACE_HPP_
+
+#include "HUD.hpp"
+#include "../app/FPSController.hpp"
+#include "../app/IntervalTimer.hpp"
+#include "../model/geometry.hpp"
+#include "../model/OutlineModel.hpp"
+#include "../world/Block.hpp"
+
+#include <SDL.h>
+#include <glm/glm.hpp>
+
+
+namespace blank {
+
+class Chunk;
+class DirectionalLighting;
+class World;
+
+class Interface {
+
+public:
+ struct Config {
+ float move_velocity = 0.005f;
+ float pitch_sensitivity = -0.0025f;
+ float yaw_sensitivity = -0.001f;
+
+ bool keyboard_disabled = false;
+ bool mouse_disabled = false;
+ bool visual_disabled = false;
+ };
+
+ Interface(const Config &, World &);
+
+ void HandlePress(const SDL_KeyboardEvent &);
+ void HandleRelease(const SDL_KeyboardEvent &);
+ void Handle(const SDL_MouseMotionEvent &);
+ void HandlePress(const SDL_MouseButtonEvent &);
+ void HandleRelease(const SDL_MouseButtonEvent &);
+ void Handle(const SDL_MouseWheelEvent &);
+ void Handle(const SDL_WindowEvent &) noexcept;
+
+ void FaceBlock();
+ void TurnBlock();
+
+ void PickBlock();
+ void PlaceBlock();
+ void RemoveBlock() noexcept;
+
+ void PrintBlockInfo();
+ void PrintChunkInfo();
+ void PrintLightInfo();
+ void PrintSelectionInfo();
+ void Print(const Block &);
+
+ void SelectNext();
+ void SelectPrevious();
+
+ void Update(int dt);
+
+ void Render(DirectionalLighting &) noexcept;
+
+private:
+ void CheckAim();
+
+private:
+ World &world;
+ FPSController ctrl;
+ HUD hud;
+
+ Ray aim;
+ Chunk *aim_chunk;
+ int aim_block;
+ glm::vec3 aim_normal;
+
+ OutlineModel outline;
+ glm::mat4 outline_transform;
+
+ Config config;
+
+ IntervalTimer place_timer;
+ IntervalTimer remove_timer;
+
+ Block remove;
+ Block selection;
+
+ glm::tvec3<int> fwd, rev;
+
+};
+
+}
+
+#endif
--- /dev/null
+#include "HUD.hpp"
+#include "Interface.hpp"
+
+#include "../app/init.hpp"
+#include "../graphics/DirectionalLighting.hpp"
+#include "../model/shapes.hpp"
+#include "../world/World.hpp"
+
+#include <iostream>
+#include <glm/gtc/matrix_transform.hpp>
+#include <glm/gtx/io.hpp>
+
+
+namespace blank {
+
+HUD::HUD(const BlockTypeRegistry &types)
+: types(types)
+, block()
+, block_buf()
+, block_transform(1.0f)
+, block_visible(false)
+, crosshair()
+, crosshair_transform(1.0f)
+, near(100.0f)
+, far(-100.0f)
+, projection(glm::ortho(0.0f, 1.0f, 1.0f, 0.0f, near, far))
+, view(glm::translate(glm::mat4(1.0f), glm::vec3(-0.5f, -0.5f, 0))) {
+ block_transform = glm::translate(block_transform, glm::vec3(50.0f, 50.0f, 0.0f));
+ block_transform = glm::scale(block_transform, glm::vec3(50.0f));
+ block_transform = glm::rotate(block_transform, 3.5f, glm::vec3(1.0f, 0.0f, 0.0f));
+ block_transform = glm::rotate(block_transform, 0.35f, glm::vec3(0.0f, 1.0f, 0.0f));
+
+ crosshair.vertices = std::vector<glm::vec3>({
+ { -10.0f, 0.0f, 0.0f }, { 10.0f, 0.0f, 0.0f },
+ { 0.0f, -10.0f, 0.0f }, { 0.0f, 10.0f, 0.0f },
+ });
+ crosshair.indices = std::vector<OutlineModel::Index>({
+ 0, 1, 2, 3
+ });
+ crosshair.colors.resize(4, { 10.0f, 10.0f, 10.0f });
+ crosshair.Invalidate();
+}
+
+
+void HUD::Viewport(float width, float height) noexcept {
+ Viewport(0, 0, width, height);
+}
+
+void HUD::Viewport(float x, float y, float width, float height) noexcept {
+ projection = glm::ortho(x, width, height, y, near, far);
+ crosshair_transform = glm::translate(glm::mat4(1.0f), glm::vec3(width * 0.5f, height * 0.5f, 0.0f));
+}
+
+
+void HUD::Display(const Block &b) {
+ const BlockType &type = types.Get(b.type);
+
+ block_buf.Clear();
+ type.FillModel(block_buf, b.Transform());
+ block.Update(block_buf);
+ block_visible = type.visible;
+}
+
+
+void HUD::Render(DirectionalLighting &program) noexcept {
+ if (block_visible) {
+ program.SetLightDirection({ 1.0f, 3.0f, 5.0f });
+ // disable distance fog
+ program.SetFogDensity(0.0f);
+ GLContext::ClearDepthBuffer();
+ program.SetMVP(block_transform, view, projection);
+ block.Draw();
+ program.SetM(crosshair_transform);
+ crosshair.Draw();
+ }
+}
+
+
+Interface::Interface(const Config &config, World &world)
+: world(world)
+, ctrl(world.Player())
+, hud(world.BlockTypes())
+, aim{{ 0, 0, 0 }, { 0, 0, -1 }}
+, aim_chunk(nullptr)
+, aim_block(0)
+, aim_normal()
+, outline()
+, outline_transform(1.0f)
+, config(config)
+, place_timer(256)
+, remove_timer(256)
+, remove(0)
+, selection(1)
+, fwd(0)
+, rev(0) {
+ hud.Viewport(960, 600);
+ hud.Display(selection);
+}
+
+
+void Interface::HandlePress(const SDL_KeyboardEvent &event) {
+ if (config.keyboard_disabled) return;
+
+ switch (event.keysym.sym) {
+ case SDLK_w:
+ rev.z = 1;
+ break;
+ case SDLK_s:
+ fwd.z = 1;
+ break;
+ case SDLK_a:
+ rev.x = 1;
+ break;
+ case SDLK_d:
+ fwd.x = 1;
+ break;
+ case SDLK_SPACE:
+ fwd.y = 1;
+ break;
+ case SDLK_LSHIFT:
+ rev.y = 1;
+ break;
+
+ case SDLK_q:
+ FaceBlock();
+ break;
+ case SDLK_e:
+ TurnBlock();
+ break;
+
+ case SDLK_b:
+ PrintBlockInfo();
+ break;
+ case SDLK_c:
+ PrintChunkInfo();
+ break;
+ case SDLK_l:
+ PrintLightInfo();
+ break;
+ case SDLK_p:
+ PrintSelectionInfo();
+ break;
+ }
+}
+
+void Interface::HandleRelease(const SDL_KeyboardEvent &event) {
+ if (config.keyboard_disabled) return;
+
+ switch (event.keysym.sym) {
+ case SDLK_w:
+ rev.z = 0;
+ break;
+ case SDLK_s:
+ fwd.z = 0;
+ break;
+ case SDLK_a:
+ rev.x = 0;
+ break;
+ case SDLK_d:
+ fwd.x = 0;
+ break;
+ case SDLK_SPACE:
+ fwd.y = 0;
+ break;
+ case SDLK_LSHIFT:
+ rev.y = 0;
+ break;
+ }
+}
+
+void Interface::FaceBlock() {
+ selection.SetFace(Block::Face((selection.GetFace() + 1) % Block::FACE_COUNT));
+ hud.Display(selection);
+}
+
+void Interface::TurnBlock() {
+ selection.SetTurn(Block::Turn((selection.GetTurn() + 1) % Block::TURN_COUNT));
+ hud.Display(selection);
+}
+
+void Interface::PrintBlockInfo() {
+ std::cout << std::endl;
+ if (!aim_chunk) {
+ std::cout << "not looking at any block" << std::endl;
+ Ray aim = ctrl.Aim();
+ std::cout << "aim ray: " << aim.orig << ", " << aim.dir << std::endl;
+ return;
+ }
+ std::cout << "looking at block " << aim_block
+ << " " << Chunk::ToCoords(aim_block)
+ << " of chunk " << aim_chunk->Position()
+ << std::endl;
+ Print(aim_chunk->BlockAt(aim_block));
+}
+
+void Interface::PrintChunkInfo() {
+ std::cout << std::endl;
+ if (!aim_chunk) {
+ std::cout << "not looking at any block" << std::endl;
+ return;
+ }
+ std::cout << "looking at chunk "
+ << aim_chunk->Position()
+ << std::endl;
+
+ std::cout << " neighbors:" << std::endl;
+ if (aim_chunk->HasNeighbor(Block::FACE_LEFT)) {
+ std::cout << " left " << aim_chunk->GetNeighbor(Block::FACE_LEFT).Position() << std::endl;
+ }
+ if (aim_chunk->HasNeighbor(Block::FACE_RIGHT)) {
+ std::cout << " right " << aim_chunk->GetNeighbor(Block::FACE_RIGHT).Position() << std::endl;
+ }
+ if (aim_chunk->HasNeighbor(Block::FACE_UP)) {
+ std::cout << " up " << aim_chunk->GetNeighbor(Block::FACE_UP).Position() << std::endl;
+ }
+ if (aim_chunk->HasNeighbor(Block::FACE_DOWN)) {
+ std::cout << " down " << aim_chunk->GetNeighbor(Block::FACE_DOWN).Position() << std::endl;
+ }
+ if (aim_chunk->HasNeighbor(Block::FACE_FRONT)) {
+ std::cout << " front " << aim_chunk->GetNeighbor(Block::FACE_FRONT).Position() << std::endl;
+ }
+ if (aim_chunk->HasNeighbor(Block::FACE_BACK)) {
+ std::cout << " back " << aim_chunk->GetNeighbor(Block::FACE_BACK).Position() << std::endl;
+ }
+ std::cout << std::endl;
+}
+
+void Interface::PrintLightInfo() {
+ std::cout
+ << "light level " << world.PlayerChunk().GetLight(world.Player().Position())
+ << " at position " << world.Player().Position()
+ << std::endl;
+}
+
+void Interface::PrintSelectionInfo() {
+ std::cout << std::endl;
+ Print(selection);
+}
+
+void Interface::Print(const Block &block) {
+ std::cout << "type: " << block.type
+ << ", face: " << block.GetFace()
+ << ", turn: " << block.GetTurn()
+ << std::endl;
+}
+
+
+void Interface::Handle(const SDL_MouseMotionEvent &event) {
+ if (config.mouse_disabled) return;
+ ctrl.RotateYaw(event.xrel * config.yaw_sensitivity);
+ ctrl.RotatePitch(event.yrel * config.pitch_sensitivity);
+}
+
+void Interface::HandlePress(const SDL_MouseButtonEvent &event) {
+ if (config.mouse_disabled) return;
+
+ if (event.button == SDL_BUTTON_LEFT) {
+ RemoveBlock();
+ remove_timer.Start();
+ } else if (event.button == SDL_BUTTON_MIDDLE) {
+ PickBlock();
+ } else if (event.button == SDL_BUTTON_RIGHT) {
+ PlaceBlock();
+ place_timer.Start();
+ }
+}
+
+void Interface::HandleRelease(const SDL_MouseButtonEvent &event) {
+ if (config.mouse_disabled) return;
+
+ if (event.button == SDL_BUTTON_LEFT) {
+ remove_timer.Stop();
+ } else if (event.button == SDL_BUTTON_RIGHT) {
+ place_timer.Stop();
+ }
+}
+
+void Interface::PickBlock() {
+ if (!aim_chunk) return;
+ selection = aim_chunk->BlockAt(aim_block);
+ hud.Display(selection);
+}
+
+void Interface::PlaceBlock() {
+ if (!aim_chunk) return;
+ Chunk *mod_chunk = aim_chunk;
+ glm::vec3 next_pos = Chunk::ToCoords(aim_block) + aim_normal;
+ if (!Chunk::InBounds(next_pos)) {
+ mod_chunk = &world.Next(*aim_chunk, aim_normal);
+ next_pos -= aim_normal * glm::vec3(Chunk::Extent());
+ }
+ mod_chunk->SetBlock(next_pos, selection);
+ mod_chunk->Invalidate();
+}
+
+void Interface::RemoveBlock() noexcept {
+ if (!aim_chunk) return;
+ aim_chunk->SetBlock(aim_block, remove);
+ aim_chunk->Invalidate();
+}
+
+
+void Interface::Handle(const SDL_MouseWheelEvent &event) {
+ if (config.mouse_disabled) return;
+
+ if (event.y < 0) {
+ SelectNext();
+ } else if (event.y > 0) {
+ SelectPrevious();
+ }
+}
+
+void Interface::SelectNext() {
+ ++selection.type;
+ if (size_t(selection.type) >= world.BlockTypes().Size()) {
+ selection.type = 1;
+ }
+ hud.Display(selection);
+}
+
+void Interface::SelectPrevious() {
+ --selection.type;
+ if (selection.type <= 0) {
+ selection.type = world.BlockTypes().Size() - 1;
+ }
+ hud.Display(selection);
+}
+
+void Interface::Handle(const SDL_WindowEvent &event) noexcept {
+ if (event.event == SDL_WINDOWEVENT_RESIZED) {
+ hud.Viewport(event.data1, event.data2);
+ }
+}
+
+
+void Interface::Update(int dt) {
+ ctrl.Velocity(glm::vec3(fwd - rev) * config.move_velocity);
+ ctrl.Update(dt);
+
+ place_timer.Update(dt);
+ remove_timer.Update(dt);
+
+ aim = ctrl.Aim();
+ CheckAim();
+
+ if (remove_timer.Hit()) {
+ RemoveBlock();
+ CheckAim();
+ }
+
+ if (place_timer.Hit()) {
+ PlaceBlock();
+ CheckAim();
+ }
+}
+
+void Interface::CheckAim() {
+ float dist;
+ if (world.Intersection(aim, glm::mat4(1.0f), &aim_chunk, &aim_block, &dist, &aim_normal)) {
+ outline.Clear();
+ aim_chunk->Type(aim_chunk->BlockAt(aim_block)).FillOutlineModel(outline);
+ outline_transform = glm::scale(glm::vec3(1.0002f));
+ outline_transform *= aim_chunk->Transform(world.Player().ChunkCoords());
+ outline_transform *= aim_chunk->ToTransform(Chunk::ToPos(aim_block), aim_block);
+ } else {
+ aim_chunk = nullptr;
+ }
+}
+
+
+void Interface::Render(DirectionalLighting &program) noexcept {
+ if (config.visual_disabled) return;
+
+ if (aim_chunk) {
+ program.SetM(outline_transform);
+ outline.Draw();
+ }
+
+ hud.Render(program);
+}
+
+}
+++ /dev/null
-#include "world.hpp"
-
-#include <limits>
-#include <glm/gtx/transform.hpp>
-
-
-namespace blank {
-
-World::World(const Config &config)
-: blockType()
-, blockShape({{ -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f }})
-, stairShape({{ -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f }}, { 0.0f, 0.0f })
-, slabShape({{ -0.5f, -0.5f, -0.5f }, { 0.5f, 0.0f, 0.5f }})
-, generate(config.gen)
-, chunks(config.load, blockType, generate)
-, player()
-, entities()
-, light_direction(config.light_direction)
-, fog_density(config.fog_density) {
- BlockType::Faces block_fill = { true, true, true, true, true, true };
- BlockType::Faces slab_fill = { false, true, false, false, false, false };
- BlockType::Faces stair_fill = { false, true, false, false, false, true };
-
- { // white block
- BlockType type(true, { 1.0f, 1.0f, 1.0f }, &blockShape);
- type.block_light = true;
- type.fill = block_fill;
- blockType.Add(type);
- }
- { // white slab
- BlockType type(true, { 1.0f, 1.0f, 1.0f }, &slabShape);
- type.block_light = true;
- type.fill = slab_fill;
- blockType.Add(type);
- }
- { // white stair
- BlockType type(true, { 1.0f, 1.0f, 1.0f }, &stairShape);
- type.block_light = true;
- type.fill = stair_fill;
- blockType.Add(type);
- }
-
- { // red block
- BlockType type(true, { 1.0f, 0.0f, 0.0f }, &blockShape);
- type.block_light = true;
- type.fill = block_fill;
- blockType.Add(type);
- }
- { // red slab
- BlockType type(true, { 1.0f, 0.0f, 0.0f }, &slabShape);
- type.block_light = true;
- type.fill = slab_fill;
- blockType.Add(type);
- }
- { // red stair
- BlockType type(true, { 1.0f, 0.0f, 0.0f }, &stairShape);
- type.block_light = true;
- type.fill = stair_fill;
- blockType.Add(type);
- }
-
- { // green block
- BlockType type(true, { 0.0f, 1.0f, 0.0f }, &blockShape);
- type.block_light = true;
- type.fill = block_fill;
- blockType.Add(type);
- }
- { // green slab
- BlockType type(true, { 0.0f, 1.0f, 0.0f }, &slabShape);
- type.block_light = true;
- type.fill = slab_fill;
- blockType.Add(type);
- }
- { // green stair
- BlockType type(true, { 0.0f, 1.0f, 0.0f }, &stairShape);
- type.block_light = true;
- type.fill = stair_fill;
- blockType.Add(type);
- }
-
- { // blue block
- BlockType type(true, { 0.0f, 0.0f, 1.0f }, &blockShape);
- type.block_light = true;
- type.fill = block_fill;
- blockType.Add(type);
- }
- { // blue slab
- BlockType type(true, { 0.0f, 0.0f, 1.0f }, &slabShape);
- type.block_light = true;
- type.fill = slab_fill;
- blockType.Add(type);
- }
- { // blue stair
- BlockType type(true, { 0.0f, 0.0f, 1.0f }, &stairShape);
- type.block_light = true;
- type.fill = stair_fill;
- blockType.Add(type);
- }
-
- { // glowing yellow block
- BlockType type(true, { 1.0f, 1.0f, 0.0f }, &blockShape);
- type.luminosity = 15;
- type.block_light = true;
- type.fill = block_fill;
- blockType.Add(type);
- }
-
- generate.Space(0);
- generate.Light(13);
- generate.Solids({ 1, 4, 7, 10 });
-
- player = &AddEntity();
- player->Position(config.spawn);
-
- chunks.GenerateSurrounding(player->ChunkCoords());
-}
-
-
-namespace {
-
-struct Candidate {
- Chunk *chunk;
- float dist;
-};
-
-std::vector<Candidate> candidates;
-
-}
-
-bool World::Intersection(
- const Ray &ray,
- const glm::mat4 &M,
- Chunk **chunk,
- int *blkid,
- float *dist,
- glm::vec3 *normal) {
- candidates.clear();
-
- for (Chunk &cur_chunk : chunks.Loaded()) {
- float cur_dist;
- if (cur_chunk.Intersection(ray, M * cur_chunk.Transform(player->ChunkCoords()), cur_dist)) {
- candidates.push_back({ &cur_chunk, cur_dist });
- }
- }
-
- if (candidates.empty()) return false;
-
- Chunk *closest_chunk = nullptr;
- float closest_dist = std::numeric_limits<float>::infinity();
- int closest_blkid = -1;
- glm::vec3 closest_normal;
-
- for (Candidate &cand : candidates) {
- if (cand.dist > closest_dist) continue;
- int cur_blkid;
- float cur_dist;
- glm::vec3 cur_normal;
- if (cand.chunk->Intersection(ray, M * cand.chunk->Transform(player->ChunkCoords()), cur_blkid, cur_dist, cur_normal)) {
- if (cur_dist < closest_dist) {
- closest_chunk = cand.chunk;
- closest_blkid = cur_blkid;
- closest_dist = cur_dist;
- closest_normal = cur_normal;
- }
- }
- }
-
- if (chunk) {
- *chunk = closest_chunk;
- }
- if (blkid) {
- *blkid = closest_blkid;
- }
- if (dist) {
- *dist = closest_dist;
- }
- if (normal) {
- *normal = closest_normal;
- }
- return closest_chunk;
-}
-
-
-Chunk &World::PlayerChunk() {
- return chunks.ForceLoad(player->ChunkCoords());
-}
-
-Chunk &World::Next(const Chunk &to, const glm::tvec3<int> &dir) {
- const Chunk::Pos tgt_pos = to.Position() + dir;
- return chunks.ForceLoad(tgt_pos);
-}
-
-
-void World::Update(int dt) {
- for (Entity &entity : entities) {
- entity.Update(dt);
- }
- chunks.Rebase(player->ChunkCoords());
- chunks.Update();
-}
-
-
-void World::Render(BlockLighting &chunk_prog, DirectionalLighting &entity_prog) {
- chunk_prog.Activate();
- chunk_prog.SetFogDensity(fog_density);
- chunk_prog.SetView(glm::inverse(player->Transform(player->ChunkCoords())));
-
- for (Chunk &chunk : chunks.Loaded()) {
- glm::mat4 m(chunk.Transform(player->ChunkCoords()));
- chunk_prog.SetM(m);
- glm::mat4 mvp(chunk_prog.GetVP() * m);
- if (!CullTest(Chunk::Bounds(), mvp)) {
- chunk.Draw();
- }
- }
-
- entity_prog.Activate();
- entity_prog.SetLightDirection(light_direction);
- entity_prog.SetFogDensity(fog_density);
- entity_prog.SetView(glm::inverse(player->Transform(player->ChunkCoords())));
-
- for (Entity &entity : entities) {
- if (entity.HasShape()) {
- entity_prog.SetM(entity.Transform(player->ChunkCoords()));
- entity.Draw();
- }
- }
-}
-
-}
+++ /dev/null
-#ifndef BLANK_WORLD_HPP_
-#define BLANK_WORLD_HPP_
-
-#include "block.hpp"
-#include "chunk.hpp"
-#include "entity.hpp"
-#include "generator.hpp"
-#include "shader.hpp"
-#include "shape.hpp"
-
-#include <list>
-#include <glm/glm.hpp>
-
-
-namespace blank {
-
-class World {
-
-public:
- struct Config {
- // initial player position
- glm::vec3 spawn = { 4.0f, 4.0f, 4.0f };
- // direction facing towards(!) the light
- glm::vec3 light_direction = { -1.0f, -3.0f, -2.0f };
- // fade out reaches 1/e (0.3679) at 1/fog_density,
- // gets less than 0.01 at e/(2 * fog_density)
- // I chose 0.011 because it yields 91 and 124 for those, so
- // slightly less than 6 and 8 chunks
- float fog_density = 0.011f;
-
- Generator::Config gen = Generator::Config();
- ChunkLoader::Config load = ChunkLoader::Config();
- };
-
- explicit World(const Config &);
-
- bool Intersection(
- const Ray &,
- const glm::mat4 &M,
- Chunk **chunk = nullptr,
- int *blkid = nullptr,
- float *dist = nullptr,
- glm::vec3 *normal = nullptr);
-
- BlockTypeRegistry &BlockTypes() { return blockType; }
-
- Entity &Player() { return *player; }
- Entity &AddEntity() { entities.emplace_back(); return entities.back(); }
-
- Chunk &PlayerChunk();
- Chunk &Next(const Chunk &to, const glm::tvec3<int> &dir);
-
- void Update(int dt);
-
- void Render(BlockLighting &, DirectionalLighting &);
-
-private:
- BlockTypeRegistry blockType;
- CuboidShape blockShape;
- StairShape stairShape;
- CuboidShape slabShape;
-
- Generator generate;
- ChunkLoader chunks;
-
- Entity *player;
- std::list<Entity> entities;
-
- glm::vec3 light_direction;
- float fog_density;
-
-};
-
-}
-
-#endif
--- /dev/null
+#ifndef BLANK_WORLD_BLOCK_HPP_
+#define BLANK_WORLD_BLOCK_HPP_
+
+#include <glm/glm.hpp>
+
+
+namespace blank {
+
+/// single 1x1x1 cube
+struct Block {
+
+ using Type = unsigned short;
+ using Pos = glm::vec3;
+
+ enum Face {
+ FACE_UP,
+ FACE_DOWN,
+ FACE_RIGHT,
+ FACE_LEFT,
+ FACE_FRONT,
+ FACE_BACK,
+ FACE_COUNT,
+ };
+ enum Turn {
+ TURN_NONE,
+ TURN_LEFT,
+ TURN_AROUND,
+ TURN_RIGHT,
+ TURN_COUNT,
+ };
+
+ static constexpr int ORIENT_COUNT = FACE_COUNT * TURN_COUNT;
+
+ Type type;
+ unsigned char orient;
+
+ constexpr explicit Block(Type type = 0, Face face = FACE_UP, Turn turn = TURN_NONE) noexcept
+ : type(type), orient(face * TURN_COUNT + turn) { }
+
+ const glm::mat4 &Transform() const noexcept { return orient2transform[orient]; }
+
+ Face GetFace() const noexcept { return Face(orient / TURN_COUNT); }
+ void SetFace(Face face) noexcept { orient = face * TURN_COUNT + GetTurn(); }
+ Turn GetTurn() const noexcept { return Turn(orient % TURN_COUNT); }
+ void SetTurn(Turn turn) noexcept { orient = GetFace() * TURN_COUNT + turn; }
+
+ Face OrientedFace(Face f) const noexcept { return orient2face[orient][f]; }
+
+ static Face Opposite(Face f) noexcept {
+ return Face(f ^ 1);
+ }
+
+ static int Axis(Face f) noexcept {
+ switch (f) {
+ case FACE_UP:
+ case FACE_DOWN:
+ return 1;
+ default:
+ case FACE_RIGHT:
+ case FACE_LEFT:
+ return 0;
+ case FACE_FRONT:
+ case FACE_BACK:
+ return 2;
+ }
+ }
+
+ static glm::tvec3<int> FaceNormal(Face face) noexcept {
+ return face2normal[face];
+ }
+
+ static Face NormalFace(const glm::vec3 &norm) noexcept {
+ const glm::vec3 anorm(abs(norm));
+ if (anorm.x > anorm.y) {
+ if (anorm.x > anorm.z) {
+ return norm.x > 0.0f ? FACE_RIGHT : FACE_LEFT;
+ } else {
+ return norm.z > 0.0f ? FACE_FRONT : FACE_BACK;
+ }
+ } else {
+ if (anorm.y > anorm.z) {
+ return norm.y > 0.0f ? FACE_UP : FACE_DOWN;
+ } else {
+ return norm.z > 0.0f ? FACE_FRONT : FACE_BACK;
+ }
+ }
+ }
+
+ struct FaceSet {
+
+ explicit FaceSet(unsigned char v = 0)
+ : value(v) { }
+
+ bool IsSet(Face f) const {
+ return value & Mask(f);
+ }
+ void Set(Face f) {
+ value |= Mask(f);
+ }
+ void Unset(Face f) {
+ value |= ~Mask(f);
+ }
+
+ void Clear() {
+ value = 0;
+ }
+ void Fill() {
+ value = Mask(FACE_COUNT) - 1;
+ }
+
+ bool Empty() const {
+ return value == 0;
+ }
+ bool All() const {
+ return value == Mask(FACE_COUNT) - 1;
+ }
+
+ unsigned char Mask(Face f) const {
+ return 1 << f;
+ }
+
+ unsigned char value;
+
+ };
+
+private:
+ static const glm::tvec3<int> face2normal[6];
+ static const glm::mat4 orient2transform[ORIENT_COUNT];
+ static const Face orient2face[ORIENT_COUNT][FACE_COUNT];
+
+};
+
+}
+
+#endif
--- /dev/null
+#ifndef BLANK_WORLD_BLOCKLOOKUP_HPP_
+#define BLANK_WORLD_BLOCKLOOKUP_HPP_
+
+#include "Block.hpp"
+#include "Chunk.hpp"
+
+
+namespace blank {
+
+class BlockLookup {
+
+public:
+ // resolve chunk/position from oob coordinates
+ BlockLookup(Chunk *c, const Chunk::Pos &p) noexcept;
+
+ // resolve chunk/position from ib coordinates and direction
+ BlockLookup(Chunk *c, const Chunk::Pos &p, Block::Face dir) noexcept;
+
+ // check if lookup was successful
+ operator bool() const { return chunk; }
+
+ // only valid if lookup was successful
+ Chunk &GetChunk() const noexcept { return *chunk; }
+ const Chunk::Pos &GetBlockPos() const noexcept { return pos; }
+ const Block &GetBlock() const noexcept { return GetChunk().BlockAt(GetBlockPos()); }
+ const BlockType &GetType() const noexcept { return GetChunk().Type(GetBlock()); }
+ int GetLight() const noexcept { return GetChunk().GetLight(GetBlockPos()); }
+
+ // traverse in given direction
+ BlockLookup Next(Block::Face f) const { return BlockLookup(chunk, pos, f); }
+
+private:
+ Chunk *chunk;
+ Chunk::Pos pos;
+
+};
+
+}
+
+#endif
--- /dev/null
+#ifndef BLANK_WORLD_BLOCKTYPE_HPP_
+#define BLANK_WORLD_BLOCKTYPE_HPP_
+
+#include "Block.hpp"
+#include "../model/BlockModel.hpp"
+#include "../model/Model.hpp"
+#include "../model/OutlineModel.hpp"
+#include "../model/shapes.hpp"
+
+#include <glm/glm.hpp>
+
+
+namespace blank {
+
+/// single 1x1x1 cube
+/// attributes of a type of block
+struct BlockType {
+
+ const Shape *shape;
+ glm::vec3 color;
+ glm::vec3 outline_color;
+
+ Block::Type id;
+
+ int luminosity;
+
+ bool visible;
+ bool block_light;
+
+ struct Faces {
+ bool face[Block::FACE_COUNT];
+ Faces &operator =(const Faces &other) noexcept {
+ for (int i = 0; i < Block::FACE_COUNT; ++i) {
+ face[i] = other.face[i];
+ }
+ return *this;
+ }
+ bool operator [](Block::Face f) const noexcept {
+ return face[f];
+ }
+ } fill;
+
+ explicit BlockType(
+ bool v = false,
+ const glm::vec3 &color = { 1, 1, 1 },
+ const Shape *shape = &DEFAULT_SHAPE
+ ) noexcept;
+
+ static const NullShape DEFAULT_SHAPE;
+
+ bool FaceFilled(const Block &block, Block::Face face) const noexcept {
+ return fill[block.OrientedFace(face)];
+ }
+
+ void FillModel(
+ Model::Buffer &m,
+ const glm::mat4 &transform = glm::mat4(1.0f),
+ Model::Index idx_offset = 0
+ ) const noexcept;
+ void FillBlockModel(
+ BlockModel::Buffer &m,
+ const glm::mat4 &transform = glm::mat4(1.0f),
+ BlockModel::Index idx_offset = 0
+ ) const noexcept;
+ void FillOutlineModel(
+ OutlineModel &m,
+ const glm::vec3 &pos_offset = { 0, 0, 0 },
+ OutlineModel::Index idx_offset = 0
+ ) const noexcept;
+
+};
+
+}
+
+#endif
--- /dev/null
+#ifndef BLANK_WORLD_BLOCKTYPEREGISTRY_HPP_
+#define BLANK_WORLD_BLOCKTYPEREGISTRY_HPP_
+
+#include "BlockType.hpp"
+
+#include <vector>
+
+
+namespace blank {
+
+class BlockTypeRegistry {
+
+public:
+ BlockTypeRegistry();
+
+public:
+ Block::Type Add(const BlockType &);
+
+ size_t Size() const noexcept { return types.size(); }
+
+ BlockType &operator [](Block::Type id) { return types[id]; }
+ const BlockType &Get(Block::Type id) const { return types[id]; }
+
+private:
+ std::vector<BlockType> types;
+
+};
+
+}
+
+#endif
--- /dev/null
+#ifndef BLANK_WORLD_CHUNK_HPP_
+#define BLANK_WORLD_CHUNK_HPP_
+
+#include "Block.hpp"
+#include "BlockTypeRegistry.hpp"
+#include "../model/BlockModel.hpp"
+#include "../model/geometry.hpp"
+
+#include <glm/glm.hpp>
+#include <glm/gtx/transform.hpp>
+
+
+namespace blank {
+
+class BlockType;
+
+/// cube of size 16 (256 tiles, 4096 blocks)
+class Chunk {
+
+public:
+ using Pos = glm::tvec3<int>;
+
+public:
+ explicit Chunk(const BlockTypeRegistry &) noexcept;
+
+ Chunk(Chunk &&) noexcept;
+ Chunk &operator =(Chunk &&) noexcept;
+
+ static constexpr int width = 16;
+ static constexpr int height = 16;
+ static constexpr int depth = 16;
+ static Pos Extent() noexcept { return { width, height, depth }; }
+ static constexpr int size = width * height * depth;
+
+ static AABB Bounds() noexcept { return AABB{ { 0, 0, 0 }, Extent() }; }
+
+ static constexpr bool InBounds(const Block::Pos &pos) noexcept {
+ return
+ pos.x >= 0 && pos.x < width &&
+ pos.y >= 0 && pos.y < height &&
+ pos.z >= 0 && pos.z < depth;
+ }
+ static constexpr bool InBounds(const Pos &pos) noexcept {
+ return
+ pos.x >= 0 && pos.x < width &&
+ pos.y >= 0 && pos.y < height &&
+ pos.z >= 0 && pos.z < depth;
+ }
+ static constexpr int ToIndex(const Pos &pos) noexcept {
+ return pos.x + pos.y * width + pos.z * width * height;
+ }
+ static constexpr bool InBounds(int idx) noexcept {
+ return idx >= 0 && idx < size;
+ }
+ static Block::Pos ToCoords(int idx) noexcept {
+ return Block::Pos(
+ 0.5f + (idx % width),
+ 0.5f + ((idx / width) % height),
+ 0.5f + (idx / (width * height))
+ );
+ }
+ static Block::Pos ToCoords(const Pos &pos) noexcept {
+ return Block::Pos(pos) + 0.5f;
+ }
+ static Pos ToPos(int idx) noexcept {
+ return Pos(
+ (idx % width),
+ ((idx / width) % height),
+ (idx / (width * height))
+ );
+ }
+ glm::mat4 ToTransform(const Pos &pos, int idx) const noexcept;
+
+ static constexpr bool IsBorder(int idx) noexcept {
+ return
+ idx < width * height || // low Z plane
+ idx % width == 0 || // low X plane
+ (idx / (width * height)) == depth - 1 || // high Z plane
+ idx % width == width - 1 || // high X plane
+ (idx / width) % height == 0 || // low Y plane
+ (idx / width) % height == height - 1; // high Y plane
+ }
+
+ bool IsSurface(int index) const noexcept { return IsSurface(ToPos(index)); }
+ bool IsSurface(const Block::Pos &pos) const noexcept { return IsSurface(Pos(pos)); }
+ bool IsSurface(const Pos &pos) const noexcept;
+
+ void SetNeighbor(Chunk &) noexcept;
+ bool HasNeighbor(Block::Face f) const noexcept { return neighbor[f]; }
+ Chunk &GetNeighbor(Block::Face f) noexcept { return *neighbor[f]; }
+ const Chunk &GetNeighbor(Block::Face f) const noexcept { return *neighbor[f]; }
+ void ClearNeighbors() noexcept;
+ void Unlink() noexcept;
+ void Relink() noexcept;
+
+ // check which faces of a block at given index are obstructed (and therefore invisible)
+ Block::FaceSet Obstructed(const Pos &) const noexcept;
+
+ void Invalidate() noexcept { dirty = true; }
+
+ void SetBlock(int index, const Block &) noexcept;
+ void SetBlock(const Block::Pos &pos, const Block &block) noexcept { SetBlock(ToIndex(pos), block); }
+ void SetBlock(const Pos &pos, const Block &block) noexcept { SetBlock(ToIndex(pos), block); }
+
+ const Block &BlockAt(int index) const noexcept { return blocks[index]; }
+ const Block &BlockAt(const Block::Pos &pos) const noexcept { return BlockAt(ToIndex(pos)); }
+ const Block &BlockAt(const Pos &pos) const noexcept { return BlockAt(ToIndex(pos)); }
+
+ const BlockType &Type(const Block &b) const noexcept { return types->Get(b.type); }
+ const BlockType &Type(int index) const noexcept { return Type(BlockAt(index)); }
+
+ void SetLight(int index, int level) noexcept;
+ void SetLight(const Pos &pos, int level) noexcept { SetLight(ToIndex(pos), level); }
+ void SetLight(const Block::Pos &pos, int level) noexcept { SetLight(ToIndex(pos), level); }
+
+ int GetLight(int index) const noexcept;
+ int GetLight(const Pos &pos) const noexcept { return GetLight(ToIndex(pos)); }
+ int GetLight(const Block::Pos &pos) const noexcept { return GetLight(ToIndex(pos)); }
+
+ float GetVertexLight(const Pos &, const BlockModel::Position &, const Model::Normal &) const noexcept;
+
+ bool Intersection(
+ const Ray &ray,
+ const glm::mat4 &M,
+ float &dist
+ ) const noexcept {
+ return blank::Intersection(ray, Bounds(), M, &dist);
+ }
+
+ bool Intersection(
+ const Ray &,
+ const glm::mat4 &M,
+ int &blkid,
+ float &dist,
+ glm::vec3 &normal) const noexcept;
+
+ void Position(const Pos &pos) noexcept { position = pos; }
+ const Pos &Position() const noexcept { return position; }
+ glm::mat4 Transform(const Pos &offset) const noexcept {
+ return glm::translate((position - offset) * Extent());
+ }
+
+ void CheckUpdate() noexcept;
+ void Draw() noexcept;
+
+private:
+ void Update() noexcept;
+
+private:
+ const BlockTypeRegistry *types;
+ Chunk *neighbor[Block::FACE_COUNT];
+ Block blocks[16 * 16 * 16];
+ unsigned char light[16 * 16 * 16];
+ BlockModel model;
+ Pos position;
+ bool dirty;
+
+};
+
+}
+
+#endif
--- /dev/null
+#ifndef BLANK_WORLD_CHUNKLOADER_HPP_
+#define BLANK_WORLD_CHUNKLOADER_HPP_
+
+#include "Chunk.hpp"
+
+#include <list>
+
+
+namespace blank {
+
+class BlockTypeRegistry;
+class Generator;
+
+class ChunkLoader {
+
+public:
+ struct Config {
+ int load_dist = 6;
+ int unload_dist = 8;
+ };
+
+ ChunkLoader(const Config &, const BlockTypeRegistry &, const Generator &) noexcept;
+
+ void Generate(const Chunk::Pos &from, const Chunk::Pos &to);
+ void GenerateSurrounding(const Chunk::Pos &);
+
+ std::list<Chunk> &Loaded() noexcept { return loaded; }
+
+ Chunk *Loaded(const Chunk::Pos &) noexcept;
+ bool Queued(const Chunk::Pos &) noexcept;
+ bool Known(const Chunk::Pos &) noexcept;
+ Chunk &ForceLoad(const Chunk::Pos &);
+
+ void Rebase(const Chunk::Pos &);
+ void Update();
+
+private:
+ Chunk &Generate(const Chunk::Pos &pos);
+ void Insert(Chunk &) noexcept;
+ void Remove(Chunk &) noexcept;
+
+private:
+ Chunk::Pos base;
+
+ const BlockTypeRegistry ®
+ const Generator &gen;
+
+ std::list<Chunk> loaded;
+ std::list<Chunk::Pos> to_generate;
+ std::list<Chunk> to_free;
+
+ int load_dist;
+ int unload_dist;
+
+};
+
+}
+
+#endif
--- /dev/null
+#include "Entity.hpp"
+
+#include "../model/geometry.hpp"
+#include "../model/Shape.hpp"
+
+#include <cmath>
+#include <glm/gtx/transform.hpp>
+
+namespace {
+
+blank::Model::Buffer model_buffer;
+
+}
+
+namespace blank {
+
+Entity::Entity() noexcept
+: shape(nullptr)
+, model()
+, velocity(0, 0, 0)
+, position(0, 0, 0)
+, chunk(0, 0, 0)
+, angular_velocity(1.0f, 0.0f, 0.0f, 0.0f)
+, rotation(1.0f) {
+
+}
+
+
+void Entity::SetShape(const Shape *s, const glm::vec3 &color) {
+ shape = s;
+ model_buffer.Clear();
+ shape->Vertices(model_buffer.vertices, model_buffer.normals, model_buffer.indices);
+ model_buffer.colors.resize(shape->VertexCount(), color);
+ model.Update(model_buffer);
+}
+
+void Entity::SetShapeless() noexcept {
+ shape = nullptr;
+}
+
+
+void Entity::Velocity(const glm::vec3 &vel) noexcept {
+ velocity = vel;
+}
+
+void Entity::Position(const Block::Pos &pos) noexcept {
+ position = pos;
+ while (position.x >= Chunk::width) {
+ position.x -= Chunk::width;
+ ++chunk.x;
+ }
+ while (position.x < 0) {
+ position.x += Chunk::width;
+ --chunk.x;
+ }
+ while (position.y >= Chunk::height) {
+ position.y -= Chunk::height;
+ ++chunk.y;
+ }
+ while (position.y < 0) {
+ position.y += Chunk::height;
+ --chunk.y;
+ }
+ while (position.z >= Chunk::depth) {
+ position.z -= Chunk::depth;
+ ++chunk.z;
+ }
+ while (position.z < 0) {
+ position.z += Chunk::depth;
+ --chunk.z;
+ }
+}
+
+void Entity::Move(const glm::vec3 &delta) noexcept {
+ Position(position + delta);
+}
+
+void Entity::AngularVelocity(const glm::quat &v) noexcept {
+ angular_velocity = v;
+}
+
+void Entity::Rotation(const glm::mat4 &rot) noexcept {
+ rotation = rot;
+}
+
+void Entity::Rotate(const glm::quat &delta) noexcept {
+ Rotation(rotation * glm::mat4_cast(delta));
+}
+
+glm::mat4 Entity::Transform(const Chunk::Pos &chunk_offset) const noexcept {
+ const glm::vec3 chunk_pos = (chunk - chunk_offset) * Chunk::Extent();
+ return glm::translate(position + chunk_pos) * rotation;
+}
+
+Ray Entity::Aim(const Chunk::Pos &chunk_offset) const noexcept {
+ glm::mat4 transform = Transform(chunk_offset);
+ glm::vec4 from = transform * glm::vec4(0.0f, 0.0f, 0.0f, 1.0f);
+ from /= from.w;
+ glm::vec4 to = transform * glm::vec4(0.0f, 0.0f, -1.0f, 1.0f);
+ to /= to.w;
+ return Ray{ glm::vec3(from), glm::normalize(glm::vec3(to - from)) };
+}
+
+void Entity::Update(int dt) noexcept {
+ Move(velocity * float(dt));
+ Rotate(angular_velocity * float(dt));
+}
+
+
+void Entity::Draw() noexcept {
+ model.Draw();
+}
+
+}
--- /dev/null
+#ifndef BLANK_WORLD_ENTITY_HPP_
+#define BLANK_WORLD_ENTITY_HPP_
+
+#include "Block.hpp"
+#include "Chunk.hpp"
+#include "../model/Model.hpp"
+
+#include <glm/glm.hpp>
+#include <glm/gtc/quaternion.hpp>
+
+
+namespace blank {
+
+class Ray;
+class Shape;
+
+class Entity {
+
+public:
+ Entity() noexcept;
+
+ bool HasShape() const noexcept { return shape; }
+ const Shape *GetShape() const noexcept { return shape; }
+ void SetShape(const Shape *, const glm::vec3 &color);
+ void SetShapeless() noexcept;
+
+ const glm::vec3 &Velocity() const noexcept { return velocity; }
+ void Velocity(const glm::vec3 &) noexcept;
+
+ const Block::Pos &Position() const noexcept { return position; }
+ void Position(const Block::Pos &) noexcept;
+ void Move(const glm::vec3 &delta) noexcept;
+
+ const Chunk::Pos ChunkCoords() const noexcept { return chunk; }
+
+ const glm::quat &AngularVelocity() const noexcept { return angular_velocity; }
+ void AngularVelocity(const glm::quat &) noexcept;
+
+ const glm::mat4 &Rotation() const noexcept { return rotation; }
+ void Rotation(const glm::mat4 &) noexcept;
+ void Rotate(const glm::quat &delta) noexcept;
+
+ glm::mat4 Transform(const Chunk::Pos &chunk_offset) const noexcept;
+ Ray Aim(const Chunk::Pos &chunk_offset) const noexcept;
+
+ void Update(int dt) noexcept;
+
+ void Draw() noexcept;
+
+private:
+ const Shape *shape;
+ Model model;
+
+ glm::vec3 velocity;
+ Block::Pos position;
+ Chunk::Pos chunk;
+
+ glm::quat angular_velocity;
+ glm::mat4 rotation;
+
+};
+
+}
+
+#endif
--- /dev/null
+#include "Generator.hpp"
+
+#include "Chunk.hpp"
+#include "../rand/OctaveNoise.hpp"
+
+#include <glm/glm.hpp>
+
+
+namespace blank {
+
+Generator::Generator(const Config &config) noexcept
+: solidNoise(config.solid_seed)
+, typeNoise(config.type_seed)
+, stretch(1.0f/config.stretch)
+, solid_threshold(config.solid_threshold)
+, space(0)
+, light(0)
+, solids() {
+
+}
+
+
+void Generator::operator ()(Chunk &chunk) const noexcept {
+ Chunk::Pos pos(chunk.Position());
+ glm::vec3 coords(pos * Chunk::Extent());
+ for (int z = 0; z < Chunk::depth; ++z) {
+ for (int y = 0; y < Chunk::height; ++y) {
+ for (int x = 0; x < Chunk::width; ++x) {
+ Block::Pos block_pos(x, y, z);
+ glm::vec3 gen_pos = (coords + block_pos) * stretch;
+ float val = OctaveNoise(solidNoise, coords + block_pos, 3, 0.5f, stretch, 2.0f);
+ if (val > solid_threshold) {
+ int type_val = int((typeNoise(gen_pos) + 1.0f) * solids.size()) % solids.size();
+ chunk.SetBlock(block_pos, Block(solids[type_val]));
+ } else {
+ chunk.SetBlock(block_pos, Block(space));
+ }
+ }
+ }
+ }
+ unsigned int random = 263167 * pos.x + 2097593 * pos.y + 426389 * pos.z;
+ for (int index = 0; index < Chunk::size; ++index) {
+ if (chunk.IsSurface(index)) {
+ random = random * 666649 + 7778777;
+ if ((random % 32) == 0) {
+ chunk.SetBlock(index, Block(light));
+ }
+ }
+ }
+ chunk.Invalidate();
+ chunk.CheckUpdate();
+}
+
+}
--- /dev/null
+#ifndef BLANK_WORLD_GENERATOR_HPP_
+#define BLANK_WORLD_GENERATOR_HPP_
+
+#include "Block.hpp"
+#include "../rand/SimplexNoise.hpp"
+#include "../rand/WorleyNoise.hpp"
+
+#include <vector>
+
+
+namespace blank {
+
+class Chunk;
+
+class Generator {
+
+public:
+ struct Config {
+ unsigned int solid_seed = 0;
+ unsigned int type_seed = 0;
+ float stretch = 64.0f;
+ float solid_threshold = 0.5f;
+ };
+
+ explicit Generator(const Config &) noexcept;
+
+ void operator ()(Chunk &) const noexcept;
+
+ void Space(Block::Type t) noexcept { space = t; }
+ void Light(Block::Type t) noexcept { light = t; }
+ void Solids(const std::vector<Block::Type> &s) { solids = s; }
+
+private:
+ SimplexNoise solidNoise;
+ WorleyNoise typeNoise;
+
+ float stretch;
+ float solid_threshold;
+
+ Block::Type space;
+ Block::Type light;
+ std::vector<Block::Type> solids;
+
+};
+
+}
+
+#endif
--- /dev/null
+#include "World.hpp"
+
+#include "../graphics/BlockLighting.hpp"
+#include "../graphics/DirectionalLighting.hpp"
+
+#include <limits>
+#include <glm/gtx/transform.hpp>
+
+
+namespace blank {
+
+World::World(const Config &config)
+: blockType()
+, blockShape({{ -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f }})
+, stairShape({{ -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f }}, { 0.0f, 0.0f })
+, slabShape({{ -0.5f, -0.5f, -0.5f }, { 0.5f, 0.0f, 0.5f }})
+, generate(config.gen)
+, chunks(config.load, blockType, generate)
+, player()
+, entities()
+, light_direction(config.light_direction)
+, fog_density(config.fog_density) {
+ BlockType::Faces block_fill = { true, true, true, true, true, true };
+ BlockType::Faces slab_fill = { false, true, false, false, false, false };
+ BlockType::Faces stair_fill = { false, true, false, false, false, true };
+
+ { // white block
+ BlockType type(true, { 1.0f, 1.0f, 1.0f }, &blockShape);
+ type.block_light = true;
+ type.fill = block_fill;
+ blockType.Add(type);
+ }
+ { // white slab
+ BlockType type(true, { 1.0f, 1.0f, 1.0f }, &slabShape);
+ type.block_light = true;
+ type.fill = slab_fill;
+ blockType.Add(type);
+ }
+ { // white stair
+ BlockType type(true, { 1.0f, 1.0f, 1.0f }, &stairShape);
+ type.block_light = true;
+ type.fill = stair_fill;
+ blockType.Add(type);
+ }
+
+ { // red block
+ BlockType type(true, { 1.0f, 0.0f, 0.0f }, &blockShape);
+ type.block_light = true;
+ type.fill = block_fill;
+ blockType.Add(type);
+ }
+ { // red slab
+ BlockType type(true, { 1.0f, 0.0f, 0.0f }, &slabShape);
+ type.block_light = true;
+ type.fill = slab_fill;
+ blockType.Add(type);
+ }
+ { // red stair
+ BlockType type(true, { 1.0f, 0.0f, 0.0f }, &stairShape);
+ type.block_light = true;
+ type.fill = stair_fill;
+ blockType.Add(type);
+ }
+
+ { // green block
+ BlockType type(true, { 0.0f, 1.0f, 0.0f }, &blockShape);
+ type.block_light = true;
+ type.fill = block_fill;
+ blockType.Add(type);
+ }
+ { // green slab
+ BlockType type(true, { 0.0f, 1.0f, 0.0f }, &slabShape);
+ type.block_light = true;
+ type.fill = slab_fill;
+ blockType.Add(type);
+ }
+ { // green stair
+ BlockType type(true, { 0.0f, 1.0f, 0.0f }, &stairShape);
+ type.block_light = true;
+ type.fill = stair_fill;
+ blockType.Add(type);
+ }
+
+ { // blue block
+ BlockType type(true, { 0.0f, 0.0f, 1.0f }, &blockShape);
+ type.block_light = true;
+ type.fill = block_fill;
+ blockType.Add(type);
+ }
+ { // blue slab
+ BlockType type(true, { 0.0f, 0.0f, 1.0f }, &slabShape);
+ type.block_light = true;
+ type.fill = slab_fill;
+ blockType.Add(type);
+ }
+ { // blue stair
+ BlockType type(true, { 0.0f, 0.0f, 1.0f }, &stairShape);
+ type.block_light = true;
+ type.fill = stair_fill;
+ blockType.Add(type);
+ }
+
+ { // glowing yellow block
+ BlockType type(true, { 1.0f, 1.0f, 0.0f }, &blockShape);
+ type.luminosity = 15;
+ type.block_light = true;
+ type.fill = block_fill;
+ blockType.Add(type);
+ }
+
+ generate.Space(0);
+ generate.Light(13);
+ generate.Solids({ 1, 4, 7, 10 });
+
+ player = &AddEntity();
+ player->Position(config.spawn);
+
+ chunks.GenerateSurrounding(player->ChunkCoords());
+}
+
+
+namespace {
+
+struct Candidate {
+ Chunk *chunk;
+ float dist;
+};
+
+std::vector<Candidate> candidates;
+
+}
+
+bool World::Intersection(
+ const Ray &ray,
+ const glm::mat4 &M,
+ Chunk **chunk,
+ int *blkid,
+ float *dist,
+ glm::vec3 *normal) {
+ candidates.clear();
+
+ for (Chunk &cur_chunk : chunks.Loaded()) {
+ float cur_dist;
+ if (cur_chunk.Intersection(ray, M * cur_chunk.Transform(player->ChunkCoords()), cur_dist)) {
+ candidates.push_back({ &cur_chunk, cur_dist });
+ }
+ }
+
+ if (candidates.empty()) return false;
+
+ Chunk *closest_chunk = nullptr;
+ float closest_dist = std::numeric_limits<float>::infinity();
+ int closest_blkid = -1;
+ glm::vec3 closest_normal;
+
+ for (Candidate &cand : candidates) {
+ if (cand.dist > closest_dist) continue;
+ int cur_blkid;
+ float cur_dist;
+ glm::vec3 cur_normal;
+ if (cand.chunk->Intersection(ray, M * cand.chunk->Transform(player->ChunkCoords()), cur_blkid, cur_dist, cur_normal)) {
+ if (cur_dist < closest_dist) {
+ closest_chunk = cand.chunk;
+ closest_blkid = cur_blkid;
+ closest_dist = cur_dist;
+ closest_normal = cur_normal;
+ }
+ }
+ }
+
+ if (chunk) {
+ *chunk = closest_chunk;
+ }
+ if (blkid) {
+ *blkid = closest_blkid;
+ }
+ if (dist) {
+ *dist = closest_dist;
+ }
+ if (normal) {
+ *normal = closest_normal;
+ }
+ return closest_chunk;
+}
+
+
+Chunk &World::PlayerChunk() {
+ return chunks.ForceLoad(player->ChunkCoords());
+}
+
+Chunk &World::Next(const Chunk &to, const glm::tvec3<int> &dir) {
+ const Chunk::Pos tgt_pos = to.Position() + dir;
+ return chunks.ForceLoad(tgt_pos);
+}
+
+
+void World::Update(int dt) {
+ for (Entity &entity : entities) {
+ entity.Update(dt);
+ }
+ chunks.Rebase(player->ChunkCoords());
+ chunks.Update();
+}
+
+
+void World::Render(BlockLighting &chunk_prog, DirectionalLighting &entity_prog) {
+ chunk_prog.Activate();
+ chunk_prog.SetFogDensity(fog_density);
+ chunk_prog.SetView(glm::inverse(player->Transform(player->ChunkCoords())));
+
+ for (Chunk &chunk : chunks.Loaded()) {
+ glm::mat4 m(chunk.Transform(player->ChunkCoords()));
+ chunk_prog.SetM(m);
+ glm::mat4 mvp(chunk_prog.GetVP() * m);
+ if (!CullTest(Chunk::Bounds(), mvp)) {
+ chunk.Draw();
+ }
+ }
+
+ entity_prog.Activate();
+ entity_prog.SetLightDirection(light_direction);
+ entity_prog.SetFogDensity(fog_density);
+ entity_prog.SetView(glm::inverse(player->Transform(player->ChunkCoords())));
+
+ for (Entity &entity : entities) {
+ if (entity.HasShape()) {
+ entity_prog.SetM(entity.Transform(player->ChunkCoords()));
+ entity.Draw();
+ }
+ }
+}
+
+}
--- /dev/null
+#ifndef BLANK_WORLD_WORLD_HPP_
+#define BLANK_WORLD_WORLD_HPP_
+
+#include "BlockTypeRegistry.hpp"
+#include "ChunkLoader.hpp"
+#include "Entity.hpp"
+#include "Generator.hpp"
+#include "../model/shapes.hpp"
+
+#include <list>
+#include <glm/glm.hpp>
+
+
+namespace blank {
+
+class BlockLighting;
+class DirectionalLighting;
+
+class World {
+
+public:
+ struct Config {
+ // initial player position
+ glm::vec3 spawn = { 4.0f, 4.0f, 4.0f };
+ // direction facing towards(!) the light
+ glm::vec3 light_direction = { -1.0f, -3.0f, -2.0f };
+ // fade out reaches 1/e (0.3679) at 1/fog_density,
+ // gets less than 0.01 at e/(2 * fog_density)
+ // I chose 0.011 because it yields 91 and 124 for those, so
+ // slightly less than 6 and 8 chunks
+ float fog_density = 0.011f;
+
+ Generator::Config gen = Generator::Config();
+ ChunkLoader::Config load = ChunkLoader::Config();
+ };
+
+ explicit World(const Config &);
+
+ bool Intersection(
+ const Ray &,
+ const glm::mat4 &M,
+ Chunk **chunk = nullptr,
+ int *blkid = nullptr,
+ float *dist = nullptr,
+ glm::vec3 *normal = nullptr);
+
+ BlockTypeRegistry &BlockTypes() { return blockType; }
+
+ Entity &Player() { return *player; }
+ Entity &AddEntity() { entities.emplace_back(); return entities.back(); }
+
+ Chunk &PlayerChunk();
+ Chunk &Next(const Chunk &to, const glm::tvec3<int> &dir);
+
+ void Update(int dt);
+
+ void Render(BlockLighting &, DirectionalLighting &);
+
+private:
+ BlockTypeRegistry blockType;
+ CuboidShape blockShape;
+ StairShape stairShape;
+ CuboidShape slabShape;
+
+ Generator generate;
+ ChunkLoader chunks;
+
+ Entity *player;
+ std::list<Entity> entities;
+
+ glm::vec3 light_direction;
+ float fog_density;
+
+};
+
+}
+
+#endif
--- /dev/null
+#include "Block.hpp"
+#include "BlockType.hpp"
+#include "BlockTypeRegistry.hpp"
+
+#include "../model/geometry.hpp"
+
+#include <glm/gtx/euler_angles.hpp>
+#include <glm/gtx/transform.hpp>
+
+
+namespace blank {
+
+const NullShape BlockType::DEFAULT_SHAPE;
+
+BlockType::BlockType(bool v, const glm::vec3 &col, const Shape *s) noexcept
+: shape(s)
+, color(col)
+, outline_color(-1, -1, -1)
+, id(0)
+, luminosity(0)
+, visible(v)
+, block_light(false)
+, fill({ false, false, false, false, false, false }) {
+
+}
+
+void BlockType::FillModel(
+ Model::Buffer &buf,
+ const glm::mat4 &transform,
+ Model::Index idx_offset
+) const noexcept {
+ shape->Vertices(buf.vertices, buf.normals, buf.indices, transform, idx_offset);
+ buf.colors.insert(buf.colors.end(), shape->VertexCount(), color);
+}
+
+void BlockType::FillBlockModel(
+ BlockModel::Buffer &buf,
+ const glm::mat4 &transform,
+ BlockModel::Index idx_offset
+) const noexcept {
+ shape->Vertices(buf.vertices, buf.indices, transform, idx_offset);
+ buf.colors.insert(buf.colors.end(), shape->VertexCount(), color);
+}
+
+void BlockType::FillOutlineModel(
+ OutlineModel &model,
+ const glm::vec3 &pos_offset,
+ OutlineModel::Index idx_offset
+) const noexcept {
+ shape->Outline(model.vertices, model.indices, pos_offset, idx_offset);
+ model.colors.insert(model.colors.end(), shape->OutlineCount(), outline_color);
+}
+
+
+BlockTypeRegistry::BlockTypeRegistry() {
+ Add(BlockType());
+}
+
+Block::Type BlockTypeRegistry::Add(const BlockType &t) {
+ int id = types.size();
+ types.push_back(t);
+ types.back().id = id;
+ return id;
+}
+
+
+const glm::mat4 Block::orient2transform[ORIENT_COUNT] = {
+ { 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, }, // face: up, turn: none
+ { 0, 0, -1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, }, // face: up, turn: left
+ { -1, 0, 0, 0, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, }, // face: up, turn: around
+ { 0, 0, 1, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 0, 1, }, // face: up, turn: right
+ { 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, }, // face: down, turn: none
+ { 0, 0, -1, 0, 0, -1, 0, 0, -1, 0, 0, 0, 0, 0, 0, 1, }, // face: down, turn: left
+ { -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, }, // face: down, turn: around
+ { 0, 0, 1, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, }, // face: down, turn: right
+ { 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, }, // face: right, turn: none
+ { 0, -1, 0, 0, 0, 0, -1, 0, 1, 0, 0, 0, 0, 0, 0, 1, }, // face: right, turn: left
+ { 0, -1, 0, 0, -1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, }, // face: right, turn: around
+ { 0, -1, 0, 0, 0, 0, 1, 0, -1, 0, 0, 0, 0, 0, 0, 1, }, // face: right, turn: right
+ { 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, }, // face: left, turn: none
+ { 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, }, // face: left, turn: left
+ { 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 1, }, // face: left, turn: around
+ { 0, 1, 0, 0, 0, 0, -1, 0, -1, 0, 0, 0, 0, 0, 0, 1, }, // face: left, turn: right
+ { 1, 0, 0, 0, 0, 0, 1, 0, 0, -1, 0, 0, 0, 0, 0, 1, }, // face: front, turn: none
+ { 0, 0, -1, 0, 1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 1, }, // face: front, turn: left
+ { -1, 0, 0, 0, 0, 0, -1, 0, 0, -1, 0, 0, 0, 0, 0, 1, }, // face: front, turn: around
+ { 0, 0, 1, 0, -1, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 1, }, // face: front, turn: right
+ { 1, 0, 0, 0, 0, 0, -1, 0, 0, 1, 0, 0, 0, 0, 0, 1, }, // face: back, turn: none
+ { 0, 0, -1, 0, -1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, }, // face: back, turn: left
+ { -1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, }, // face: back, turn: around
+ { 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, }, // face: back, turn: right
+};
+
+const glm::tvec3<int> Block::face2normal[FACE_COUNT] = {
+ { 0, 1, 0 },
+ { 0, -1, 0 },
+ { 1, 0, 0 },
+ { -1, 0, 0 },
+ { 0, 0, 1 },
+ { 0, 0, -1 },
+};
+
+const Block::Face Block::orient2face[ORIENT_COUNT][FACE_COUNT] = {
+ { FACE_UP, FACE_DOWN, FACE_RIGHT, FACE_LEFT, FACE_FRONT, FACE_BACK, }, // face: up, turn: none
+ { FACE_UP, FACE_DOWN, FACE_FRONT, FACE_BACK, FACE_LEFT, FACE_RIGHT, }, // face: up, turn: left
+ { FACE_UP, FACE_DOWN, FACE_LEFT, FACE_RIGHT, FACE_BACK, FACE_FRONT, }, // face: up, turn: around
+ { FACE_UP, FACE_DOWN, FACE_BACK, FACE_FRONT, FACE_RIGHT, FACE_LEFT, }, // face: up, turn: right
+ { FACE_DOWN, FACE_UP, FACE_RIGHT, FACE_LEFT, FACE_BACK, FACE_FRONT, }, // face: down, turn: none
+ { FACE_DOWN, FACE_UP, FACE_BACK, FACE_FRONT, FACE_LEFT, FACE_RIGHT, }, // face: down, turn: left
+ { FACE_DOWN, FACE_UP, FACE_LEFT, FACE_RIGHT, FACE_FRONT, FACE_BACK, }, // face: down, turn: around
+ { FACE_DOWN, FACE_UP, FACE_FRONT, FACE_BACK, FACE_RIGHT, FACE_LEFT, }, // face: down, turn: right
+ { FACE_LEFT, FACE_RIGHT, FACE_UP, FACE_DOWN, FACE_FRONT, FACE_BACK, }, // face: right, turn: none
+ { FACE_LEFT, FACE_RIGHT, FACE_FRONT, FACE_BACK, FACE_DOWN, FACE_UP, }, // face: right, turn: left
+ { FACE_LEFT, FACE_RIGHT, FACE_DOWN, FACE_UP, FACE_BACK, FACE_FRONT, }, // face: right, turn: around
+ { FACE_LEFT, FACE_RIGHT, FACE_BACK, FACE_FRONT, FACE_UP, FACE_DOWN, }, // face: right, turn: right
+ { FACE_RIGHT, FACE_LEFT, FACE_DOWN, FACE_UP, FACE_FRONT, FACE_BACK, }, // face: left, turn: none
+ { FACE_RIGHT, FACE_LEFT, FACE_FRONT, FACE_BACK, FACE_UP, FACE_DOWN, }, // face: left, turn: left
+ { FACE_RIGHT, FACE_LEFT, FACE_UP, FACE_DOWN, FACE_BACK, FACE_FRONT, }, // face: left, turn: around
+ { FACE_RIGHT, FACE_LEFT, FACE_BACK, FACE_FRONT, FACE_DOWN, FACE_UP, }, // face: left, turn: right
+ { FACE_BACK, FACE_FRONT, FACE_RIGHT, FACE_LEFT, FACE_UP, FACE_DOWN, }, // face: front, turn: none
+ { FACE_BACK, FACE_FRONT, FACE_UP, FACE_DOWN, FACE_LEFT, FACE_RIGHT, }, // face: front, turn: left
+ { FACE_BACK, FACE_FRONT, FACE_LEFT, FACE_RIGHT, FACE_DOWN, FACE_UP, }, // face: front, turn: around
+ { FACE_BACK, FACE_FRONT, FACE_DOWN, FACE_UP, FACE_RIGHT, FACE_LEFT, }, // face: front, turn: right
+ { FACE_FRONT, FACE_BACK, FACE_RIGHT, FACE_LEFT, FACE_DOWN, FACE_UP, }, // face: back, turn: none
+ { FACE_FRONT, FACE_BACK, FACE_DOWN, FACE_UP, FACE_LEFT, FACE_RIGHT, }, // face: back, turn: left
+ { FACE_FRONT, FACE_BACK, FACE_LEFT, FACE_RIGHT, FACE_UP, FACE_DOWN, }, // face: back, turn: around
+ { FACE_FRONT, FACE_BACK, FACE_UP, FACE_DOWN, FACE_RIGHT, FACE_LEFT, }, // face: back, turn: right
+};
+
+}
--- /dev/null
+#include "BlockLookup.hpp"
+#include "Chunk.hpp"
+#include "ChunkLoader.hpp"
+
+#include "Generator.hpp"
+
+#include <algorithm>
+#include <limits>
+#include <queue>
+
+
+namespace blank {
+
+constexpr int Chunk::width;
+constexpr int Chunk::height;
+constexpr int Chunk::depth;
+constexpr int Chunk::size;
+
+
+Chunk::Chunk(const BlockTypeRegistry &types) noexcept
+: types(&types)
+, neighbor{0}
+, blocks{}
+, light{0}
+, model()
+, position(0, 0, 0)
+, dirty(false) {
+
+}
+
+Chunk::Chunk(Chunk &&other) noexcept
+: types(other.types)
+, model(std::move(other.model))
+, position(other.position)
+, dirty(other.dirty) {
+ std::copy(other.neighbor, other.neighbor + sizeof(neighbor), neighbor);
+ std::copy(other.blocks, other.blocks + sizeof(blocks), blocks);
+ std::copy(other.light, other.light + sizeof(light), light);
+}
+
+Chunk &Chunk::operator =(Chunk &&other) noexcept {
+ types = other.types;
+ std::copy(other.neighbor, other.neighbor + sizeof(neighbor), neighbor);
+ std::copy(other.blocks, other.blocks + sizeof(blocks), blocks);
+ std::copy(other.light, other.light + sizeof(light), light);
+ model = std::move(other.model);
+ position = other.position;
+ dirty = other.dirty;
+ return *this;
+}
+
+
+namespace {
+
+struct SetNode {
+
+ Chunk *chunk;
+ Chunk::Pos pos;
+
+ SetNode(Chunk *chunk, Chunk::Pos pos)
+ : chunk(chunk), pos(pos) { }
+
+ int Get() const noexcept { return chunk->GetLight(pos); }
+ void Set(int level) noexcept { chunk->SetLight(pos, level); }
+
+ bool HasNext(Block::Face face) noexcept {
+ const BlockLookup next(chunk, pos, face);
+ return next && !next.GetType().block_light;
+ }
+ SetNode GetNext(Block::Face face) noexcept {
+ const BlockLookup next(chunk, pos, face);
+ return SetNode(&next.GetChunk(), next.GetBlockPos());
+ }
+
+};
+
+struct UnsetNode
+: public SetNode {
+
+ int level;
+
+ UnsetNode(Chunk *chunk, Chunk::Pos pos)
+ : SetNode(chunk, pos), level(Get()) { }
+
+ UnsetNode(const SetNode &set)
+ : SetNode(set), level(Get()) { }
+
+
+ bool HasNext(Block::Face face) noexcept {
+ const BlockLookup next(chunk, pos, face);
+ return next;
+ }
+ UnsetNode GetNext(Block::Face face) noexcept { return UnsetNode(SetNode::GetNext(face)); }
+
+};
+
+std::queue<SetNode> light_queue;
+std::queue<UnsetNode> dark_queue;
+
+void work_light() noexcept {
+ while (!light_queue.empty()) {
+ SetNode node = light_queue.front();
+ light_queue.pop();
+
+ int level = node.Get() - 1;
+ for (int face = 0; face < Block::FACE_COUNT; ++face) {
+ if (node.HasNext(Block::Face(face))) {
+ SetNode other = node.GetNext(Block::Face(face));
+ if (other.Get() < level) {
+ other.Set(level);
+ light_queue.emplace(other);
+ }
+ }
+ }
+ }
+}
+
+void work_dark() noexcept {
+ while (!dark_queue.empty()) {
+ UnsetNode node = dark_queue.front();
+ dark_queue.pop();
+
+ for (int face = 0; face < Block::FACE_COUNT; ++face) {
+ if (node.HasNext(Block::Face(face))) {
+ UnsetNode other = node.GetNext(Block::Face(face));
+ // TODO: if there a light source here with the same level this will err
+ if (other.Get() != 0 && other.Get() < node.level) {
+ other.Set(0);
+ dark_queue.emplace(other);
+ } else {
+ light_queue.emplace(other);
+ }
+ }
+ }
+ }
+}
+
+}
+
+void Chunk::SetBlock(int index, const Block &block) noexcept {
+ const BlockType &old_type = Type(blocks[index]);
+ const BlockType &new_type = Type(block);
+
+ blocks[index] = block;
+
+ if (&old_type == &new_type) return;
+
+ if (new_type.luminosity > old_type.luminosity) {
+ // light added
+ SetLight(index, new_type.luminosity);
+ light_queue.emplace(this, ToPos(index));
+ work_light();
+ } else if (new_type.luminosity < old_type.luminosity) {
+ // light removed
+ dark_queue.emplace(this, ToPos(index));
+ SetLight(index, 0);
+ work_dark();
+ SetLight(index, new_type.luminosity);
+ light_queue.emplace(this, ToPos(index));
+ work_light();
+ } else if (new_type.block_light && !old_type.block_light) {
+ // obstacle added
+ if (GetLight(index) > 0) {
+ dark_queue.emplace(this, ToPos(index));
+ SetLight(index, 0);
+ work_dark();
+ work_light();
+ }
+ } else if (!new_type.block_light && old_type.block_light) {
+ // obstacle removed
+ int level = 0;
+ for (int face = 0; face < Block::FACE_COUNT; ++face) {
+ BlockLookup next_block(this, ToPos(index), Block::Face(face));
+ if (next_block) {
+ level = std::min(level, next_block.GetLight());
+ }
+ }
+ if (level > 1) {
+ SetLight(index, level - 1);
+ light_queue.emplace(this, ToPos(index));
+ work_light();
+ }
+ }
+}
+
+void Chunk::SetNeighbor(Chunk &other) noexcept {
+ if (other.position == position + Pos(-1, 0, 0)) {
+ if (neighbor[Block::FACE_LEFT] != &other) {
+ neighbor[Block::FACE_LEFT] = &other;
+ other.neighbor[Block::FACE_RIGHT] = this;
+ for (int z = 0; z < depth; ++z) {
+ for (int y = 0; y < height; ++y) {
+ Pos my_pos(0, y, z);
+ Pos other_pos(width - 1, y, z);
+ if (GetLight(my_pos) > 0) {
+ light_queue.emplace(this, my_pos);
+ }
+ if (other.GetLight(other_pos) > 0) {
+ light_queue.emplace(&other, other_pos);
+ }
+ }
+ }
+ work_light();
+ }
+ } else if (other.position == position + Pos(1, 0, 0)) {
+ if (neighbor[Block::FACE_RIGHT] != &other) {
+ neighbor[Block::FACE_RIGHT] = &other;
+ other.neighbor[Block::FACE_LEFT] = this;
+ for (int z = 0; z < depth; ++z) {
+ for (int y = 0; y < height; ++y) {
+ Pos my_pos(width - 1, y, z);
+ Pos other_pos(0, y, z);
+ if (GetLight(my_pos) > 0) {
+ light_queue.emplace(this, my_pos);
+ }
+ if (other.GetLight(other_pos) > 0) {
+ light_queue.emplace(&other, other_pos);
+ }
+ }
+ }
+ work_light();
+ }
+ } else if (other.position == position + Pos(0, -1, 0)) {
+ if (neighbor[Block::FACE_DOWN] != &other) {
+ neighbor[Block::FACE_DOWN] = &other;
+ other.neighbor[Block::FACE_UP] = this;
+ for (int z = 0; z < depth; ++z) {
+ for (int x = 0; x < width; ++x) {
+ Pos my_pos(x, 0, z);
+ Pos other_pos(x, height - 1, z);
+ if (GetLight(my_pos) > 0) {
+ light_queue.emplace(this, my_pos);
+ }
+ if (other.GetLight(other_pos) > 0) {
+ light_queue.emplace(&other, other_pos);
+ }
+ }
+ }
+ work_light();
+ }
+ } else if (other.position == position + Pos(0, 1, 0)) {
+ if (neighbor[Block::FACE_UP] != &other) {
+ neighbor[Block::FACE_UP] = &other;
+ other.neighbor[Block::FACE_DOWN] = this;
+ for (int z = 0; z < depth; ++z) {
+ for (int x = 0; x < width; ++x) {
+ Pos my_pos(x, height - 1, z);
+ Pos other_pos(x, 0, z);
+ if (GetLight(my_pos) > 0) {
+ light_queue.emplace(this, my_pos);
+ }
+ if (other.GetLight(other_pos) > 0) {
+ light_queue.emplace(&other, other_pos);
+ }
+ }
+ }
+ work_light();
+ }
+ } else if (other.position == position + Pos(0, 0, -1)) {
+ if (neighbor[Block::FACE_BACK] != &other) {
+ neighbor[Block::FACE_BACK] = &other;
+ other.neighbor[Block::FACE_FRONT] = this;
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ Pos my_pos(x, y, 0);
+ Pos other_pos(x, y, depth - 1);
+ if (GetLight(my_pos) > 0) {
+ light_queue.emplace(this, my_pos);
+ }
+ if (other.GetLight(other_pos) > 0) {
+ light_queue.emplace(&other, other_pos);
+ }
+ }
+ }
+ work_light();
+ }
+ } else if (other.position == position + Pos(0, 0, 1)) {
+ if (neighbor[Block::FACE_FRONT] != &other) {
+ neighbor[Block::FACE_FRONT] = &other;
+ other.neighbor[Block::FACE_BACK] = this;
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x) {
+ Pos my_pos(x, y, depth - 1);
+ Pos other_pos(x, y, 0);
+ if (GetLight(my_pos) > 0) {
+ light_queue.emplace(this, my_pos);
+ }
+ if (other.GetLight(other_pos) > 0) {
+ light_queue.emplace(&other, other_pos);
+ }
+ }
+ }
+ work_light();
+ }
+ }
+}
+
+void Chunk::ClearNeighbors() noexcept {
+ for (int i = 0; i < Block::FACE_COUNT; ++i) {
+ neighbor[i] = nullptr;
+ }
+}
+
+void Chunk::Unlink() noexcept {
+ for (int face = 0; face < Block::FACE_COUNT; ++face) {
+ if (neighbor[face]) {
+ neighbor[face]->neighbor[Block::Opposite(Block::Face(face))] = nullptr;
+ }
+ }
+}
+
+void Chunk::Relink() noexcept {
+ for (int face = 0; face < Block::FACE_COUNT; ++face) {
+ if (neighbor[face]) {
+ neighbor[face]->neighbor[Block::Opposite(Block::Face(face))] = this;
+ }
+ }
+}
+
+
+void Chunk::SetLight(int index, int level) noexcept {
+ if (light[index] != level) {
+ light[index] = level;
+ Invalidate();
+ }
+}
+
+int Chunk::GetLight(int index) const noexcept {
+ return light[index];
+}
+
+float Chunk::GetVertexLight(const Pos &pos, const BlockModel::Position &vtx, const Model::Normal &norm) const noexcept {
+ int index = ToIndex(pos);
+ float light = GetLight(index);
+
+ Block::Face direct_face(Block::NormalFace(norm));
+ // tis okay
+ BlockLookup direct(const_cast<Chunk *>(this), pos, Block::NormalFace(norm));
+ if (direct) {
+ float direct_light = direct.GetLight();
+ if (direct_light > light) {
+ light = direct_light;
+ }
+ } else {
+ return light;
+ }
+
+ if (Type(BlockAt(index)).luminosity > 0 || direct.GetType().block_light) {
+ return light;
+ }
+
+ Block::Face edge[2];
+ switch (Block::Axis(direct_face)) {
+ case 0: // X
+ edge[0] = (vtx.y - pos.y) > 0.5f ? Block::FACE_UP : Block::FACE_DOWN;
+ edge[1] = (vtx.z - pos.z) > 0.5f ? Block::FACE_FRONT : Block::FACE_BACK;
+ break;
+ case 1: // Y
+ edge[0] = (vtx.z - pos.z) > 0.5f ? Block::FACE_FRONT : Block::FACE_BACK;
+ edge[1] = (vtx.x - pos.x) > 0.5f ? Block::FACE_RIGHT : Block::FACE_LEFT;
+ break;
+ case 2: // Z
+ edge[0] = (vtx.x - pos.x) > 0.5f ? Block::FACE_RIGHT : Block::FACE_LEFT;
+ edge[1] = (vtx.y - pos.y) > 0.5f ? Block::FACE_UP : Block::FACE_DOWN;
+ break;
+ }
+
+ int num = 1;
+ int occlusion = 0;
+
+ BlockLookup next[2] = {
+ direct.Next(edge[0]),
+ direct.Next(edge[1]),
+ };
+
+ if (next[0]) {
+ if (next[0].GetType().block_light) {
+ ++occlusion;
+ } else {
+ light += next[0].GetLight();
+ ++num;
+ }
+ }
+ if (next[1]) {
+ if (next[1].GetType().block_light) {
+ ++occlusion;
+ } else {
+ light += next[1].GetLight();
+ ++num;
+ }
+ }
+ if (occlusion < 2) {
+ if (next[0]) {
+ BlockLookup corner = next[0].Next(edge[1]);
+ if (corner) {
+ if (corner.GetType().block_light) {
+ ++occlusion;
+ } else {
+ light += corner.GetLight();
+ ++num;
+ }
+ }
+ } else if (next[1]) {
+ BlockLookup corner = next[1].Next(edge[0]);
+ if (corner) {
+ if (corner.GetType().block_light) {
+ ++occlusion;
+ } else {
+ light += corner.GetLight();
+ ++num;
+ }
+ }
+ }
+ } else {
+ ++occlusion;
+ }
+
+ return (light / num) - (occlusion * 0.8f);
+}
+
+
+bool Chunk::IsSurface(const Pos &pos) const noexcept {
+ const Block &block = BlockAt(pos);
+ if (!Type(block).visible) {
+ return false;
+ }
+ for (int face = 0; face < Block::FACE_COUNT; ++face) {
+ BlockLookup next = BlockLookup(const_cast<Chunk *>(this), pos, Block::Face(face));
+ if (!next || !next.GetType().visible) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+void Chunk::Draw() noexcept {
+ if (dirty) {
+ Update();
+ }
+ model.Draw();
+}
+
+
+bool Chunk::Intersection(
+ const Ray &ray,
+ const glm::mat4 &M,
+ int &blkid,
+ float &dist,
+ glm::vec3 &normal
+) const noexcept {
+ // TODO: should be possible to heavily optimize this
+ int idx = 0;
+ blkid = -1;
+ dist = std::numeric_limits<float>::infinity();
+ for (int z = 0; z < depth; ++z) {
+ for (int y = 0; y < height; ++y) {
+ for (int x = 0; x < width; ++x, ++idx) {
+ const BlockType &type = Type(idx);
+ if (!type.visible) {
+ continue;
+ }
+ float cur_dist;
+ glm::vec3 cur_norm;
+ if (type.shape->Intersects(ray, M * ToTransform(Pos(x, y, z), idx), cur_dist, cur_norm)) {
+ if (cur_dist < dist) {
+ blkid = idx;
+ dist = cur_dist;
+ normal = cur_norm;
+ }
+ }
+ }
+ }
+ }
+
+ if (blkid < 0) {
+ return false;
+ } else {
+ normal = glm::vec3(BlockAt(blkid).Transform() * glm::vec4(normal, 0.0f));
+ return true;
+ }
+}
+
+
+namespace {
+
+BlockModel::Buffer buf;
+
+}
+
+void Chunk::CheckUpdate() noexcept {
+ if (dirty) {
+ Update();
+ }
+}
+
+void Chunk::Update() noexcept {
+ int vtx_count = 0, idx_count = 0;
+ for (const auto &block : blocks) {
+ const Shape *shape = Type(block).shape;
+ vtx_count += shape->VertexCount();
+ idx_count += shape->VertexIndexCount();
+ }
+ buf.Clear();
+ buf.Reserve(vtx_count, idx_count);
+
+ int idx = 0;
+ BlockModel::Index vtx_counter = 0;
+ for (size_t z = 0; z < depth; ++z) {
+ for (size_t y = 0; y < height; ++y) {
+ for (size_t x = 0; x < width; ++x, ++idx) {
+ const BlockType &type = Type(BlockAt(idx));
+ const Pos pos(x, y, z);
+
+ if (!type.visible || Obstructed(pos).All()) continue;
+
+ type.FillBlockModel(buf, ToTransform(pos, idx), vtx_counter);
+ size_t vtx_begin = vtx_counter;
+ vtx_counter += type.shape->VertexCount();
+
+ for (size_t vtx = vtx_begin; vtx < vtx_counter; ++vtx) {
+ buf.lights.emplace_back(GetVertexLight(
+ pos,
+ buf.vertices[vtx],
+ type.shape->VertexNormal(vtx - vtx_begin, BlockAt(idx).Transform())
+ ));
+ }
+ }
+ }
+ }
+
+ model.Update(buf);
+ dirty = false;
+}
+
+Block::FaceSet Chunk::Obstructed(const Pos &pos) const noexcept {
+ Block::FaceSet result;
+
+ for (int f = 0; f < Block::FACE_COUNT; ++f) {
+ Block::Face face = Block::Face(f);
+ BlockLookup next(const_cast<Chunk *>(this), pos, face);
+ if (next && next.GetType().FaceFilled(next.GetBlock(), Block::Opposite(face))) {
+ result.Set(face);
+ }
+ }
+
+ return result;
+}
+
+glm::mat4 Chunk::ToTransform(const Pos &pos, int idx) const noexcept {
+ return glm::translate(ToCoords(pos)) * BlockAt(idx).Transform();
+}
+
+
+BlockLookup::BlockLookup(Chunk *c, const Chunk::Pos &p) noexcept
+: chunk(c), pos(p) {
+ while (pos.x >= Chunk::width) {
+ if (chunk->HasNeighbor(Block::FACE_RIGHT)) {
+ chunk = &chunk->GetNeighbor(Block::FACE_RIGHT);
+ pos.x -= Chunk::width;
+ } else {
+ chunk = nullptr;
+ return;
+ }
+ }
+ while (pos.x < 0) {
+ if (chunk->HasNeighbor(Block::FACE_LEFT)) {
+ chunk = &chunk->GetNeighbor(Block::FACE_LEFT);
+ pos.x += Chunk::width;
+ } else {
+ chunk = nullptr;
+ return;
+ }
+ }
+ while (pos.y >= Chunk::height) {
+ if (chunk->HasNeighbor(Block::FACE_UP)) {
+ chunk = &chunk->GetNeighbor(Block::FACE_UP);
+ pos.y -= Chunk::height;
+ } else {
+ chunk = nullptr;
+ return;
+ }
+ }
+ while (pos.y < 0) {
+ if (chunk->HasNeighbor(Block::FACE_DOWN)) {
+ chunk = &chunk->GetNeighbor(Block::FACE_DOWN);
+ pos.y += Chunk::height;
+ } else {
+ chunk = nullptr;
+ return;
+ }
+ }
+ while (pos.z >= Chunk::depth) {
+ if (chunk->HasNeighbor(Block::FACE_FRONT)) {
+ chunk = &chunk->GetNeighbor(Block::FACE_FRONT);
+ pos.z -= Chunk::depth;
+ } else {
+ chunk = nullptr;
+ return;
+ }
+ }
+ while (pos.z < 0) {
+ if (chunk->HasNeighbor(Block::FACE_BACK)) {
+ chunk = &chunk->GetNeighbor(Block::FACE_BACK);
+ pos.z += Chunk::depth;
+ } else {
+ chunk = nullptr;
+ return;
+ }
+ }
+}
+
+BlockLookup::BlockLookup(Chunk *c, const Chunk::Pos &p, Block::Face face) noexcept
+: chunk(c), pos(p) {
+ pos += Block::FaceNormal(face);
+ if (!Chunk::InBounds(pos)) {
+ pos -= Block::FaceNormal(face) * Chunk::Extent();
+ chunk = &chunk->GetNeighbor(face);
+ }
+}
+
+
+ChunkLoader::ChunkLoader(const Config &config, const BlockTypeRegistry ®, const Generator &gen) noexcept
+: base(0, 0, 0)
+, reg(reg)
+, gen(gen)
+, loaded()
+, to_generate()
+, to_free()
+, load_dist(config.load_dist)
+, unload_dist(config.unload_dist) {
+
+}
+
+namespace {
+
+struct ChunkLess {
+
+ explicit ChunkLess(const Chunk::Pos &base) noexcept
+ : base(base) { }
+
+ bool operator ()(const Chunk::Pos &a, const Chunk::Pos &b) const noexcept {
+ Chunk::Pos da(base - a);
+ Chunk::Pos db(base - b);
+ return
+ da.x * da.x + da.y * da.y + da.z * da.z <
+ db.x * db.x + db.y * db.y + db.z * db.z;
+ }
+
+ Chunk::Pos base;
+
+};
+
+}
+
+void ChunkLoader::Generate(const Chunk::Pos &from, const Chunk::Pos &to) {
+ for (int z = from.z; z < to.z; ++z) {
+ for (int y = from.y; y < to.y; ++y) {
+ for (int x = from.x; x < to.x; ++x) {
+ Chunk::Pos pos(x, y, z);
+ if (Known(pos)) {
+ continue;
+ } else if (pos == base) {
+ Generate(pos);
+
+ // light testing
+ // for (int i = 0; i < 16; ++i) {
+ // for (int j = 0; j < 16; ++j) {
+ // loaded.back().SetBlock(Chunk::Pos{ i, j, 0 }, Block(1));
+ // loaded.back().SetBlock(Chunk::Pos{ i, j, 15 }, Block(1));
+ // loaded.back().SetBlock(Chunk::Pos{ 0, j, i }, Block(1));
+ // loaded.back().SetBlock(Chunk::Pos{ 15, j, i }, Block(1));
+ // }
+ // }
+ // loaded.back().SetBlock(Chunk::Pos{ 1, 0, 1 }, Block(13));
+ // loaded.back().SetBlock(Chunk::Pos{ 14, 0, 1 }, Block(13));
+ // loaded.back().SetBlock(Chunk::Pos{ 1, 0, 14 }, Block(13));
+ // loaded.back().SetBlock(Chunk::Pos{ 14, 0, 14 }, Block(13));
+ // loaded.back().SetBlock(Chunk::Pos{ 1, 15, 1 }, Block(13));
+ // loaded.back().SetBlock(Chunk::Pos{ 14, 15, 1 }, Block(13));
+ // loaded.back().SetBlock(Chunk::Pos{ 1, 15, 14 }, Block(13));
+ // loaded.back().SetBlock(Chunk::Pos{ 14, 15, 14 }, Block(13));
+ // loaded.back().SetBlock(Chunk::Pos{ 7, 7, 0 }, Block(13));
+ // loaded.back().SetBlock(Chunk::Pos{ 8, 7, 0 }, Block(13));
+ // loaded.back().SetBlock(Chunk::Pos{ 7, 8, 0 }, Block(13));
+ // loaded.back().SetBlock(Chunk::Pos{ 8, 8, 0 }, Block(13));
+ // loaded.back().SetBlock(Chunk::Pos{ 7, 7, 15 }, Block(13));
+ // loaded.back().SetBlock(Chunk::Pos{ 8, 7, 15 }, Block(13));
+ // loaded.back().SetBlock(Chunk::Pos{ 7, 8, 15 }, Block(13));
+ // loaded.back().SetBlock(Chunk::Pos{ 8, 8, 15 }, Block(13));
+ // loaded.back().SetBlock(Chunk::Pos{ 0, 7, 7 }, Block(13));
+ // loaded.back().SetBlock(Chunk::Pos{ 0, 7, 8 }, Block(13));
+ // loaded.back().SetBlock(Chunk::Pos{ 0, 8, 7 }, Block(13));
+ // loaded.back().SetBlock(Chunk::Pos{ 0, 8, 8 }, Block(13));
+ // loaded.back().SetBlock(Chunk::Pos{ 15, 7, 7 }, Block(13));
+ // loaded.back().SetBlock(Chunk::Pos{ 15, 7, 8 }, Block(13));
+ // loaded.back().SetBlock(Chunk::Pos{ 15, 8, 7 }, Block(13));
+ // loaded.back().SetBlock(Chunk::Pos{ 15, 8, 8 }, Block(13));
+ // loaded.back().Invalidate();
+ // loaded.back().CheckUpdate();
+
+ // orientation testing
+ // for (int i = 0; i < Block::FACE_COUNT; ++i) {
+ // for (int j = 0; j < Block::TURN_COUNT; ++j) {
+ // loaded.back().BlockAt(512 * j + 2 * i) = Block(3 * (j + 1), Block::Face(i), Block::Turn(j));
+ // }
+ // }
+ // loaded.back().Invalidate();
+ // loaded.back().CheckUpdate();
+ } else {
+ to_generate.emplace_back(pos);
+ }
+ }
+ }
+ }
+ to_generate.sort(ChunkLess(base));
+}
+
+Chunk &ChunkLoader::Generate(const Chunk::Pos &pos) {
+ loaded.emplace_back(reg);
+ Chunk &chunk = loaded.back();
+ chunk.Position(pos);
+ gen(chunk);
+ Insert(chunk);
+ return chunk;
+}
+
+void ChunkLoader::Insert(Chunk &chunk) noexcept {
+ for (Chunk &other : loaded) {
+ chunk.SetNeighbor(other);
+ }
+}
+
+void ChunkLoader::Remove(Chunk &chunk) noexcept {
+ chunk.Unlink();
+}
+
+Chunk *ChunkLoader::Loaded(const Chunk::Pos &pos) noexcept {
+ for (Chunk &chunk : loaded) {
+ if (chunk.Position() == pos) {
+ return &chunk;
+ }
+ }
+ return nullptr;
+}
+
+bool ChunkLoader::Queued(const Chunk::Pos &pos) noexcept {
+ for (const Chunk::Pos &chunk : to_generate) {
+ if (chunk == pos) {
+ return true;
+ }
+ }
+ return nullptr;
+}
+
+bool ChunkLoader::Known(const Chunk::Pos &pos) noexcept {
+ if (Loaded(pos)) return true;
+ return Queued(pos);
+}
+
+Chunk &ChunkLoader::ForceLoad(const Chunk::Pos &pos) {
+ Chunk *chunk = Loaded(pos);
+ if (chunk) {
+ return *chunk;
+ }
+
+ for (auto iter(to_generate.begin()), end(to_generate.end()); iter != end; ++iter) {
+ if (*iter == pos) {
+ to_generate.erase(iter);
+ break;
+ }
+ }
+
+ return Generate(pos);
+}
+
+void ChunkLoader::Rebase(const Chunk::Pos &new_base) {
+ if (new_base == base) {
+ return;
+ }
+ base = new_base;
+
+ // unload far away chunks
+ for (auto iter(loaded.begin()), end(loaded.end()); iter != end;) {
+ if (std::abs(base.x - iter->Position().x) > unload_dist
+ || std::abs(base.y - iter->Position().y) > unload_dist
+ || std::abs(base.z - iter->Position().z) > unload_dist) {
+ auto saved = iter;
+ Remove(*saved);
+ ++iter;
+ to_free.splice(to_free.end(), loaded, saved);
+ } else {
+ ++iter;
+ }
+ }
+ // abort far away queued chunks
+ for (auto iter(to_generate.begin()), end(to_generate.end()); iter != end;) {
+ if (std::abs(base.x - iter->x) > unload_dist
+ || std::abs(base.y - iter->y) > unload_dist
+ || std::abs(base.z - iter->z) > unload_dist) {
+ iter = to_generate.erase(iter);
+ } else {
+ ++iter;
+ }
+ }
+ // add missing new chunks
+ GenerateSurrounding(base);
+}
+
+void ChunkLoader::GenerateSurrounding(const Chunk::Pos &pos) {
+ const Chunk::Pos offset(load_dist, load_dist, load_dist);
+ Generate(pos - offset, pos + offset);
+}
+
+void ChunkLoader::Update() {
+ if (to_generate.empty()) {
+ return;
+ }
+
+ Chunk::Pos pos(to_generate.front());
+ to_generate.pop_front();
+
+ for (auto iter(to_free.begin()), end(to_free.end()); iter != end; ++iter) {
+ if (iter->Position() == pos) {
+ iter->Relink();
+ loaded.splice(loaded.end(), to_free, iter);
+ return;
+ }
+ }
+
+ if (to_free.empty()) {
+ loaded.emplace_back(reg);
+ } else {
+ to_free.front().ClearNeighbors();
+ loaded.splice(loaded.end(), to_free, to_free.begin());
+ }
+ Chunk &chunk = loaded.back();
+ chunk.Position(pos);
+ gen(chunk);
+ Insert(chunk);
+}
+
+}