--- /dev/null
+[submodule "assets"]
+ path = assets
+ url = http://git.localhorst.tv/repo/blank-assets.git
TEST_DIR := build/test
DIR := $(RELEASE_DIR) $(DEBUG_DIR) $(PROFILE_DIR) $(TEST_DIR) build
+ASSET_DIR := assets
+ASSET_DEP := $(ASSET_DIR)/.git
+
LIB_SRC := $(wildcard $(SOURCE_DIR)/*/*.cpp)
BIN_SRC := $(wildcard $(SOURCE_DIR)/*.cpp)
SRC := $(LIB_SRC) $(BIN_SRC)
tests: $(TEST_BIN)
-run: blank
+run: $(ASSET_DEP) blank
./blank
-gdb: blank.debug
+gdb: $(ASSET_DEP) blank.debug
gdb ./blank.debug
-cachegrind: blank.profile
+cachegrind: $(ASSET_DEP) blank.profile
valgrind ./blank.profile
-callgrind: blank.profile
+callgrind: $(ASSET_DEP) blank.profile
valgrind --tool=callgrind \
--branch-sim=yes --cacheuse=yes --cache-sim=yes \
--collect-bus=yes --collect-systime=yes --collect-jumps=yes \
@echo link: $@
@$(LDXX) -o $@ $(CXXFLAGS) $(LDXXFLAGS) $(TESTLIBS) $(TEST_FLAGS) $^
+$(ASSET_DEP): .git/$(shell git symbolic-ref HEAD)
+ @git submodule update --init >/dev/null
+ @touch $@
+
$(RELEASE_DIR)/%.o: $(SOURCE_DIR)/%.cpp | $(RELEASE_DIR)
@mkdir -p "$(@D)"
@echo compile: $@
do I need to say anything? :)
+font colours
+
+ set font fg and bg colour as either uniform or vertex attribute
+ and lerp between them based on the texture's alpha component
+
command line
usefull for development and later on world administration
--- /dev/null
+Subproject commit 7ac546c3310a3f147a889077dffa6919fad94be7
+++ /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_ttf()
-, 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.Name("test");
- e.Position({ 0.0f, 0.0f, 0.0f });
- e.Bounds({ { -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f } });
- e.WorldCollidable(true);
- 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();
-}
-
-}
#ifndef BLANK_APP_APPLICATION_HPP_
#define BLANK_APP_APPLICATION_HPP_
+#include "Assets.hpp"
#include "init.hpp"
#include "RandomWalk.hpp"
+#include "../graphics/BlendedSprite.hpp"
#include "../graphics/BlockLighting.hpp"
#include "../graphics/Camera.hpp"
#include "../graphics/DirectionalLighting.hpp"
#include "../ui/Interface.hpp"
#include "../world/World.hpp"
+#include <SDL.h>
+
namespace blank {
/// process all events in SDL's queue
void HandleEvents();
+ void Handle(const SDL_WindowEvent &);
/// integrate to the next step with dt milliseconds passed
void Update(int dt);
/// push the current state to display
Window window;
GLContext ctx;
InitGLEW init_glew;
+ Assets assets;
+
BlockLighting chunk_prog;
DirectionalLighting entity_prog;
+ BlendedSprite sprite_prog;
Camera cam;
World world;
--- /dev/null
+#ifndef BLANK_APP_ASSETS_HPP_
+#define BLANK_APP_ASSETS_HPP_
+
+#include <string>
+
+
+namespace blank {
+
+class Font;
+
+class Assets {
+
+public:
+ explicit Assets(const std::string &base);
+
+ Font LoadFont(const std::string &name, int size) const;
+
+private:
+ std::string fonts;
+
+};
+
+}
+
+#endif
--- /dev/null
+#include "Application.hpp"
+#include "Assets.hpp"
+
+#include "../graphics/Font.hpp"
+#include "../world/BlockType.hpp"
+#include "../world/Entity.hpp"
+
+#include <iostream>
+#include <stdexcept>
+
+using std::string;
+
+
+namespace {
+
+string get_asset_path() {
+ char *base = SDL_GetBasePath();
+ string assets(base);
+ assets += "assets/";
+ SDL_free(base);
+ return assets;
+}
+
+}
+
+namespace blank {
+
+Application::Application(const Config &config)
+: init_sdl()
+, init_img()
+, init_ttf()
+, init_gl(config.doublebuf, config.multisampling)
+, window()
+, ctx(window.CreateContext())
+, init_glew()
+, assets(get_asset_path())
+, chunk_prog()
+, entity_prog()
+, sprite_prog()
+, cam()
+, world(config.world)
+, interface(config.interface, assets, 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.Name("test");
+ e.Position({ 0.0f, 0.0f, 0.0f });
+ e.Bounds({ { -0.5f, -0.5f, -0.5f }, { 0.5f, 0.5f, 0.5f } });
+ e.WorldCollidable(true);
+ 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:
+ Handle(event.window);
+ break;
+ default:
+ break;
+ }
+ }
+}
+
+void Application::Handle(const SDL_WindowEvent &event) {
+ switch (event.event) {
+ case SDL_WINDOWEVENT_FOCUS_GAINED:
+ window.GrabMouse();
+ break;
+ case SDL_WINDOWEVENT_FOCUS_LOST:
+ window.ReleaseMouse();
+ break;
+ case SDL_WINDOWEVENT_RESIZED:
+ cam.Viewport(event.data1, event.data2);
+ interface.Handle(event);
+ break;
+ default:
+ interface.Handle(event);
+ 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, sprite_prog);
+
+ window.Flip();
+}
+
+
+Assets::Assets(const string &base)
+: fonts(base + "fonts/") {
+
+}
+
+Font Assets::LoadFont(const string &name, int size) const {
+ string full = fonts + name + ".ttf";
+ return Font(full.c_str(), size);
+}
+
+}
class Font {
+public:
+ enum FontStyle {
+ STYLE_NORMAL = TTF_STYLE_NORMAL,
+ STYLE_BOLD = TTF_STYLE_BOLD,
+ STYLE_ITALIC = TTF_STYLE_ITALIC,
+ STYLE_UNDERLINE = TTF_STYLE_UNDERLINE,
+ STYLE_STRIKE = TTF_STYLE_STRIKETHROUGH,
+ };
+ enum FontHinting {
+ HINT_NORMAL = TTF_HINTING_NORMAL,
+ HINT_LIGHT = TTF_HINTING_LIGHT,
+ HINT_MONO = TTF_HINTING_MONO,
+ HINT_NONE = TTF_HINTING_NONE,
+ };
+
public:
Font(const char *src, int size, long index = 0);
~Font();
Font &operator =(const Font &) = delete;
public:
+ int Style() const noexcept;
+ void Style(int) const noexcept;
+ int Outline() const noexcept;
+ void Outline(int) noexcept;
+
+ int Hinting() const noexcept;
+ void Hinting(int) const noexcept;
bool Kerning() const noexcept;
void Kerning(bool) noexcept;
int Descent() const noexcept;
int LineSkip() const noexcept;
+ const char *FamilyName() const noexcept;
+ const char *StyleName() const noexcept;
+
bool HasGlyph(Uint16) const noexcept;
glm::tvec2<int> TextSize(const char *) const;
Texture Render(const char *, SDL_Color) const;
+ void Render(const char *, SDL_Color, Texture &) const;
private:
TTF_Font *handle;
}
+int Font::Style() const noexcept {
+ return TTF_GetFontStyle(handle);
+}
+
+void Font::Style(int s) const noexcept {
+ TTF_SetFontStyle(handle, s);
+}
+
+int Font::Outline() const noexcept {
+ return TTF_GetFontOutline(handle);
+}
+
+void Font::Outline(int px) noexcept {
+ TTF_SetFontOutline(handle, px);
+}
+
+
+int Font::Hinting() const noexcept {
+ return TTF_GetFontHinting(handle);
+}
+
+void Font::Hinting(int h) const noexcept {
+ TTF_SetFontHinting(handle, h);
+}
+
bool Font::Kerning() const noexcept {
return TTF_GetFontKerning(handle);
}
}
+const char *Font::FamilyName() const noexcept {
+ return TTF_FontFaceFamilyName(handle);
+}
+
+const char *Font::StyleName() const noexcept {
+ return TTF_FontFaceStyleName(handle);
+}
+
+
bool Font::HasGlyph(Uint16 c) const noexcept {
return TTF_GlyphIsProvided(handle, c);
}
}
Texture Font::Render(const char *text, SDL_Color color) const {
+ Texture tex;
+ return tex;
+}
+
+void Font::Render(const char *text, SDL_Color color, Texture &tex) const {
SDL_Surface *srf = TTF_RenderUTF8_Blended(handle, text, color);
if (!srf) {
throw std::runtime_error(TTF_GetError());
}
- Texture tex;
tex.Bind();
tex.Data(*srf, false);
tex.FilterLinear();
SDL_FreeSurface(srf);
- return tex;
}
#ifndef BLANK_UI_HUD_H_
#define BLANK_UI_HUD_H_
+#include "../graphics/Texture.hpp"
#include "../model/Model.hpp"
#include "../model/OutlineModel.hpp"
+#include "../model/SpriteModel.hpp"
#include <glm/glm.hpp>
namespace blank {
+class BlendedSprite;
class Block;
class BlockTypeRegistry;
class DirectionalLighting;
+class Font;
class HUD {
public:
- explicit HUD(const BlockTypeRegistry &);
+ HUD(const BlockTypeRegistry &, const Font &);
HUD(const HUD &) = delete;
HUD &operator =(const HUD &) = delete;
void Display(const Block &);
- void Render(DirectionalLighting &) noexcept;
+ void Render(DirectionalLighting &, BlendedSprite &) noexcept;
private:
const BlockTypeRegistry &types;
+ const Font &font;
Model block;
Model::Buffer block_buf;
glm::mat4 block_transform;
+
+ Texture block_label;
+ SpriteModel label_sprite;
+ glm::mat4 label_transform;
+ SDL_Color label_color;
+
bool block_visible;
OutlineModel crosshair;
#include "HUD.hpp"
#include "../app/FPSController.hpp"
#include "../app/IntervalTimer.hpp"
+#include "../graphics/Font.hpp"
#include "../model/geometry.hpp"
#include "../model/OutlineModel.hpp"
#include "../world/Block.hpp"
namespace blank {
class Chunk;
+class BlendedSprite;
class DirectionalLighting;
+class Assets;
class World;
class Interface {
bool visual_disabled = false;
};
- Interface(const Config &, World &);
+ Interface(const Config &, const Assets &, World &);
void HandlePress(const SDL_KeyboardEvent &);
void HandleRelease(const SDL_KeyboardEvent &);
void Update(int dt);
- void Render(DirectionalLighting &) noexcept;
+ void Render(DirectionalLighting &, BlendedSprite &) noexcept;
private:
void CheckAim();
private:
World &world;
FPSController ctrl;
+ Font font;
HUD hud;
Ray aim;
#include "HUD.hpp"
#include "Interface.hpp"
+#include "../app/Assets.hpp"
#include "../app/init.hpp"
+#include "../graphics/BlendedSprite.hpp"
#include "../graphics/DirectionalLighting.hpp"
+#include "../graphics/Font.hpp"
#include "../model/shapes.hpp"
#include "../world/World.hpp"
+#include <algorithm>
+#include <cmath>
#include <iostream>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtx/io.hpp>
namespace blank {
-HUD::HUD(const BlockTypeRegistry &types)
+HUD::HUD(const BlockTypeRegistry &types, const Font &font)
: types(types)
+, font(font)
, block()
, block_buf()
, block_transform(1.0f)
+, block_label()
+, label_sprite()
+, label_transform(1.0f)
+, label_color{0xFF, 0xFF, 0xFF, 0xFF}
, 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))) {
+, view(1.0f) {
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));
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));
+ crosshair_transform = glm::translate(glm::vec3(width * 0.5f, height * 0.5f, 0.0f));
}
block_buf.Clear();
type.FillModel(block_buf, b.Transform());
block.Update(block_buf);
+
+ font.Render(type.label.c_str(), label_color, block_label);
+ glm::vec2 size(font.TextSize(type.label.c_str()));
+ label_sprite.LoadRect(size.x, size.y);
+ label_transform = glm::translate(glm::vec3(
+ std::max(5.0f, 50.0f - std::round(size.x * 0.5f)),
+ 70.0f + size.y,
+ 0.75f
+ ));
+
block_visible = type.visible;
}
-void HUD::Render(DirectionalLighting &program) noexcept {
- program.SetLightDirection({ 1.0f, 3.0f, 5.0f });
+void HUD::Render(DirectionalLighting &world_prog, BlendedSprite &sprite_prog) noexcept {
+ world_prog.Activate();
+ world_prog.SetLightDirection({ 1.0f, 3.0f, 5.0f });
// disable distance fog
- program.SetFogDensity(0.0f);
+ world_prog.SetFogDensity(0.0f);
GLContext::ClearDepthBuffer();
- program.SetVP(view, projection);
+ world_prog.SetMVP(crosshair_transform, view, projection);
+ crosshair.Draw();
if (block_visible) {
- program.SetM(block_transform);
+ world_prog.SetM(block_transform);
block.Draw();
- }
- program.SetM(crosshair_transform);
- crosshair.Draw();
+ sprite_prog.Activate();
+ sprite_prog.SetMVP(label_transform, view, projection);
+ sprite_prog.SetTexture(block_label);
+ label_sprite.Draw();
+ }
}
-Interface::Interface(const Config &config, World &world)
+Interface::Interface(const Config &config, const Assets &assets, World &world)
: world(world)
, ctrl(world.Player())
-, hud(world.BlockTypes())
+, font(assets.LoadFont("DejaVuSans", 16))
+, hud(world.BlockTypes(), font)
, aim{{ 0, 0, 0 }, { 0, 0, -1 }}
, aim_chunk(nullptr)
, aim_block(0)
}
-void Interface::Render(DirectionalLighting &program) noexcept {
+void Interface::Render(DirectionalLighting &world_prog, BlendedSprite &sprite_prog) noexcept {
if (config.visual_disabled) return;
if (aim_chunk) {
- program.SetM(outline_transform);
+ world_prog.Activate();
+ world_prog.SetM(outline_transform);
outline.Draw();
}
- hud.Render(program);
+ hud.Render(world_prog, sprite_prog);
}
}
glm::vec3 color;
glm::vec3 outline_color;
+ // a string to display to the user
+ std::string label;
+
Block::Type id;
// light level that blocks of this type emit
{ // white block
BlockType type(true, { 1.0f, 1.0f, 1.0f }, &blockShape);
+ type.label = "White Block";
type.block_light = true;
type.collision = true;
type.collide_block = true;
}
{ // white slab
BlockType type(true, { 1.0f, 1.0f, 1.0f }, &slabShape);
+ type.label = "White Slab";
type.block_light = true;
type.collision = true;
type.collide_block = true;
}
{ // white stair
BlockType type(true, { 1.0f, 1.0f, 1.0f }, &stairShape);
+ type.label = "White Stair";
type.block_light = true;
type.collision = true;
type.collide_block = true;
{ // red block
BlockType type(true, { 1.0f, 0.0f, 0.0f }, &blockShape);
+ type.label = "Red Block";
type.block_light = true;
type.collision = true;
type.collide_block = true;
}
{ // red slab
BlockType type(true, { 1.0f, 0.0f, 0.0f }, &slabShape);
+ type.label = "Red Slab";
type.block_light = true;
type.collision = true;
type.collide_block = true;
}
{ // red stair
BlockType type(true, { 1.0f, 0.0f, 0.0f }, &stairShape);
+ type.label = "Red Stair";
type.block_light = true;
type.collision = true;
type.collide_block = true;
{ // green block
BlockType type(true, { 0.0f, 1.0f, 0.0f }, &blockShape);
+ type.label = "Green Block";
type.block_light = true;
type.collision = true;
type.collide_block = true;
}
{ // green slab
BlockType type(true, { 0.0f, 1.0f, 0.0f }, &slabShape);
+ type.label = "Green Slab";
type.block_light = true;
type.collision = true;
type.collide_block = true;
}
{ // green stair
BlockType type(true, { 0.0f, 1.0f, 0.0f }, &stairShape);
+ type.label = "Green Stair";
type.block_light = true;
type.collision = true;
type.collide_block = true;
{ // blue block
BlockType type(true, { 0.0f, 0.0f, 1.0f }, &blockShape);
+ type.label = "Blue Block";
type.block_light = true;
type.collision = true;
type.collide_block = true;
}
{ // blue slab
BlockType type(true, { 0.0f, 0.0f, 1.0f }, &slabShape);
+ type.label = "Blue Slab";
type.block_light = true;
type.collision = true;
type.collide_block = true;
}
{ // blue stair
BlockType type(true, { 0.0f, 0.0f, 1.0f }, &stairShape);
+ type.label = "Blue Stair";
type.block_light = true;
type.collision = true;
type.collide_block = true;
{ // glowing yellow block
BlockType type(true, { 1.0f, 1.0f, 0.0f }, &blockShape);
+ type.label = "Light";
type.luminosity = 15;
type.block_light = true;
type.collision = true;
: shape(s)
, color(col)
, outline_color(-1, -1, -1)
+, label("some block")
, id(0)
, luminosity(0)
, visible(v)