First World Class Implementation
- Added PerlinNoise.(cpp/hpp) to repository. - New BlockChunk class for storing block info - New MeshChunk class, extends Entity and has a `build` method for building a mesh - World Class that encapsulates some world logic - ArrayTrans3D class for converting vectors to ints in chunks - Rewrote some code in Main to use the World class - MeshGenerator now uses a blockChunk and a blockAtlas - MeshGenerator is not doing any culling in this commit - Changed addFaces to use a glm::vec3 - BlockAtlas now has a `model` variable containing a BlockModel - Changed the window title to be more descriptive - Changed references of 'atlas' to 'textureAtlas'master
parent
d0c006d660
commit
188f6ff20e
|
@ -16,6 +16,6 @@ find_package(Boost COMPONENTS system thread)
|
||||||
|
|
||||||
link_directories(Libraries/glew/lib)
|
link_directories(Libraries/glew/lib)
|
||||||
|
|
||||||
add_executable(GlProject GlProject/Main.cpp GlProject/engine/graphics/Mesh.cpp GlProject/engine/graphics/Mesh.h GlProject/engine/PerlinNoise.cpp GlProject/engine/PerlinNoise.h GlProject/engine/Entity.cpp GlProject/engine/Entity.h GlProject/engine/graphics/Shader.cpp GlProject/engine/graphics/Shader.h GlProject/engine/Window.cpp GlProject/engine/Window.h GlProject/engine/Camera.cpp GlProject/engine/Camera.h GlProject/engine/graphics/Texture.cpp GlProject/engine/graphics/Texture.h GlProject/mesh/MeshGenerator.cpp GlProject/mesh/MeshGenerator.h GlProject/engine/Timer.cpp GlProject/engine/Timer.h GlProject/blocks/BlockAtlas.cpp GlProject/blocks/BlockAtlas.h GlProject/blocks/BlockDef.cpp GlProject/blocks/BlockDef.h GlProject/mesh/MeshPart.cpp GlProject/mesh/MeshPart.h GlProject/mesh/MeshMod.h GlProject/mesh/Vertex.cpp GlProject/mesh/Vertex.h GlProject/mesh/BlockModel.cpp GlProject/mesh/BlockModel.h GlProject/engine/TextureAtlas.cpp GlProject/engine/TextureAtlas.h GlProject/UDP.cpp GlProject/UDP.h)
|
add_executable(GlProject GlProject/Main.cpp GlProject/engine/graphics/Mesh.cpp GlProject/engine/graphics/Mesh.h GlProject/engine/PerlinNoise.cpp GlProject/engine/PerlinNoise.h GlProject/engine/Entity.cpp GlProject/engine/Entity.h GlProject/engine/graphics/Shader.cpp GlProject/engine/graphics/Shader.h GlProject/engine/Window.cpp GlProject/engine/Window.h GlProject/engine/Camera.cpp GlProject/engine/Camera.h GlProject/engine/graphics/Texture.cpp GlProject/engine/graphics/Texture.h GlProject/mesh/MeshGenerator.cpp GlProject/mesh/MeshGenerator.h GlProject/engine/Timer.cpp GlProject/engine/Timer.h GlProject/blocks/BlockAtlas.cpp GlProject/blocks/BlockAtlas.h GlProject/blocks/BlockDef.cpp GlProject/blocks/BlockDef.h GlProject/mesh/MeshPart.cpp GlProject/mesh/MeshPart.h GlProject/mesh/MeshMod.h GlProject/mesh/Vertex.cpp GlProject/mesh/Vertex.h GlProject/mesh/BlockModel.cpp GlProject/mesh/BlockModel.h GlProject/engine/TextureAtlas.cpp GlProject/engine/TextureAtlas.h GlProject/UDP.cpp GlProject/UDP.h GlProject/world/World.cpp GlProject/world/World.h GlProject/world/BlockChunk.cpp GlProject/world/BlockChunk.h GlProject/engine/helpers/ArrayTrans3D.h GlProject/world/MeshChunk.cpp GlProject/world/MeshChunk.h)
|
||||||
|
|
||||||
target_link_libraries(GlProject ${OPENGL_gl_LIBRARY} glfw libGLEW.so pthread)
|
target_link_libraries(GlProject ${OPENGL_gl_LIBRARY} glfw libGLEW.so pthread)
|
|
@ -22,18 +22,22 @@
|
||||||
#include "engine/TextureAtlas.h"
|
#include "engine/TextureAtlas.h"
|
||||||
#include "UDP.h"
|
#include "UDP.h"
|
||||||
#include "engine/PerlinNoise.h"
|
#include "engine/PerlinNoise.h"
|
||||||
|
#include "world/World.h"
|
||||||
|
#include "engine/helpers/ArrayTrans3D.h"
|
||||||
|
|
||||||
Window* window;
|
Window* window;
|
||||||
Shader* shader;
|
Shader* shader;
|
||||||
std::vector<Entity*> entities;
|
std::vector<Entity*> entities;
|
||||||
Camera* camera;
|
Camera* camera;
|
||||||
|
|
||||||
TextureAtlas* atlas;
|
TextureAtlas* textureAtlas;
|
||||||
|
BlockAtlas* blockAtlas;
|
||||||
|
World* world;
|
||||||
|
|
||||||
GLfloat deltaTime = 0.0f;
|
GLfloat deltaTime = 0.0f;
|
||||||
GLfloat lastTime = 0.0f;
|
GLfloat lastTime = 0.0f;
|
||||||
|
|
||||||
BlockModel* createBlockModel() {
|
BlockAtlas* createAtlas() {
|
||||||
Vertex* leftVerts = new Vertex[4] {
|
Vertex* leftVerts = new Vertex[4] {
|
||||||
Vertex(new glm::vec3(0.0f, 0.0f, 0.0f), nullptr, new glm::vec2(0.0f, 1.0f)),
|
Vertex(new glm::vec3(0.0f, 0.0f, 0.0f), nullptr, new glm::vec2(0.0f, 1.0f)),
|
||||||
Vertex(new glm::vec3(0.0f, 0.0f, 1.0f), nullptr, new glm::vec2(1.0f, 1.0f)),
|
Vertex(new glm::vec3(0.0f, 0.0f, 1.0f), nullptr, new glm::vec2(1.0f, 1.0f)),
|
||||||
|
@ -44,7 +48,7 @@ BlockModel* createBlockModel() {
|
||||||
0, 1, 2, 2, 3, 0
|
0, 1, 2, 2, 3, 0
|
||||||
};
|
};
|
||||||
|
|
||||||
auto* leftPart = new MeshPart(leftVerts, 4, leftInds, 6, "default_cobblestone", atlas);
|
auto* leftPart = new MeshPart(leftVerts, 4, leftInds, 6, "default_grass_side", textureAtlas);
|
||||||
|
|
||||||
Vertex* rightVerts = new Vertex[4] {
|
Vertex* rightVerts = new Vertex[4] {
|
||||||
Vertex(new glm::vec3(1.0f, 0.0f, 0.0f), nullptr, new glm::vec2(0.0f, 1.0f)),
|
Vertex(new glm::vec3(1.0f, 0.0f, 0.0f), nullptr, new glm::vec2(0.0f, 1.0f)),
|
||||||
|
@ -56,7 +60,7 @@ BlockModel* createBlockModel() {
|
||||||
0, 1, 2, 2, 3, 0
|
0, 1, 2, 2, 3, 0
|
||||||
};
|
};
|
||||||
|
|
||||||
auto* rightPart = new MeshPart(rightVerts, 4, rightInds, 6, "default_cobblestone", atlas);
|
auto* rightPart = new MeshPart(rightVerts, 4, rightInds, 6, "default_grass_side", textureAtlas);
|
||||||
|
|
||||||
Vertex* topVerts = new Vertex[4] {
|
Vertex* topVerts = new Vertex[4] {
|
||||||
Vertex(new glm::vec3(0.0f, 1.0f, 0.0f), nullptr, new glm::vec2(0.0f, 0.0f)),
|
Vertex(new glm::vec3(0.0f, 1.0f, 0.0f), nullptr, new glm::vec2(0.0f, 0.0f)),
|
||||||
|
@ -68,7 +72,7 @@ BlockModel* createBlockModel() {
|
||||||
0, 1, 2, 2, 3, 0
|
0, 1, 2, 2, 3, 0
|
||||||
};
|
};
|
||||||
|
|
||||||
auto* topPart = new MeshPart(topVerts, 4, topInds, 6, "default_cobblestone", atlas);
|
auto* topPart = new MeshPart(topVerts, 4, topInds, 6, "default_grass_top", textureAtlas);
|
||||||
|
|
||||||
Vertex* bottomVerts = new Vertex[4] {
|
Vertex* bottomVerts = new Vertex[4] {
|
||||||
Vertex(new glm::vec3(0.0f, 0.0f, 0.0f), nullptr, new glm::vec2(0.0f, 0.0f)),
|
Vertex(new glm::vec3(0.0f, 0.0f, 0.0f), nullptr, new glm::vec2(0.0f, 0.0f)),
|
||||||
|
@ -80,7 +84,7 @@ BlockModel* createBlockModel() {
|
||||||
0, 1, 2, 2, 3, 0
|
0, 1, 2, 2, 3, 0
|
||||||
};
|
};
|
||||||
|
|
||||||
auto* bottomPart = new MeshPart(bottomVerts, 4, bottomInds, 6, "default_cobblestone", atlas);
|
auto* bottomPart = new MeshPart(bottomVerts, 4, bottomInds, 6, "default_dirt", textureAtlas);
|
||||||
|
|
||||||
Vertex* frontVerts = new Vertex[4] {
|
Vertex* frontVerts = new Vertex[4] {
|
||||||
Vertex(new glm::vec3(0.0f, 0.0f, 1.0f), nullptr, new glm::vec2(0.0f, 1.0f)),
|
Vertex(new glm::vec3(0.0f, 0.0f, 1.0f), nullptr, new glm::vec2(0.0f, 1.0f)),
|
||||||
|
@ -92,7 +96,7 @@ BlockModel* createBlockModel() {
|
||||||
0, 1, 2, 2, 3, 0
|
0, 1, 2, 2, 3, 0
|
||||||
};
|
};
|
||||||
|
|
||||||
auto* frontPart = new MeshPart(frontVerts, 4, frontInds, 6, "default_cobblestone", atlas);
|
auto* frontPart = new MeshPart(frontVerts, 4, frontInds, 6, "default_grass_side", textureAtlas);
|
||||||
|
|
||||||
Vertex* backVerts = new Vertex[4] {
|
Vertex* backVerts = new Vertex[4] {
|
||||||
Vertex(new glm::vec3(0.0f, 0.0f, 0.0f), nullptr, new glm::vec2(0.0f, 1.0f)),
|
Vertex(new glm::vec3(0.0f, 0.0f, 0.0f), nullptr, new glm::vec2(0.0f, 1.0f)),
|
||||||
|
@ -104,49 +108,40 @@ BlockModel* createBlockModel() {
|
||||||
0, 1, 2, 2, 3, 0
|
0, 1, 2, 2, 3, 0
|
||||||
};
|
};
|
||||||
|
|
||||||
auto* backPart = new MeshPart(backVerts, 4, backInds, 6, "default_cobblestone", atlas);
|
auto* backPart = new MeshPart(backVerts, 4, backInds, 6, "default_grass_side", textureAtlas);
|
||||||
|
|
||||||
return new BlockModel(leftPart, rightPart, topPart, bottomPart, frontPart, backPart, nullptr, true, true);
|
auto* bm = new BlockModel(leftPart, rightPart, topPart, bottomPart, frontPart, backPart, nullptr, true, true);
|
||||||
|
return new BlockAtlas(bm);
|
||||||
}
|
}
|
||||||
|
|
||||||
void makeEntities(BlockModel* model) {
|
void genChunks(World* world) {
|
||||||
PerlinNoise p(0);
|
PerlinNoise p(0);
|
||||||
|
|
||||||
int VIEW_RANGE = 12;
|
int VIEW_RANGE = 1;
|
||||||
|
|
||||||
for (int i = -VIEW_RANGE; i < VIEW_RANGE; i++) {
|
for (int i = -VIEW_RANGE; i < VIEW_RANGE; i++) {
|
||||||
for (int j = -VIEW_RANGE; j < VIEW_RANGE; j++) {
|
for (int j = -VIEW_RANGE; j < VIEW_RANGE; j++) {
|
||||||
for (int k = -8; k < 4; k++) {
|
for (int k = -2; k < 2; k++) {
|
||||||
|
|
||||||
int array[CHUNK_SIZE][CHUNK_SIZE][CHUNK_SIZE];
|
auto* blocks = new std::vector<int>();
|
||||||
|
blocks->reserve(4096);
|
||||||
|
|
||||||
for (int x = 0; x < CHUNK_SIZE; x++) { // NOLINT(modernize-loop-convert)
|
glm::vec3 innerPos, pos;
|
||||||
for (int z = 0; z < CHUNK_SIZE; z++) {
|
|
||||||
for (int y = 0; y < CHUNK_SIZE; y++) {
|
|
||||||
int xx = x + i * 16;
|
|
||||||
int yy = y + k * 16;
|
|
||||||
int zz = z + j * 16;
|
|
||||||
double val = p.noise(xx / (double) 16, zz / (double) 16, yy / (double) 16);
|
|
||||||
|
|
||||||
array[z][y][x] = (int) (val > 0.5);
|
for (int ind = 0; ind < 4096; ind++) {
|
||||||
}
|
ArrayTrans3D::indAssignVec(ind, &innerPos);
|
||||||
}
|
pos.x = innerPos.x + i * CHUNK_SIZE;
|
||||||
|
pos.y = innerPos.y + k * CHUNK_SIZE;
|
||||||
|
pos.z = innerPos.z + j * CHUNK_SIZE;
|
||||||
|
|
||||||
|
double val = p.noise(pos.x / (double) 32, pos.z / (double) 32, pos.y / (double) 16) - pos.y * 0.08;
|
||||||
|
|
||||||
|
std::cout << (int)(val > 0.5) << std::endl;
|
||||||
|
|
||||||
|
blocks->push_back((int)(val > 0.5));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<float> vertices;
|
world->newChunk(new glm::vec3(i, k, j), new BlockChunk(blocks));
|
||||||
std::vector<unsigned int> indices;
|
|
||||||
|
|
||||||
MeshGenerator mg;
|
|
||||||
mg.build(array, model, vertices, indices);
|
|
||||||
|
|
||||||
auto *mesh = new Mesh();
|
|
||||||
mesh->create(&vertices, &indices);
|
|
||||||
|
|
||||||
auto *chunk = new Entity();
|
|
||||||
chunk->create(mesh);
|
|
||||||
chunk->setPosition(glm::vec3(i * CHUNK_SIZE, k * CHUNK_SIZE, j * CHUNK_SIZE));
|
|
||||||
chunk->setScale(1);
|
|
||||||
entities.push_back(chunk);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -166,13 +161,19 @@ int main(int argc, char* argv[]) {
|
||||||
camera = new Camera(glm::vec3(0.0f, 16.0f, 0.0f), glm::vec3(0, 1, 0), -90.0f, -45.0f, 10.0f, 0.1f);
|
camera = new Camera(glm::vec3(0.0f, 16.0f, 0.0f), glm::vec3(0, 1, 0), -90.0f, -45.0f, 10.0f, 0.1f);
|
||||||
|
|
||||||
//Load Textures
|
//Load Textures
|
||||||
atlas = new TextureAtlas("../Textures");
|
textureAtlas = new TextureAtlas("../Textures");
|
||||||
|
|
||||||
//Create model
|
//Create model
|
||||||
BlockModel* model = createBlockModel();
|
blockAtlas = createAtlas();
|
||||||
|
|
||||||
//Create entities
|
//Create world
|
||||||
makeEntities(model);
|
world = new World(blockAtlas);
|
||||||
|
// for (int i = 0; i < 17; i++) {
|
||||||
|
// world->newChunk(new glm::vec3(i, 0, 0), nullptr);
|
||||||
|
// }
|
||||||
|
|
||||||
|
//Generate Chunks
|
||||||
|
genChunks(world);
|
||||||
|
|
||||||
//Create shader
|
//Create shader
|
||||||
shader = new Shader();
|
shader = new Shader();
|
||||||
|
@ -201,6 +202,8 @@ int main(int argc, char* argv[]) {
|
||||||
camera->keyControl(window->getKeysArray(), deltaTime);
|
camera->keyControl(window->getKeysArray(), deltaTime);
|
||||||
camera->mouseControl(window->getDeltaX(), window->getDeltaY());
|
camera->mouseControl(window->getDeltaX(), window->getDeltaY());
|
||||||
|
|
||||||
|
world->update();
|
||||||
|
|
||||||
//Clear Window
|
//Clear Window
|
||||||
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
|
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
@ -210,12 +213,14 @@ int main(int argc, char* argv[]) {
|
||||||
glUniformMatrix4fv(shader->getProjectionLocation(), 1, GL_FALSE, glm::value_ptr(projectionMatrix));
|
glUniformMatrix4fv(shader->getProjectionLocation(), 1, GL_FALSE, glm::value_ptr(projectionMatrix));
|
||||||
glUniformMatrix4fv(shader->getViewLocation(), 1, GL_FALSE, glm::value_ptr(camera->calculateViewMatrix()));
|
glUniformMatrix4fv(shader->getViewLocation(), 1, GL_FALSE, glm::value_ptr(camera->calculateViewMatrix()));
|
||||||
|
|
||||||
atlas->getTexture()->use();
|
textureAtlas->getTexture()->use();
|
||||||
|
|
||||||
for (auto &entity : entities) {
|
// for (auto &entity : entities) {
|
||||||
glUniformMatrix4fv(shader->getModelLocation(), 1, GL_FALSE, glm::value_ptr(entity->getModelMatrix()));
|
// glUniformMatrix4fv(shader->getModelLocation(), 1, GL_FALSE, glm::value_ptr(entity->getModelMatrix()));
|
||||||
entity->draw();
|
// entity->draw();
|
||||||
}
|
// }
|
||||||
|
|
||||||
|
world->draw(shader->getModelLocation());
|
||||||
|
|
||||||
Shader::clearShader();
|
Shader::clearShader();
|
||||||
|
|
||||||
|
|
|
@ -3,3 +3,11 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
#include "BlockAtlas.h"
|
#include "BlockAtlas.h"
|
||||||
|
|
||||||
|
BlockAtlas::BlockAtlas(BlockModel *model) {
|
||||||
|
blockModel = model;
|
||||||
|
}
|
||||||
|
|
||||||
|
BlockModel* BlockAtlas::getBlockModel() {
|
||||||
|
return blockModel;
|
||||||
|
}
|
||||||
|
|
|
@ -6,8 +6,16 @@
|
||||||
#define GLPROJECT_BLOCKATLAS_H
|
#define GLPROJECT_BLOCKATLAS_H
|
||||||
|
|
||||||
|
|
||||||
class BlockAtlas {
|
#include "../mesh/BlockModel.h"
|
||||||
|
|
||||||
|
class BlockAtlas {
|
||||||
|
public:
|
||||||
|
BlockAtlas() = default;
|
||||||
|
explicit BlockAtlas(BlockModel* model);
|
||||||
|
|
||||||
|
BlockModel* getBlockModel();
|
||||||
|
private:
|
||||||
|
BlockModel* blockModel;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
#include "PerlinNoise.h"
|
||||||
|
#include <cmath>
|
||||||
|
#include <random>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <numeric>
|
||||||
|
|
||||||
|
// THIS IS A DIRECT TRANSLATION TO C++11 FROM THE REFERENCE
|
||||||
|
// JAVA IMPLEMENTATION OF THE IMPROVED PERLIN FUNCTION (see http://mrl.nyu.edu/~perlin/noise/)
|
||||||
|
// THE ORIGINAL JAVA IMPLEMENTATION IS COPYRIGHT 2002 KEN PERLIN
|
||||||
|
|
||||||
|
// I ADDED AN EXTRA METHOD THAT GENERATES A NEW PERMUTATION VECTOR (THIS IS NOT PRESENT IN THE ORIGINAL IMPLEMENTATION)
|
||||||
|
|
||||||
|
// Initialize with the reference values for the permutation vector
|
||||||
|
PerlinNoise::PerlinNoise() {
|
||||||
|
|
||||||
|
// Initialize the permutation vector with the reference values
|
||||||
|
p = {
|
||||||
|
151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,140,36,103,30,69,142,
|
||||||
|
8,99,37,240,21,10,23,190, 6,148,247,120,234,75,0,26,197,62,94,252,219,203,117,
|
||||||
|
35,11,32,57,177,33,88,237,149,56,87,174,20,125,136,171,168, 68,175,74,165,71,
|
||||||
|
134,139,48,27,166,77,146,158,231,83,111,229,122,60,211,133,230,220,105,92,41,
|
||||||
|
55,46,245,40,244,102,143,54, 65,25,63,161,1,216,80,73,209,76,132,187,208, 89,
|
||||||
|
18,169,200,196,135,130,116,188,159,86,164,100,109,198,173,186, 3,64,52,217,226,
|
||||||
|
250,124,123,5,202,38,147,118,126,255,82,85,212,207,206,59,227,47,16,58,17,182,
|
||||||
|
189,28,42,223,183,170,213,119,248,152, 2,44,154,163, 70,221,153,101,155,167,
|
||||||
|
43,172,9,129,22,39,253, 19,98,108,110,79,113,224,232,178,185, 112,104,218,246,
|
||||||
|
97,228,251,34,242,193,238,210,144,12,191,179,162,241, 81,51,145,235,249,14,239,
|
||||||
|
107,49,192,214, 31,181,199,106,157,184, 84,204,176,115,121,50,45,127, 4,150,254,
|
||||||
|
138,236,205,93,222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180 };
|
||||||
|
// Duplicate the permutation vector
|
||||||
|
p.insert(p.end(), p.begin(), p.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generate a new permutation vector based on the value of seed
|
||||||
|
PerlinNoise::PerlinNoise(unsigned int seed) {
|
||||||
|
p.resize(256);
|
||||||
|
|
||||||
|
// Fill p with values from 0 to 255
|
||||||
|
std::iota(p.begin(), p.end(), 0);
|
||||||
|
|
||||||
|
// Initialize a random engine with seed
|
||||||
|
std::default_random_engine engine(seed);
|
||||||
|
|
||||||
|
// Suffle using the above random engine
|
||||||
|
std::shuffle(p.begin(), p.end(), engine);
|
||||||
|
|
||||||
|
// Duplicate the permutation vector
|
||||||
|
p.insert(p.end(), p.begin(), p.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
double PerlinNoise::noise(double x, double y, double z) {
|
||||||
|
// Find the unit cube that contains the point
|
||||||
|
int X = (int) floor(x) & 255;
|
||||||
|
int Y = (int) floor(y) & 255;
|
||||||
|
int Z = (int) floor(z) & 255;
|
||||||
|
|
||||||
|
// Find relative x, y,z of point in cube
|
||||||
|
x -= floor(x);
|
||||||
|
y -= floor(y);
|
||||||
|
z -= floor(z);
|
||||||
|
|
||||||
|
// Compute fade curves for each of x, y, z
|
||||||
|
double u = fade(x);
|
||||||
|
double v = fade(y);
|
||||||
|
double w = fade(z);
|
||||||
|
|
||||||
|
// Hash coordinates of the 8 cube corners
|
||||||
|
int A = p[X] + Y;
|
||||||
|
int AA = p[A] + Z;
|
||||||
|
int AB = p[A + 1] + Z;
|
||||||
|
int B = p[X + 1] + Y;
|
||||||
|
int BA = p[B] + Z;
|
||||||
|
int BB = p[B + 1] + Z;
|
||||||
|
|
||||||
|
// Add blended results from 8 corners of cube
|
||||||
|
double res = lerp(w, lerp(v, lerp(u, grad(p[AA], x, y, z), grad(p[BA], x-1, y, z)), lerp(u, grad(p[AB], x, y-1, z), grad(p[BB], x-1, y-1, z))), lerp(v, lerp(u, grad(p[AA+1], x, y, z-1), grad(p[BA+1], x-1, y, z-1)), lerp(u, grad(p[AB+1], x, y-1, z-1), grad(p[BB+1], x-1, y-1, z-1))));
|
||||||
|
return (res + 1.0)/2.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double PerlinNoise::fade(double t) {
|
||||||
|
return t * t * t * (t * (t * 6 - 15) + 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
double PerlinNoise::lerp(double t, double a, double b) {
|
||||||
|
return a + t * (b - a);
|
||||||
|
}
|
||||||
|
|
||||||
|
double PerlinNoise::grad(int hash, double x, double y, double z) {
|
||||||
|
int h = hash & 15;
|
||||||
|
// Convert lower 4 bits of hash into 12 gradient directions
|
||||||
|
double u = h < 8 ? x : y,
|
||||||
|
v = h < 4 ? y : h == 12 || h == 14 ? x : z;
|
||||||
|
return ((h & 1) == 0 ? u : -u) + ((h & 2) == 0 ? v : -v);
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
// THIS CLASS IS A TRANSLATION TO C++11 FROM THE REFERENCE
|
||||||
|
|
||||||
|
// JAVA IMPLEMENTATION OF THE IMPROVED PERLIN FUNCTION (see http://mrl.nyu.edu/~perlin/noise/)
|
||||||
|
// THE ORIGINAL JAVA IMPLEMENTATION IS COPYRIGHT 2002 KEN PERLIN
|
||||||
|
// I ADDED AN EXTRA METHOD THAT GENERATES A NEW PERMUTATION VECTOR (THIS IS NOT PRESENT IN THE ORIGINAL IMPLEMENTATION)
|
||||||
|
|
||||||
|
#ifndef PERLINNOISE_H
|
||||||
|
#define PERLINNOISE_H
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class PerlinNoise {
|
||||||
|
// The permutation vector
|
||||||
|
std::vector<int> p;
|
||||||
|
public:
|
||||||
|
// Initialize with the reference values for the permutation vector
|
||||||
|
PerlinNoise();
|
||||||
|
// Generate a new permutation vector based on the value of seed
|
||||||
|
PerlinNoise(unsigned int seed);
|
||||||
|
// Get a noise value, for 2D images z can have any value
|
||||||
|
double noise(double x, double y, double z);
|
||||||
|
private:
|
||||||
|
double fade(double t);
|
||||||
|
double lerp(double t, double a, double b);
|
||||||
|
double grad(int hash, double x, double y, double z);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -94,7 +94,7 @@ TextureAtlas::TextureAtlas(const char* directory) {
|
||||||
pageData[i * 4 + 3] = 0;
|
pageData[i * 4 + 3] = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Add all of the textures to the atlas and store the UVs in the associative array
|
//Add all of the textures to the textureAtlas and store the UVs in the associative array
|
||||||
int widthOffset = 0;
|
int widthOffset = 0;
|
||||||
int heightOffset = 0;
|
int heightOffset = 0;
|
||||||
int tallestInRow = 0;
|
int tallestInRow = 0;
|
||||||
|
@ -130,7 +130,7 @@ TextureAtlas::TextureAtlas(const char* directory) {
|
||||||
widthOffset += i.width;
|
widthOffset += i.width;
|
||||||
}
|
}
|
||||||
|
|
||||||
stbi_write_png("../atlas.png", pageWidth, pageHeight, 4, pageData, pageWidth*4);
|
stbi_write_png("../textureAtlas.png", pageWidth, pageHeight, 4, pageData, pageWidth*4);
|
||||||
texture = new Texture();
|
texture = new Texture();
|
||||||
texture->load(pageData, pageWidth, pageHeight);
|
texture->load(pageData, pageWidth, pageHeight);
|
||||||
}
|
}
|
||||||
|
@ -141,7 +141,7 @@ Texture* TextureAtlas::getTexture() {
|
||||||
|
|
||||||
glm::vec4* TextureAtlas::getUVs(std::string* texture) {
|
glm::vec4* TextureAtlas::getUVs(std::string* texture) {
|
||||||
if (textures.count(*texture) == 0) {
|
if (textures.count(*texture) == 0) {
|
||||||
std::cout << "Texture '" << *texture << "' Not found in atlas! Terminating." << std::endl;
|
std::cout << "Texture '" << *texture << "' Not found in textureAtlas! Terminating." << std::endl;
|
||||||
throw std::exception();
|
throw std::exception();
|
||||||
}
|
}
|
||||||
return &textures.at(*texture);
|
return &textures.at(*texture);
|
||||||
|
|
|
@ -43,7 +43,7 @@ int Window::initialize() {
|
||||||
// glEnable(GL_MULTISAMPLE);
|
// glEnable(GL_MULTISAMPLE);
|
||||||
|
|
||||||
//Create the window
|
//Create the window
|
||||||
mainWindow = glfwCreateWindow(width, height, "OpenGL Learning", nullptr, nullptr);
|
mainWindow = glfwCreateWindow(width, height, "Zeus_cpp OPENGL Linux x64", nullptr, nullptr);
|
||||||
|
|
||||||
if (!mainWindow) {
|
if (!mainWindow) {
|
||||||
printf("GLFW window failed");
|
printf("GLFW window failed");
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
//
|
||||||
|
// Created by aurailus on 14/12/18.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef GLPROJECT_ARRAYTRANS3D_H
|
||||||
|
#define GLPROJECT_ARRAYTRANS3D_H
|
||||||
|
|
||||||
|
#include <vec3.hpp>
|
||||||
|
|
||||||
|
class ArrayTrans3D {
|
||||||
|
static const int SIZE = 16;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static void indAssignVec(int ind, glm::vec3* vec) {
|
||||||
|
vec->z = ind / (SIZE * SIZE);
|
||||||
|
ind -= ((int)vec->z * SIZE * SIZE);
|
||||||
|
vec->y = ind / SIZE;
|
||||||
|
vec->x = ind % SIZE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static glm::vec3* indToVec(int ind) {
|
||||||
|
int z = ind / (SIZE * SIZE);
|
||||||
|
ind -= (z * SIZE * SIZE);
|
||||||
|
int y = ind / SIZE;
|
||||||
|
int x = ind % SIZE;
|
||||||
|
return new glm::vec3(x, y, z);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int vecToInd(glm::vec3* vec) {
|
||||||
|
return vecToInd((int)vec->x, (int)vec->y, (int)vec->z);
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned int vecToInd(int x, int y, int z) {
|
||||||
|
return (unsigned int)(x + SIZE * (y + SIZE * z));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //GLPROJECT_ARRAYTRANS3D_H
|
|
@ -2,9 +2,8 @@
|
||||||
// Created by aurailus on 01/12/18.
|
// Created by aurailus on 01/12/18.
|
||||||
//
|
//
|
||||||
|
|
||||||
#include <cstdio>
|
|
||||||
#include "MeshGenerator.h"
|
#include "MeshGenerator.h"
|
||||||
#include "../engine/Timer.h"
|
#include "../engine/helpers/ArrayTrans3D.h"
|
||||||
|
|
||||||
MeshGenerator::MeshGenerator() {
|
MeshGenerator::MeshGenerator() {
|
||||||
indOffset = 0;
|
indOffset = 0;
|
||||||
|
@ -14,8 +13,8 @@ bool outOfRange(glm::vec3 pos) {
|
||||||
return (pos.x < 0 || pos.x > 15 || pos.y < 0 || pos.y > 15 || pos.z < 0 || pos.z > 15);
|
return (pos.x < 0 || pos.x > 15 || pos.y < 0 || pos.y > 15 || pos.z < 0 || pos.z > 15);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MeshGenerator::build(int blocks[CHUNK_SIZE][CHUNK_SIZE][CHUNK_SIZE], BlockModel* model,
|
void MeshGenerator::build(BlockChunk* chunk, BlockAtlas* atlas,
|
||||||
std::vector<float> &vertices, std::vector<unsigned int> &indices) {
|
std::vector<float> &vertices, std::vector<unsigned int> &indices) {
|
||||||
|
|
||||||
Timer t("Mesh Generation");
|
Timer t("Mesh Generation");
|
||||||
|
|
||||||
|
@ -24,32 +23,31 @@ void MeshGenerator::build(int blocks[CHUNK_SIZE][CHUNK_SIZE][CHUNK_SIZE], BlockM
|
||||||
|
|
||||||
glm::vec3 off;
|
glm::vec3 off;
|
||||||
|
|
||||||
|
BlockModel* model = atlas->getBlockModel();
|
||||||
|
|
||||||
for (int i = 0; i < 4096; i++) {
|
for (int i = 0; i < 4096; i++) {
|
||||||
int idx = i;
|
|
||||||
int z = idx / (CHUNK_SIZE * CHUNK_SIZE);
|
|
||||||
idx -= (z * CHUNK_SIZE * CHUNK_SIZE);
|
|
||||||
int y = idx / CHUNK_SIZE;
|
|
||||||
int x = idx % CHUNK_SIZE;
|
|
||||||
|
|
||||||
if (blocks[z][y][x] == 1) {
|
if (chunk->getBlock(i) == 1) {
|
||||||
|
|
||||||
if (outOfRange(glm::vec3(x - 1, y, z)) || blocks[z][y][x - 1] == 0)
|
ArrayTrans3D::indAssignVec(i, &off);
|
||||||
addFaces(x, y, z, &vertices, &indices, &model->leftFaces);
|
|
||||||
|
|
||||||
if (outOfRange(glm::vec3(x + 1, y, z)) || blocks[z][y][x + 1] == 0)
|
// if (outOfRange(glm::vec3(x - 1, y, z)) || blocks[z][y][x - 1] == 0)
|
||||||
addFaces(x, y, z, &vertices, &indices, &model->rightFaces);
|
addFaces(off, &vertices, &indices, &model->leftFaces);
|
||||||
|
|
||||||
if (outOfRange(glm::vec3(x, y - 1, z)) || blocks[z][y - 1][x] == 0)
|
// if (outOfRange(glm::vec3(x + 1, y, z)) || blocks[z][y][x + 1] == 0)
|
||||||
addFaces(x, y, z, &vertices, &indices, &model->bottomFaces);
|
addFaces(off, &vertices, &indices, &model->rightFaces);
|
||||||
|
|
||||||
if (outOfRange(glm::vec3(x, y + 1, z)) || blocks[z][y + 1][x] == 0)
|
// if (outOfRange(glm::vec3(x, y - 1, z)) || blocks[z][y - 1][x] == 0)
|
||||||
addFaces(x, y, z, &vertices, &indices, &model->topFaces);
|
addFaces(off, &vertices, &indices, &model->bottomFaces);
|
||||||
|
|
||||||
if (outOfRange(glm::vec3(x, y, z - 1)) || blocks[z - 1][y][x] == 0)
|
// if (outOfRange(glm::vec3(x, y + 1, z)) || blocks[z][y + 1][x] == 0)
|
||||||
addFaces(x, y, z, &vertices, &indices, &model->backFaces);
|
addFaces(off, &vertices, &indices, &model->topFaces);
|
||||||
|
|
||||||
if (outOfRange(glm::vec3(x, y, z + 1)) || blocks[z + 1][y][x] == 0)
|
// if (outOfRange(glm::vec3(x, y, z - 1)) || blocks[z - 1][y][x] == 0)
|
||||||
addFaces(x, y, z, &vertices, &indices, &model->frontFaces);
|
addFaces(off, &vertices, &indices, &model->backFaces);
|
||||||
|
|
||||||
|
// if (outOfRange(glm::vec3(x, y, z + 1)) || blocks[z + 1][y][x] == 0)
|
||||||
|
addFaces(off, &vertices, &indices, &model->frontFaces);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -59,16 +57,16 @@ void MeshGenerator::build(int blocks[CHUNK_SIZE][CHUNK_SIZE][CHUNK_SIZE], BlockM
|
||||||
t.elapsedMs();
|
t.elapsedMs();
|
||||||
}
|
}
|
||||||
|
|
||||||
void MeshGenerator::addFaces(int x, int y, int z, vector<float>* vertices, vector<unsigned int>* indices, vector<MeshPart*>* meshParts) {
|
void MeshGenerator::addFaces(glm::vec3 &offset, vector<float>* vertices, vector<unsigned int>* indices, vector<MeshPart*>* meshParts) {
|
||||||
for (MeshPart *mp : *meshParts) {
|
for (MeshPart *mp : *meshParts) {
|
||||||
|
|
||||||
MeshVertexIter *mvIterator = mp->getVertexIterator();
|
MeshVertexIter *mvIterator = mp->getVertexIterator();
|
||||||
while (mvIterator->hasNext()) {
|
while (mvIterator->hasNext()) {
|
||||||
Vertex *vertex = mvIterator->next();
|
Vertex *vertex = mvIterator->next();
|
||||||
|
|
||||||
vertices->push_back(vertex->pos->x + x);
|
vertices->push_back(vertex->pos->x + offset.x);
|
||||||
vertices->push_back(vertex->pos->y + y);
|
vertices->push_back(vertex->pos->y + offset.y);
|
||||||
vertices->push_back(vertex->pos->z + z);
|
vertices->push_back(vertex->pos->z + offset.z);
|
||||||
|
|
||||||
vertices->push_back(vertex->tex->x);
|
vertices->push_back(vertex->tex->x);
|
||||||
vertices->push_back(vertex->tex->y);
|
vertices->push_back(vertex->tex->y);
|
||||||
|
|
|
@ -10,20 +10,24 @@
|
||||||
#include "BlockModel.h"
|
#include "BlockModel.h"
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <gtx/normal.hpp>
|
#include <gtx/normal.hpp>
|
||||||
|
#include <cstdio>
|
||||||
|
#include "../engine/Timer.h"
|
||||||
|
#include "../blocks/BlockAtlas.h"
|
||||||
|
#include "../world/BlockChunk.h"
|
||||||
|
|
||||||
const int CHUNK_SIZE = 16;
|
const int CHUNK_SIZE = 16;
|
||||||
|
|
||||||
class MeshGenerator {
|
class MeshGenerator {
|
||||||
public:
|
public:
|
||||||
MeshGenerator();
|
MeshGenerator();
|
||||||
void build(int blocks[CHUNK_SIZE][CHUNK_SIZE][CHUNK_SIZE], BlockModel* model,
|
void build(BlockChunk* chunk, BlockAtlas* atlas,
|
||||||
std::vector<float> &vertices, std::vector<unsigned int> &indices);
|
std::vector<float> &vertices, std::vector<unsigned int> &indices);
|
||||||
|
|
||||||
~MeshGenerator();
|
~MeshGenerator();
|
||||||
private:
|
private:
|
||||||
unsigned int indOffset;
|
unsigned int indOffset;
|
||||||
|
|
||||||
void addFaces(int x, int y, int z, vector<float>* vertices, vector<unsigned int>* indices, vector<MeshPart*>* meshParts);
|
void addFaces(glm::vec3 &offset, vector<float>* vertices, vector<unsigned int>* indices, vector<MeshPart*>* meshParts);
|
||||||
|
|
||||||
void cleanup();
|
void cleanup();
|
||||||
};
|
};
|
||||||
|
|
|
@ -49,7 +49,7 @@ void MeshPart::construct(Vertex* vertices, int vSize, unsigned int *indices, int
|
||||||
auto uv = atlas->getUVs(&texString);
|
auto uv = atlas->getUVs(&texString);
|
||||||
// std::cout << uv->x << ", " << uv->y << ", " << uv->z << ", " << uv->w << ", " << std::endl;
|
// std::cout << uv->x << ", " << uv->y << ", " << uv->z << ", " << uv->w << ", " << std::endl;
|
||||||
|
|
||||||
//Iterate through the vertices to adjust the texture coordinates to fit the atlas.
|
//Iterate through the vertices to adjust the texture coordinates to fit the textureAtlas.
|
||||||
for (int i = 0; i < vSize; i++) {
|
for (int i = 0; i < vSize; i++) {
|
||||||
Vertex* vertex = &vertices[i];
|
Vertex* vertex = &vertices[i];
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
//
|
||||||
|
// Created by aurailus on 14/12/18.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include "BlockChunk.h"
|
||||||
|
#include "../engine/helpers/ArrayTrans3D.h"
|
||||||
|
|
||||||
|
BlockChunk::BlockChunk(std::vector<int>* blocks) {
|
||||||
|
this->blocks = blocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
int BlockChunk::getBlock(glm::vec3* pos) {
|
||||||
|
unsigned int ind = ArrayTrans3D::vecToInd(pos);
|
||||||
|
if (ind < 0 || ind >= 4096) return -1;
|
||||||
|
return blocks->at(ind);
|
||||||
|
}
|
||||||
|
|
||||||
|
int BlockChunk::getBlock(int ind) {
|
||||||
|
if (ind < 0 || ind >= 4096) return -1;
|
||||||
|
return blocks->at((unsigned long)ind);
|
||||||
|
}
|
||||||
|
|
||||||
|
int BlockChunk::getBlock(int x, int y, int z) {
|
||||||
|
unsigned int ind = ArrayTrans3D::vecToInd(x, y, z);
|
||||||
|
if (ind < 0 || ind >= 4096) return -1;
|
||||||
|
return blocks->at(ind);
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
//
|
||||||
|
// Created by aurailus on 14/12/18.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef GLPROJECT_BLOCKCHUNK_H
|
||||||
|
#define GLPROJECT_BLOCKCHUNK_H
|
||||||
|
|
||||||
|
#include <vec3.hpp>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
class BlockChunk {
|
||||||
|
public:
|
||||||
|
BlockChunk() = default;
|
||||||
|
explicit BlockChunk(std::vector<int>* blocks);
|
||||||
|
|
||||||
|
int getBlock(int ind);
|
||||||
|
int getBlock(glm::vec3* pos);
|
||||||
|
int getBlock(int x, int y, int z);
|
||||||
|
|
||||||
|
//TODO: Add block setting methods as well
|
||||||
|
private:
|
||||||
|
std::vector<int>* blocks;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif //GLPROJECT_BLOCKCHUNK_H
|
|
@ -0,0 +1,27 @@
|
||||||
|
//
|
||||||
|
// Created by aurailus on 15/12/18.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef GLPROJECT_MESHCHUNK_H
|
||||||
|
#define GLPROJECT_MESHCHUNK_H
|
||||||
|
|
||||||
|
|
||||||
|
#include "BlockChunk.h"
|
||||||
|
#include "../engine/Entity.h"
|
||||||
|
#include "../mesh/MeshGenerator.h"
|
||||||
|
|
||||||
|
class MeshChunk : public Entity {
|
||||||
|
public:
|
||||||
|
MeshChunk();
|
||||||
|
explicit MeshChunk(BlockChunk* blockChunk);
|
||||||
|
|
||||||
|
void build(BlockAtlas* atlas);
|
||||||
|
|
||||||
|
bool isReady();
|
||||||
|
private:
|
||||||
|
bool ready;
|
||||||
|
BlockChunk* blockChunk;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //GLPROJECT_MESHCHUNK_H
|
|
@ -0,0 +1,36 @@
|
||||||
|
//
|
||||||
|
// Created by aurailus on 14/12/18.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "World.h"
|
||||||
|
|
||||||
|
World::World(BlockAtlas *atlas) {
|
||||||
|
blockAtlas = atlas;
|
||||||
|
}
|
||||||
|
|
||||||
|
void World::newChunk(glm::vec3* pos, BlockChunk *c) {
|
||||||
|
blockChunks.insert(std::pair<glm::vec3*, BlockChunk*>(pos, c));
|
||||||
|
meshGenQueue.insert(pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
void World::update() {
|
||||||
|
int done = 0;
|
||||||
|
while (!meshGenQueue.empty() && done < 8) {
|
||||||
|
auto it = meshGenQueue.begin();
|
||||||
|
meshGenQueue.erase(meshGenQueue.begin());
|
||||||
|
glm::vec3* pos = (*it);
|
||||||
|
|
||||||
|
auto meshChunk = new MeshChunk(blockChunks.at(pos));
|
||||||
|
meshChunk->build(blockAtlas);
|
||||||
|
meshChunk->setPosition(glm::vec3(pos->x * CHUNK_SIZE, pos->y * CHUNK_SIZE, pos->z * CHUNK_SIZE));
|
||||||
|
|
||||||
|
meshChunks.insert(std::pair<glm::vec3*, MeshChunk*>(pos, meshChunk));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void World::draw(GLint modelUni) {
|
||||||
|
for (auto &meshChunk : meshChunks) {
|
||||||
|
glUniformMatrix4fv(modelUni, 1, GL_FALSE, glm::value_ptr(meshChunk.second->getModelMatrix()));
|
||||||
|
meshChunk.second->draw();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,42 @@
|
||||||
|
//
|
||||||
|
// Created by aurailus on 14/12/18.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef GLPROJECT_WORLD_H
|
||||||
|
#define GLPROJECT_WORLD_H
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <vec3.hpp>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include <iostream>
|
||||||
|
#include <gtc/type_ptr.hpp>
|
||||||
|
|
||||||
|
#include "BlockChunk.h"
|
||||||
|
#include "MeshChunk.h"
|
||||||
|
#include "../blocks/BlockAtlas.h"
|
||||||
|
|
||||||
|
class World {
|
||||||
|
public:
|
||||||
|
World() = default;
|
||||||
|
explicit World(BlockAtlas* atlas);
|
||||||
|
|
||||||
|
void newChunk(glm::vec3* pos, BlockChunk* c);
|
||||||
|
|
||||||
|
void update();
|
||||||
|
void draw(GLint modelUni);
|
||||||
|
|
||||||
|
~World() = default;
|
||||||
|
private:
|
||||||
|
//Note to self:
|
||||||
|
//The same glm::vec3 pointer is used for the blockChunk and meshChunk maps. If discarding a block/meshChunk, keep
|
||||||
|
//this in mind. If this needs to be changed later change the 'update' method.
|
||||||
|
std::map<glm::vec3*, BlockChunk*> blockChunks;
|
||||||
|
std::map<glm::vec3*, MeshChunk*> meshChunks;
|
||||||
|
|
||||||
|
std::unordered_set<glm::vec3*> meshGenQueue;
|
||||||
|
|
||||||
|
BlockAtlas* blockAtlas;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif //GLPROJECT_WORLD_H
|
Binary file not shown.
After Width: | Height: | Size: 841 B |
Loading…
Reference in New Issue