aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorClyne Sullivan <clyne@bitgloo.com>2019-09-17 13:55:22 -0400
committerClyne Sullivan <clyne@bitgloo.com>2019-09-17 13:55:22 -0400
commitceda39e4bd2e3a7794f0cb4f96df1de6ebee47d2 (patch)
treeba5451b6dcade324114d145d526e7c5b5465689a
parentdbb26902ed54ce308fdcec4697616e152f2894fd (diff)
parent0236eb7f6391c9d925dcaaddb8cb01ecfb7d5e55 (diff)
update with master; using VBOs for fonts
-rw-r--r--.gitignore2
-rw-r--r--Assets/world.pngbin0 -> 236 bytes
-rw-r--r--Assets/world_normal.pngbin0 -> 1204 bytes
-rw-r--r--README.md3
-rw-r--r--Scripts/init.lua5
-rw-r--r--Scripts/world.lua101
-rw-r--r--src/engine.cpp19
-rw-r--r--src/events/world.hpp43
-rw-r--r--src/main.cpp1
-rw-r--r--src/physics.hpp2
-rw-r--r--src/render.cpp39
-rw-r--r--src/render.hpp23
-rw-r--r--src/script.cpp18
-rw-r--r--src/script.hpp10
-rw-r--r--src/shader.hpp1
-rw-r--r--src/text.cpp114
-rw-r--r--src/text.hpp44
-rw-r--r--src/texture.cpp128
-rw-r--r--src/texture.hpp15
-rw-r--r--src/world.cpp220
-rw-r--r--src/world.hpp156
-rwxr-xr-xtodo.sh19
22 files changed, 863 insertions, 100 deletions
diff --git a/.gitignore b/.gitignore
index 65892b3..3435d00 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,4 +4,4 @@ out
.*.swo
*.o
*.json
-
+TODOS
diff --git a/Assets/world.png b/Assets/world.png
new file mode 100644
index 0000000..c972c87
--- /dev/null
+++ b/Assets/world.png
Binary files differ
diff --git a/Assets/world_normal.png b/Assets/world_normal.png
new file mode 100644
index 0000000..e5f013c
--- /dev/null
+++ b/Assets/world_normal.png
Binary files differ
diff --git a/README.md b/README.md
index 351db1f..bbc4b78 100644
--- a/README.md
+++ b/README.md
@@ -33,7 +33,8 @@ The libraries used to develop gamedev2 are as follows:
* [sol2](https://github.com/ThePhD/sol2)
* [EntityX](https://github.com/alecthomas/entityx)
* [FreeType 2](https://www.freetype.org/)
-
+* [SOIL](https://lonesock.net/soil.html)
+* [cereal](https://uscilab.github.io/cereal/)
## Building gamedev2
### Build Requirements
diff --git a/Scripts/init.lua b/Scripts/init.lua
index 2eeee18..65513ec 100644
--- a/Scripts/init.lua
+++ b/Scripts/init.lua
@@ -131,6 +131,9 @@ wall = {
}
}
+-- Create the world
+dofile("Scripts/world.lua")
+
birdSpawn = game.spawn(bird);
dogSpawn = game.spawn(cat);
@@ -158,8 +161,6 @@ game.spawn({
end
});
-dofile("Scripts/world.lua")
-
-------------------
-- SERIALIZING --
-------------------
diff --git a/Scripts/world.lua b/Scripts/world.lua
index db0dc70..8fb3136 100644
--- a/Scripts/world.lua
+++ b/Scripts/world.lua
@@ -1,35 +1,92 @@
world = {
- Registry = {
- dirt = {
- id = "world0:dirt",
- texture = "Assets/dirt.png",
- normal = "Assets/dirt_normal.png"
- },
- stone = {
- id = "world0:stone",
- texture = "Assets/stone.png",
- normal = "Assets/dirt_normal.png"
- }
- },
Seed = 5345345,
- Layers = 3,
+ Layers = 2,
+
+ -- This is run when the world is registered and not after,
+ -- although it is possible to register materials later
+ Register = function(self)
+
+ -- TODO make world have global textures to speed up rendering
+
+ self:registerMaterial("grass", {
+ -- TODO combine both of these into 1
+ texture = {
+ file = "Assets/world.png",
+ offset = { x = 0, y = 0 },
+ size = { x = 8, y = 8 }
+ },
+ normal = {
+ file = "Assets/world_normal.png",
+ offset = { x = 0, y = 0 },
+ size = { x = 8, y = 8 }
+ }
+ });
+ self:registerMaterial("dirt", {
+ texture = {
+ file = "Assets/world.png",
+ offset = { x = 8, y = 0 },
+ size = { x = 8, y = 8 }
+ },
+ normal = {
+ file = "Assets/world_normal.png",
+ offset = { x = 8, y = 0 },
+ size = { x = 8, y = 8 }
+ }
+ });
+ self:registerMaterial("stone", {
+ texture = {
+ file = "Assets/world.png",
+ offset = { x = 16, y = 0 },
+ size = { x = 8, y = 8 }
+ },
+ normal = {
+ file = "Assets/world_normal.png",
+ offset = { x = 16, y = 0 },
+ size = { x = 8, y = 8 }
+ }
+ });
+ self:registerMaterial("flower", {
+ texture = {
+ file = "Assets/world.png",
+ offset = { x = 24, y = 0 },
+ size = { x = 8, y = 8 }
+ },
+ normal = {
+ file = "Assets/world_normal.png",
+ offset = { x = 24, y = 0 },
+ size = { x = 8, y = 8 }
+ },
+ passable = true
+ });
+ end,
+
Generate = function(self)
- self.data = {}
- for Z = 0,2 do
- self.data[Z] = {}
- for X = 0,250 do
- self.data[Z][X] = {}
+ math.randomseed(self.Seed)
+ xsize, ysize, zsize = self:setSize(250, 128, 3)
+ --self.data = {}
+ for Z = 0,zsize-1 do
+ --self.data[Z] = {}
+ for X = 0,xsize-1 do
+ --self.data[Z][X] = {}
YGen = math.floor(6*math.sin(X/20) + Z) + 64
- for Y = 0,128 do
+ YDepth = math.random(2,5)
+ for Y = 0,ysize-1 do
if Y == YGen then
- self.data[Z][X][Y] = 1
+ self:setData(X, Y, Z, "grass");
+ elseif Y < YGen and Y > (YGen - YDepth) then
+ self:setData(X, Y, Z, "dirt");
elseif Y < YGen then
- self.data[Z][X][Y] = 2
+ --self:setData(X, Y, Z, "stone");
+ self:setData(X, Y, Z, "grass");
end
+ --print(X..","..Y..","..Z);
end
end
end
+ self:setData(1000, 1345, 5, "grass"); -- Test error checking
+ print("Done with world gen");
end
}
-world:Generate()
+--world:Generate()
+game.worldRegister(world)
diff --git a/src/engine.cpp b/src/engine.cpp
index 37f6e68..70b0b45 100644
--- a/src/engine.cpp
+++ b/src/engine.cpp
@@ -45,8 +45,9 @@ int Engine::init(void)
systems.add<GameRunSystem>();
systems.add<InputSystem>();
systems.add<PlayerSystem>(entities);
+ systems.add<WorldSystem>();
systems.add<RenderSystem>();
- systems.add<ScriptSystem>(entities);
+ systems.add<ScriptSystem>(entities, *(systems.system<WorldSystem>().get()));
systems.add<PhysicsSystem>();
systems.add<TextSystem>();
systems.configure();
@@ -65,13 +66,19 @@ int Engine::init(void)
"it." << std::endl;
}
+ // Initially update the world to send all systems world data
+ systems.update<WorldSystem>(0);
return 0;
}
void Engine::logicLoop(void)
{
entityx::TimeDelta dt = 0; /**< Elapsed milliseconds since each loop */
- double elapsed = 0;
+ double elapsed = 1000; /**< Time elapsed since last logic loop. This
+ should be initialized to something larger
+ than our logic loop period (50ms), so
+ the logic loop is run during our first
+ loop. */
while (shouldRun()) {
auto start = mc::now();
@@ -86,6 +93,8 @@ void Engine::logicLoop(void)
// Update 20 times a second
if (elapsed > 50) {
elapsed = 0;
+
+ systems.update<WorldSystem>(dt);
// All entities with an idle function should be run here
entities.each<Scripted>([](entityx::Entity, Scripted &f){
@@ -155,9 +164,11 @@ void Engine::run(void)
GameState::save("save.json", entities);
// Remove all Lua references from entities
- entities.each<Scripted>([](entityx::Entity, Scripted &f){ f.cleanup(); });
- entities.each<EventListener>([](entityx::Entity, EventListener &f){
+ entities.each<Scripted>([](entityx::Entity, Scripted &f) {
+ f.cleanup(); });
+ entities.each<EventListener>([](entityx::Entity, EventListener &f) {
f.cleanup(); });
+ systems.system<WorldSystem>()->cleanup();
}
bool Engine::shouldRun(void)
diff --git a/src/events/world.hpp b/src/events/world.hpp
new file mode 100644
index 0000000..e5969c0
--- /dev/null
+++ b/src/events/world.hpp
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2019 Belle-Isle, Andrew <drumsetmonkey@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef EVENTS_WORLD_HPP_
+#define EVENTS_WORLD_HPP_
+
+#include "world.hpp"
+
+struct WorldChangeEvent
+{
+ World* newWorld;
+
+ WorldChangeEvent(World* w) :
+ newWorld(w) {}
+};
+
+struct WorldMeshUpdateEvent
+{
+ GLuint worldVBO;
+ unsigned int numVertex;
+ GLuint worldTexture;
+ GLuint worldNormal;
+
+ WorldMeshUpdateEvent(GLuint v, unsigned int p,
+ GLuint t, GLuint n) :
+ worldVBO(v), numVertex(p), worldTexture(t), worldNormal(n) {}
+};
+
+#endif//EVENTS_WORLD_HPP
diff --git a/src/main.cpp b/src/main.cpp
index a0632fd..5f39d95 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -18,6 +18,7 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
+#define GLEW_STATIC
#define SOL_ALL_SAFETIES_ON = 1
#include "engine.hpp"
diff --git a/src/physics.hpp b/src/physics.hpp
index 63443c4..8231d6d 100644
--- a/src/physics.hpp
+++ b/src/physics.hpp
@@ -1,6 +1,6 @@
/**
* @file position.hpp
- * Manages all Lua scripts.
+ * Manages all entity movements
*
* Copyright (C) 2019 Belle-Isle, Andrew <drumsetmonkey@gmail.com>
*
diff --git a/src/render.cpp b/src/render.cpp
index ceef025..b991b2f 100644
--- a/src/render.cpp
+++ b/src/render.cpp
@@ -27,6 +27,7 @@
void RenderSystem::configure([[maybe_unused]] entityx::EntityManager& entities,
[[maybe_unused]] entityx::EventManager& events)
{
+ events.subscribe<WorldMeshUpdateEvent>(*this);
init();
}
@@ -69,7 +70,7 @@ void RenderSystem::update([[maybe_unused]] entityx::EntityManager& entities,
);
glm::mat4 model = glm::mat4(1.0f);
- model = glm::scale(model, glm::vec3(2.0f));
+ model = glm::scale(model, glm::vec3(20.0f, 20.0f, 1.0f));
glUseProgram(s);
@@ -178,7 +179,29 @@ void RenderSystem::update([[maybe_unused]] entityx::EntityManager& entities,
5*sizeof(float), (void*)(3*sizeof(float)));
glDrawArrays(GL_TRIANGLES, 0, 6);
});
-
+
+ // If we were given a world VBO render it
+ if (worldVBO) {
+ glActiveTexture(GL_TEXTURE0);
+ glBindTexture(GL_TEXTURE_2D, worldTexture);
+ glUniform1i(q, 0);
+
+ glActiveTexture(GL_TEXTURE1);
+ glBindTexture(GL_TEXTURE_2D, worldNormal);
+ glUniform1i(n, 1);
+
+ glBindBuffer(GL_ARRAY_BUFFER, worldVBO);
+ //glBufferData(GL_ARRAY_BUFFER,
+ // wm.size() * sizeof(WorldMeshData),
+ // &wm.front(),
+ // GL_STREAM_DRAW);
+
+ glVertexAttribPointer(a, 3, GL_FLOAT, GL_FALSE,
+ 6*sizeof(float), 0);
+ glVertexAttribPointer(t, 2, GL_FLOAT, GL_FALSE,
+ 6*sizeof(float), (void*)(3*sizeof(float)));
+ glDrawArrays(GL_TRIANGLES, 0, worldVertex);
+ }
/*************
* CLEANUP *
@@ -261,8 +284,18 @@ int RenderSystem::init(void)
//glClearColor(0.6, 0.8, 1.0, 0.0);
- camPos = glm::vec3(0.0f, 0.0f, 0.5f);
+ camPos = glm::vec3(0.0f, 0.0f, 5.0f);
return 0;
}
+/************
+* EVENTS *
+************/
+void RenderSystem::receive(const WorldMeshUpdateEvent &wmu)
+{
+ worldVBO = wmu.worldVBO;
+ worldVertex = wmu.numVertex;
+ worldTexture = wmu.worldTexture;
+ worldNormal = wmu.worldNormal;
+}
diff --git a/src/render.hpp b/src/render.hpp
index 0e4275e..26e525b 100644
--- a/src/render.hpp
+++ b/src/render.hpp
@@ -22,11 +22,11 @@
#ifndef SYSTEM_RENDER_HPP_
#define SYSTEM_RENDER_HPP_
-#include "shader.hpp"
-
#include <entityx/entityx.h>
+#include <GL/glew.h>
#include <SDL2/SDL.h>
+#include <SDL2/SDL_opengl.h>
#define GLM_FORCE_RADIANS
#include <glm/glm.hpp>
@@ -34,7 +34,12 @@
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/noise.hpp>
-class RenderSystem : public entityx::System<RenderSystem>
+#include "shader.hpp"
+#include "world.hpp"
+#include "events/world.hpp"
+
+class RenderSystem : public entityx::System<RenderSystem>,
+ public entityx::Receiver<RenderSystem>
{
private:
constexpr static const char *title = "gamedev2";
@@ -47,8 +52,12 @@ private:
Shader worldShader;
glm::vec3 camPos;
+ GLuint worldVBO = 0;
+ unsigned int worldVertex = 0;
+ GLuint worldTexture = 0;
+ GLuint worldNormal = 0;
public:
- RenderSystem(void) :
+ RenderSystem() :
window(nullptr, SDL_DestroyWindow) {}
~RenderSystem(void)
@@ -75,6 +84,12 @@ public:
* @return Zero on success, non-zero on error
*/
int init(void);
+
+ /************
+ * EVENTS *
+ ************/
+ void receive(const WorldMeshUpdateEvent &wmu);
+
};
#endif // SYSTEM_RENDER_HPP_
diff --git a/src/script.cpp b/src/script.cpp
index 61aa136..ec8f5c7 100644
--- a/src/script.cpp
+++ b/src/script.cpp
@@ -87,6 +87,12 @@ void ScriptSystem::doFile(void)
void ScriptSystem::scriptExport(void)
{
+ std::function<sol::table(sol::table)> entitySpawn =
+ [this](sol::table t){ return spawn(t);};
+
+ std::function<World* (sol::object)> worldRegister =
+ [this](sol::object t){ return worldSystem.addWorld(t); };
+
lua.new_usertype<Position>("Position",
sol::constructors<Position(double x, double y), Position()>(),
"x", &Position::x,
@@ -121,8 +127,18 @@ void ScriptSystem::scriptExport(void)
sol::constructors<Physics(void), Physics()>(),
"standing", &Physics::standing);
+ lua.new_usertype<World>("World",
+ sol::constructors<World(sol::object), World(void)>(),
+ "Generate", &World::generate,
+ "Seed", sol::property(&World::setSeed, &World::getSeed),
+ "setData", &World::setData,
+ "registerMaterial", &World::registerMaterial,
+ "setSize", &World::setSize,
+ "getSize", &World::getSize);
+
game = lua["game"].get_or_create<sol::table>();
- game.set_function("spawn", [this](sol::table t) { return spawn(t); });
+ game.set_function("spawn", entitySpawn);
+ game.set_function("worldRegister", worldRegister);
}
sol::table ScriptSystem::spawn(sol::object param)
diff --git a/src/script.hpp b/src/script.hpp
index f86c8cd..0ac9e63 100644
--- a/src/script.hpp
+++ b/src/script.hpp
@@ -24,6 +24,8 @@
#include <entityx/entityx.h>
#include <sol/sol.hpp>
+#include "world.hpp"
+
struct EntitySpawnEvent
{
sol::object ref;
@@ -48,10 +50,14 @@ private:
sol::table game;
entityx::EntityManager& manager;
+
+ // TODO possibly emit events to spawn worlds instead of passing the
+ // world system around like a toy
+ WorldSystem &worldSystem;
public:
- ScriptSystem(entityx::EntityManager& _manager):
- manager(_manager) {}
+ ScriptSystem(entityx::EntityManager& _mg, WorldSystem& _ws):
+ manager(_mg), worldSystem(_ws) {}
~ScriptSystem(void) {}
diff --git a/src/shader.hpp b/src/shader.hpp
index 8691ab0..2a93f9c 100644
--- a/src/shader.hpp
+++ b/src/shader.hpp
@@ -24,6 +24,7 @@
#include <unordered_map>
#include <GL/glew.h>
+#include <SDL2/SDL_opengl.h>
class Shader
{
diff --git a/src/text.cpp b/src/text.cpp
index 85c0ceb..8726b88 100644
--- a/src/text.cpp
+++ b/src/text.cpp
@@ -2,10 +2,6 @@
#include <iostream>
-//FT_Library freetype;
-//std::map<std::string, FT_Face> fonts;
-//std::map<std::string, std::vector<FT_Info>> fontData;
-
void TextSystem::configure([[maybe_unused]] entityx::EntityManager& entities,
[[maybe_unused]] entityx::EventManager& events)
{
@@ -28,6 +24,8 @@ void TextSystem::loadFont(const std::string& name,
const std::string& file,
int size)
{
+ // Find or load font at given size
+ //
if (fonts.find(file) == fonts.end()) {
FT_Face face;
@@ -39,34 +37,88 @@ void TextSystem::loadFont(const std::string& name,
auto& face = fonts[file];
FT_Set_Pixel_Sizes(face, 0, size);
- fontData.try_emplace(name, 95);
-
- char c = 32;
- for (auto& d : fontData[name]) {
- FT_Load_Char(face, c++, FT_LOAD_RENDER);
-
- glGenTextures(1, &d.tex);
- glBindTexture(GL_TEXTURE_2D, d.tex);
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S , GL_CLAMP_TO_EDGE);
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T , GL_CLAMP_TO_EDGE);
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER , GL_LINEAR);
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER , GL_LINEAR);
- glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
-
- // convert red-on-black to RGBA
- auto& g = face->glyph;
- std::vector<uint32_t> buf (g->bitmap.width * g->bitmap.rows, 0xFFFFFF);
- for (auto j = buf.size(); j--;)
- buf[j] |= g->bitmap.buffer[j] << 24;
-
- d.wh = { g->bitmap.width, g->bitmap.rows };
- d.bl = { g->bitmap_left, g->bitmap_top };
- d.ad = { g->advance.x >> 6, g->advance.y >> 6 };
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, g->bitmap.width, g->bitmap.rows,
- 0, GL_RGBA, GL_UNSIGNED_BYTE, buf.data());
+ fontData.try_emplace(name);
+
+ // Calculate dimensions of final texture
+ //
+
+ float width = 0, height = 0;
+ for (int c = 32; c < 128; c++) {
+ FT_Load_Char(face, c, FT_LOAD_RENDER);
+ width += face->glyph->bitmap.width + 1;
+ height = std::max(height, static_cast<float>(face->glyph->bitmap.rows));
}
- std::cout << "Loaded font: " << file << " (size: " << size << ')'
- << std::endl;
+ // Generate texture to hold entire font
+ //
+
+ auto& font = fontData[name];
+ glGenTextures(1, &font.tex);
+ glGenBuffers(1, &font.vbo);
+ glBindTexture(GL_TEXTURE_2D, font.tex);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width, height,
+ 0, GL_RED, GL_UNSIGNED_BYTE, 0);
+ glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+
+ // // convert red-on-black to RGBA
+ // auto& g = face->glyph;
+ // std::vector<uint32_t> buf (g->bitmap.width * g->bitmap.rows, 0xFFFFFF);
+ // for (auto j = buf.size(); j--;)
+ // buf[j] |= g->bitmap.buffer[j] << 24;
+
+ // Load each character and add it to the texture
+ //
+
+ float offsetX = 0, offsetY = 0;
+ for (int c = 32; c < 128; c++) {
+ FT_Load_Char(face, c, FT_LOAD_RENDER);
+
+ auto* g = face->glyph;
+ glTexSubImage2D(GL_TEXTURE_2D, 0, offsetX, offsetY,
+ g->bitmap.width, g->bitmap.rows,
+ GL_RED, GL_UNSIGNED_BYTE,
+ g->bitmap.buffer);
+
+ auto& d = font.data[c - 32];
+ d.dim = { g->bitmap.width, g->bitmap.rows };
+ d.bitmap = { g->bitmap_left, g->bitmap_top };
+ d.advance = { g->advance.x >> 6, g->advance.y >> 6 };
+
+ d.offset = { offsetX / width, offsetY / height };
+ offsetX += g->bitmap.width;
+ // Keep offsetY at zero?
+ }
+
+ std::cout << "Loaded font: " << file << " (size: " << size << ", tex: "
+ << font.tex << ")" << std::endl;
+}
+
+void TextSystem::updateVBOs(void)
+{
+ for (auto& data : fontData) {
+ auto& d = data.second;
+ d.buffer.clear();
+ for (auto& text : d.text) {
+ for (char c : text.text) {
+ if (c < 32)
+ continue;
+
+ d.buffer += {
+ text.x, text.y, text.z,
+ d.data[c].offset.first, d.data[c].offset.second,
+ 1.0f
+ };
+ }
+ }
+
+ glBindBuffer(GL_ARRAY_BUFFER, d.vbo);
+ glBufferData(GL_ARRAY_BUFFER,
+ d.text.size() * sizeof(TextMeshData), d.text.data(),
+ GL_STREAM_DRAW);
+ }
}
diff --git a/src/text.hpp b/src/text.hpp
index d7fb790..f456500 100644
--- a/src/text.hpp
+++ b/src/text.hpp
@@ -24,28 +24,46 @@
#include <entityx/entityx.h>
#include <ft2build.h>
#include <freetype/freetype.h>
+#include <GL/glew.h>
#include <SDL2/SDL_opengl.h>
-
#include <map>
#include <string>
#include <tuple>
#include <vector>
+struct TextMeshData
+{
+ float posX, posY, posZ;
+ float texX, texY;
+ float transparency;
+} __attribute__ ((packed));
+
struct FT_Info {
- std::pair<float, float> wh;
- std::pair<float, float> bl;
- std::pair<float, float> ad;
- GLuint tex;
+ std::pair<float, float> offset;
+ std::pair<float, float> dim;
+ std::pair<float, float> bitmap;
+ std::pair<float, float> advance;
+};
- FT_Info(void)
- : tex(0) {}
+struct Text {
+ std::string text;
+ float x;
+ float y;
+ float z;
+};
+
+// Stores texture and placement data for a font at a size.
+struct Font {
+ GLuint tex;
+ GLuint vbo;
+
+ std::array<FT_Info, 96> data;
+ // Stores currently shown text at given index into VBO?
+ std::vector<Text> text;
+ std::basic_string<TextMeshData> buffer;
};
-/**
- * @class PhysicsSystem
- * Handles the position and velocity updating of all entities
- */
class TextSystem : public entityx::System<TextSystem>
{
public:
@@ -67,7 +85,9 @@ public:
private:
FT_Library freetype;
std::map<std::string, FT_Face> fonts;
- std::map<std::string, std::vector<FT_Info>> fontData;
+ std::map<std::string, Font> fontData;
+
+ void updateVBOs(void);
};
#endif // SYSTEM_TEXT_HPP_
diff --git a/src/texture.cpp b/src/texture.cpp
index 5604812..2870b9f 100644
--- a/src/texture.cpp
+++ b/src/texture.cpp
@@ -1,20 +1,118 @@
+/**
+ * @file texture.cpp
+ * Handles all texture loading
+ *
+ * Copyright (C) 2019 Belle-Isle, Andrew <drumsetmonkey@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+
#include "texture.hpp"
+#include <unordered_map>
+#include <iostream>
+
+struct TextureData
+{
+ GLuint tex = 0;
+ int width = 0;
+ int height= 0;
+};
+
+// Stores a list of all textures we've already loaded. This makes sure we don't
+// waste our precious CPU cycles reloading a texture.
+std::unordered_map<std::string, TextureData> textureCache;
+
+TextureData loadTexture(std::string filename)
+{
+ TextureData tex;
+
+ // Search to see if this texture has already been loading
+ auto cacheSearch = textureCache.find(filename);
+
+ if (cacheSearch == textureCache.end()) {
+ // If this texture hasn't been loading
+
+ unsigned char* image = SOIL_load_image(filename.c_str(),
+ &(tex.width), &(tex.height), 0,
+ SOIL_LOAD_RGBA);
+
+ glGenTextures(1, &(tex.tex));
+ glBindTexture(GL_TEXTURE_2D, tex.tex);
+ glPixelStoref(GL_UNPACK_ALIGNMENT, 1);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex.width, tex.height, 0,
+ GL_RGBA, GL_UNSIGNED_BYTE, image);
+
+ SOIL_free_image_data(image);
+
+ // Add new texture to the texture cache
+ textureCache.emplace(filename, tex);
+
+ } else {
+ // If this texture has been loaded, just return the loaded texture
+ tex = cacheSearch->second;
+ }
+
+ return tex;
+}
+
+void Texture::loadFromString(std::string filename)
+{
+ TextureData d = loadTexture(filename);
+
+ tex = d.tex;
+ width = d.width;
+ height = d.height;
+}
+
Texture::Texture(std::string filename)
{
- unsigned char* image = SOIL_load_image(filename.c_str(),
- &width, &height, 0,
- SOIL_LOAD_RGBA);
-
- glGenTextures(1, &tex); // Turns "object" into a texture
- glBindTexture(GL_TEXTURE_2D, tex); // Binds "object" to the top of the stack
- glPixelStoref(GL_UNPACK_ALIGNMENT, 1);
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); // Sets the "min" filter
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); // The the "max" filter of the stack
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); // Wrap the texture to the matrix
- glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); //
- glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
- GL_RGBA, GL_UNSIGNED_BYTE, image);
-
- SOIL_free_image_data(image);
+ loadFromString(filename);
+}
+
+Texture::Texture(sol::object param)
+{
+ if (param.get_type() == sol::type::string) {
+ loadFromString(param.as<std::string>());
+ } else if (param.get_type() == sol::type::table) {
+ sol::table tab = param;
+
+ // If there is a filename given, load that file to get image data
+ if (tab["file"] == sol::type::string)
+ loadFromString(tab.get<std::string>("file"));
+ else
+ return; // If we don't have image data just return a null image
+
+ if (tab["offset"] == sol::type::table) {
+ sol::table off = tab["offset"];
+ if (off["x"] == sol::type::number)
+ offset.x = off.get<float>("x")/width;
+ if (off["y"] == sol::type::number)
+ offset.y = off.get<float>("y")/height;
+ }
+
+ if (tab["size"] == sol::type::table) {
+ sol::table siz = tab["size"];
+ if (siz["x"] == sol::type::number)
+ size.x = siz.get<float>("x")/width;
+ if (siz["y"] == sol::type::number)
+ size.y = siz.get<float>("y")/height;
+ }
+ }
}
diff --git a/src/texture.hpp b/src/texture.hpp
index 16987f8..3daebbd 100644
--- a/src/texture.hpp
+++ b/src/texture.hpp
@@ -21,18 +21,31 @@
#define TEXTURE_HPP_
#include <soil/SOIL.h>
+
+#include <sol/sol.hpp>
+
+#include <GL/glew.h>
#include <SDL2/SDL_opengl.h>
+#include <glm/glm.hpp>
+
#include <string>
class Texture
{
private:
+ void loadFromString(std::string);
public:
- GLuint tex;
+ GLuint tex = 0;
+
+ glm::vec2 offset = glm::vec2(0);
+ glm::vec2 size = glm::vec2(1);
+
int width;
int height;
+
Texture() {};
Texture(std::string);
+ Texture(sol::object);
};
#endif//TEXTURE_HPP_
diff --git a/src/world.cpp b/src/world.cpp
new file mode 100644
index 0000000..7710ecc
--- /dev/null
+++ b/src/world.cpp
@@ -0,0 +1,220 @@
+/**
+ * @file world.cpp
+ *
+ * Copyright (C) 2019 Belle-Isle, Andrew <drumsetmonkey@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include "world.hpp"
+#include "events/world.hpp"
+
+/*****************
+* WORLD CLASS *
+*****************/
+/* CONSTRUCTORS */
+World::World(sol::object param)
+{
+ if (param.get_type() == sol::type::table) {
+ sol::table tab = param;
+
+ if (tab["Seed"] == sol::type::number) {
+ seed = tab["Seed"];
+ }
+ if (tab["Layers"] == sol::type::number) {
+ layers = tab["Layers"];
+ }
+ if (tab["Register"] == sol::type::function) {
+ registerMat = tab["Register"];
+ }
+ if (tab["Generate"] == sol::type::function) {
+ generate = tab["Generate"];
+ }
+
+ } else {
+ // TODO better logging
+ std::cerr << "World paramaters must be stored in a table" << std::endl;
+ }
+
+ // If there is a register function, we should call it here
+ if (registerMat != sol::nil)
+ registerMat(this);
+
+ // If a generate function is defined, call it
+ if (generate != sol::nil)
+ generate(this);
+
+ // Create our world VBO
+ glGenBuffers(1, &worldVBO);
+ // Generate our mesh
+ generateMesh();
+}
+
+/* REGISTRY */
+void World::registerMaterial(std::string name, sol::object data)
+{
+ if (data.get_type() == sol::type::table) {
+ sol::table tab = data;
+
+ // Make sure this material has not been registered before
+ auto it = string_registry.find(name);
+ if (it == string_registry.end()) {
+ string_registry.emplace(name, registry.size());
+ registry.push_back(WorldMaterial(tab));
+ } else {
+ std::cerr << "Material: " << name
+ << " was already registered" << std::endl;
+ }
+ } else {
+ // TODO better logging
+ std::cerr << "Material registration must have a table" << std::endl;
+ }
+}
+
+/* DATA */
+void World::setData(unsigned int x,
+ unsigned int y,
+ unsigned int z,
+ std::string d)
+{
+ unsigned int discovered = -1;
+
+ auto found = string_registry.find(d);
+ if (found != string_registry.end())
+ discovered = found->second;
+
+ try {
+ data.at(z).at(x).at(y) = discovered;
+ } catch (std::out_of_range &oor) {
+ // Make sure any assignments that are outsize specified world size are
+ // caught to avoid any seg faults
+ std::cerr << "Unable to set data at: "
+ << x << "," << y << "," << z
+ << " Exception: " << oor.what() << std::endl;
+ }
+}
+
+/* SIZE */
+std::tuple<unsigned int, unsigned int, unsigned int>
+World::setSize(unsigned int x, unsigned int y, unsigned int z)
+{
+ width = x;
+ height = y;
+ layers = z;
+
+ data = std::vector<std::vector<std::vector<int>>>
+ (z, std::vector<std::vector<int>>
+ (x,std::vector<int>
+ (y, -1)
+ )
+ );
+
+ return {width, height, layers};
+}
+
+std::tuple<unsigned int, unsigned int, unsigned int>
+World::getSize()
+{
+ return {width, height, layers};
+}
+
+/* RENDERING */
+void World::generateMesh()
+{
+ //const unsigned int voxelLength = 6; // 2 triangles @ 3 points each
+
+ // Preallocate size of vertexes
+ mesh = std::basic_string<WorldMeshData>();
+ for (float Z = 0; Z < data.size(); Z++) {
+ for (float X = 0; X < data.at(Z).size(); X++) {
+ for (float Y = 0; Y < data.at(Z).at(X).size(); Y++) {
+ int d = data.at(Z).at(X).at(Y);
+
+ if (d == -1) // Don't make a mesh for air of course
+ continue;
+
+ Texture &t = registry.at(d).texture;
+ glm::vec2& to = t.offset;
+ glm::vec2& ts = t.size;
+
+ mesh += {X , Y , Z, to.x , to.y+ts.y, 1.0};
+ mesh += {X+1, Y , Z, to.x+ts.x, to.y+ts.y, 1.0};
+ mesh += {X , Y+1, Z, to.x , to.y , 1.0};
+
+ mesh += {X+1, Y , Z, to.x+ts.x, to.y+ts.y, 1.0};
+ mesh += {X+1, Y+1, Z, to.x+ts.x, to.y , 1.0};
+ mesh += {X , Y+1, Z, to.x , to.y , 1.0};
+ }
+ }
+ }
+
+ glBindBuffer(GL_ARRAY_BUFFER, worldVBO);
+ glBufferData(GL_ARRAY_BUFFER,
+ mesh.size() * sizeof(mesh),
+ &mesh.front(),
+ GL_STREAM_DRAW);
+
+ meshUpdated = true;
+}
+
+/* SEED */
+unsigned int World::getSeed()
+{
+ return seed;
+}
+
+unsigned int World::setSeed(unsigned int s)
+{
+ seed = s;
+ return seed;
+}
+
+
+/******************
+* WORLD SYSTEM *
+******************/
+
+World* WorldSystem::addWorld(sol::object t)
+{
+ worlds.push_back(World(t));
+ if (currentWorld == nullptr)
+ currentWorld = &(worlds.back());
+ return &(worlds.back());
+}
+
+void WorldSystem::configure([[maybe_unused]]entityx::EntityManager& entities,
+ [[maybe_unused]]entityx::EventManager& events)
+{
+
+}
+
+void WorldSystem::update([[maybe_unused]]entityx::EntityManager& entities,
+ [[maybe_unused]]entityx::EventManager& events,
+ [[maybe_unused]]entityx::TimeDelta dt)
+{
+ if (currentWorld == nullptr) {
+ currentWorld = &(worlds.front());
+ events.emit<WorldChangeEvent>(currentWorld);
+ std::cout << "Emitted" << std::endl;
+ }
+
+ if (currentWorld->meshUpdated) {
+ events.emit<WorldMeshUpdateEvent>(
+ currentWorld->worldVBO,
+ currentWorld->mesh.size(),
+ currentWorld->getTexture(),
+ currentWorld->getNormal()
+ );
+ }
+}
diff --git a/src/world.hpp b/src/world.hpp
new file mode 100644
index 0000000..696f0aa
--- /dev/null
+++ b/src/world.hpp
@@ -0,0 +1,156 @@
+/**
+ * @file world.hpp
+ * Manages the world systems
+ *
+ * Copyright (C) 2019 Belle-Isle, Andrew <drumsetmonkey@gmail.com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef SYSTEM_WORLD_HPP_
+#define SYSTEM_WORLD_HPP_
+
+#include <vector>
+
+#include <entityx/entityx.h>
+#include <sol/sol.hpp>
+
+#include "texture.hpp"
+
+struct WorldMeshData
+{
+ float posX, posY, posZ;
+ float texX, texY;
+ float transparency;
+}__attribute__((packed));
+
+struct WorldMaterial
+{
+ bool passable = false;
+
+ Texture texture;
+ Texture normal;
+
+ WorldMaterial(sol::table tab) {
+ if (tab["texture"] != nullptr) {
+ sol::object t = tab["texture"];
+ texture = Texture(t);
+ }
+ if (tab["normal"] != nullptr) {
+ sol::object n = tab["normal"];
+ normal = Texture(n);
+ }
+ if (tab["passable"] == sol::type::boolean) {
+ passable = tab["passable"];
+ }
+ }
+};
+
+class World
+{
+ friend class WorldSystem;
+private:
+ unsigned int seed;
+ unsigned int layers;
+
+ unsigned int height;
+ unsigned int width;
+
+ std::vector<std::vector<std::vector<int>>> data;
+
+ std::unordered_map<std::string, int> string_registry;
+ std::vector<WorldMaterial> registry;
+
+protected:
+ // RENDER
+ std::basic_string<WorldMeshData> mesh;
+ GLuint worldVBO;
+ bool meshUpdated = false;
+public:
+ /* VARS */
+ sol::function generate;
+ sol::function registerMat;
+
+ World() {}
+ World(sol::object ref);
+ ~World() {
+ registerMat = sol::nil;
+ generate = sol::nil;
+ registry.clear();
+ data.clear();
+ }
+
+ /* REGISTRY */
+ void registerMaterial(std::string, sol::object);
+
+ /* DATA */
+ void setData(unsigned int, unsigned int, unsigned int, std::string);
+
+ /* SIZE */
+ std::tuple<unsigned int, unsigned int, unsigned int> setSize(unsigned int,
+ unsigned int,
+ unsigned int);
+ std::tuple<unsigned int, unsigned int, unsigned int> getSize();
+
+ /* RENDERING */
+ void generateMesh();
+ std::basic_string<WorldMeshData>& getMesh() {return mesh;}
+ GLuint getTexture() {return registry.at(0).texture.tex;}
+ GLuint getNormal() {return registry.at(0).normal.tex;};
+
+ /* SEED */
+ unsigned int getSeed();
+ unsigned int setSeed(unsigned int);
+};
+
+/**
+ * @class WorldSystem
+ * Handles the game's world system
+ */
+class WorldSystem : public entityx::System<WorldSystem>
+{
+private:
+ std::vector<World> worlds;
+ World* currentWorld;
+public:
+ WorldSystem(void):
+ currentWorld(nullptr) {}
+
+ ~WorldSystem(void) {
+ currentWorld = nullptr;
+ worlds.clear();
+ }
+
+ World* addWorld(sol::object);
+ World* current() {return currentWorld;};
+ void cleanup()
+ {
+ worlds.clear();
+ }
+
+ /**
+ * Prepares the system for running.
+ */
+ void configure(entityx::EntityManager& entities,
+ entityx::EventManager& events) final;
+
+ /**
+ * Updates the world ticks (entity spawns and world events)
+ */
+ void update(entityx::EntityManager& entites,
+ entityx::EventManager& events,
+ entityx::TimeDelta dt) final;
+};
+
+#endif // SYSTEM_WORLD_HPP_
diff --git a/todo.sh b/todo.sh
new file mode 100755
index 0000000..9520eab
--- /dev/null
+++ b/todo.sh
@@ -0,0 +1,19 @@
+#!/bin/bash
+
+#
+# Searches for all TODOs and tosses them in a file.
+#
+TODO_COUNT=0
+rm -f TODOS
+touch TODOS
+
+for file in $(find {src,Shaders,Scripts} -type f -name '*' -print)
+do
+ echo "########################################" >> TODOS
+ echo $file >> TODOS
+ echo "========================================" >> TODOS
+ grep -n -B 5 -A 5 "TODO" $file | sed s/--/========================================/g >> TODOS
+ TODO_COUNT=$((TODO_COUNT+$(grep -c "TODO" $file)))
+done
+
+echo "Found" $TODO_COUNT "TODOs."