1
0
Fork 0

Infinite box universe

This commit is contained in:
May B. 2020-07-10 19:49:16 +02:00
parent 2aff0cea95
commit 77d10611ae
84 changed files with 1448 additions and 133 deletions

12
TODO.md
View File

@ -1,21 +1,25 @@
# Features
## Data
- [ ] Hashed Octree
- [x] Generate noise
- [x] Density
- [ ] Octree world
- [ ] Serialize
- [ ] Generate noise
- [ ] In memory RLE
- [ ] Edition
- [ ] Planet
## Rendering
- [x] Render triangle
- [ ] Avoid texture noise
- [x] Avoid texture noise
- [x] MipMap LOD
- [ ] SRGB
- [ ] Skybox
- [ ] Better cheap planar
- [ ] Cascaded shadow maps
## Contouring
- [ ] Box contouring
- [x] Box contouring
- [ ] LOD
- [ ] Dual MC
- [ ] Collision

143
content/shaders/Main.fs Normal file
View File

@ -0,0 +1,143 @@
#version 330 core
// Ouput data
layout(location = 0) out vec3 color;
uniform sampler2DArray TextureAtlas;
uniform sampler2DArray NormalAtlas;
uniform sampler2DArray HOSAtlas;
uniform mat4 View;
in VertexData {
vec3 Position_worldspace;
float Material;
vec3 FaceNormal_modelspace;
#ifdef PBR
vec3 FaceNormal_worldspace;
vec3 EyeDirection_cameraspace;
vec3 LightDirection_cameraspace;
#endif
} vs;
vec3 expand(vec3 v) {
return (v - 0.5) * 2;
}
vec4 getTexture(sampler2DArray sample, vec2 UV) {
#ifdef BLEND
vec4 colx = texture(sample, vec3(UV, vs.Materials.x));
vec4 coly = texture(sample, vec3(UV, vs.Materials.y));
vec4 colz = texture(sample, vec3(UV, vs.Materials.z));
return colx * vs.MaterialRatio.x + coly * vs.MaterialRatio.y + colz * vs.MaterialRatio.z;
#else
return texture(sample, vec3(UV, vs.Material));
#endif
}
vec3 getTriTexture(sampler2DArray sample, vec2 crdx, vec2 crdy, vec2 crdz, vec3 weights) {
return getTexture(sample, crdx).rgb * weights.x +
getTexture(sample, crdy).rgb * weights.y +
getTexture(sample, crdz).rgb * weights.z;
}
void main() {
float texScale = .5;
#ifdef TRIPLANAR
// Triplanar
float plateauSize = 0.001;
float transitionSpeed = 2;
vec3 blendWeights = abs(vs.FaceNormal_modelspace);
blendWeights = blendWeights - plateauSize;
blendWeights = pow(max(blendWeights, 0), vec3(transitionSpeed));
vec2 UVx = vs.Position_worldspace.yz * texScale;
vec2 UVy = vs.Position_worldspace.zx * texScale;
vec2 UVz = vs.Position_worldspace.xy * texScale;
vec3 tex = getTriTexture(TextureAtlas, UVx, UVy, UVz, blendWeights);
#ifdef PBR
// Whiteout normal blend
vec3 texNx = expand(getTexture(NormalAtlas, UVx).rgb);
vec3 texNy = expand(getTexture(NormalAtlas, UVy).rgb);
vec3 texNz = expand(getTexture(NormalAtlas, UVz).rgb);
// Swizzle world normals into tangent space and apply Whiteout blend
texNx = vec3(texNx.xy + vs.FaceNormal_worldspace.zy, abs(texNx.z) * vs.FaceNormal_worldspace.x);
texNy = vec3(texNy.xy + vs.FaceNormal_worldspace.xz, abs(texNy.z) * vs.FaceNormal_worldspace.y);
texNz = vec3(texNz.xy + vs.FaceNormal_worldspace.xy, abs(texNz.z) * vs.FaceNormal_worldspace.z);
// Swizzle tangent normals to match world orientation and triblend
vec3 worldNormal = normalize(texNx.zyx * blendWeights.x + texNy.xzy * blendWeights.y +texNz.xyz * blendWeights.z);
vec3 texHOS = getTriTexture(HOSAtlas, UVx, UVy, UVz, blendWeights);
#endif
#else
// Cheap planar
vec3 blendWeights = abs(vs.FaceNormal_modelspace);
vec3 nrm = normalize(pow(blendWeights, vec3(80)));
vec2 UV = (vec2(vs.Position_worldspace.xy * nrm.z) + vec2(vs.Position_worldspace.yz * nrm.x) + vec2(vs.Position_worldspace.zx * nrm.y)) * texScale;
vec3 tex = getTexture(TextureAtlas, UV).rgb;
#ifdef PBR
vec3 texN = expand(getTexture(NormalAtlas, UV).rgb);
// Swizzle world normals into tangent space and apply Whiteout blend
// Swizzle tangent normals to match world orientation and triblend
vec3 worldNormal = normalize(vec3(texN.xy + vs.FaceNormal_worldspace.zy, abs(texN.z) * vs.FaceNormal_worldspace.x).zyx * blendWeights.x +
vec3(texN.xy + vs.FaceNormal_worldspace.xz, abs(texN.z) * vs.FaceNormal_worldspace.y).xzy * blendWeights.y +
vec3(texN.xy + vs.FaceNormal_worldspace.xy, abs(texN.z) * vs.FaceNormal_worldspace.z).xyz * blendWeights.z);
vec3 texHOS = getTexture(HOSAtlas, UV).rgb;
#endif
#endif
// Colors
#ifdef PBR
// Material properties
vec3 MaterialDiffuseColor = tex;
vec3 MaterialAmbientColor = vec3(.2) * MaterialDiffuseColor * texHOS.y;
vec3 MaterialSpecularColor = vec3(.3) * texHOS.z;
vec3 Normal_cameraspace = normalize((View * vec4(worldNormal,0)).xyz);
// Light emission properties
// You probably want to put them as uniforms
vec3 LightColor = vec3(1,1,1);
float LightPower = 1.5f;
// Distance to the light
float distance = 1.0f;//length( LightPosition_worldspace - Position_worldspace );
// Direction of the light (from the fragment to the light)
vec3 l = normalize(vs.LightDirection_cameraspace);
// Cosine of the angle between the normal and the light direction,
// clamped above 0
// - light is at the vertical of the triangle -> 1
// - light is perpendiular to the triangle -> 0
// - light is behind the triangle -> 0
float cosTheta = clamp(dot(Normal_cameraspace,l), 0,1 );
// Eye vector (towards the camera)
vec3 E = normalize(vs.EyeDirection_cameraspace);
// Direction in which the triangle reflects the light
vec3 R = reflect(-l,Normal_cameraspace);
// Cosine of the angle between the Eye vector and the Reflect vector,
// clamped to 0
// - Looking into the reflection -> 1
// - Looking elsewhere -> < 1
float cosAlpha = clamp( dot( E,R ), 0,1 );
float visibility=1.0;
// MAYBE: shadow
color =
// Ambient : simulates indirect lighting
MaterialAmbientColor +
// Diffuse : "color" of the object
visibility * MaterialDiffuseColor * LightColor * LightPower * cosTheta / (distance * distance) +
// Specular : reflective highlight, like a mirror
visibility * MaterialSpecularColor * LightColor * LightPower * pow(cosAlpha,5) / (distance * distance);
#else
color = tex;
#endif
}

41
content/shaders/Main.vs Normal file
View File

@ -0,0 +1,41 @@
#version 330 core
layout(location = 0) in vec3 Position_modelspace;
layout(location = 1) in uint Material_model;
layout(location = 2) in vec3 Normal_modelspace;
out VertexData {
vec3 Position_worldspace;
float Material;
vec3 FaceNormal_modelspace;
#ifdef PBR
vec3 FaceNormal_worldspace;
vec3 EyeDirection_cameraspace;
vec3 LightDirection_cameraspace;
#endif
} vs;
uniform mat4 MVP;
uniform mat4 Model;
uniform mat4 View;
uniform vec3 LightInvDirection_worldspace;
void main(){
gl_Position = MVP * vec4(Position_modelspace, 1);
vs.Position_worldspace = Position_modelspace;
vs.Material = float(Material_model);
vs.FaceNormal_modelspace = normalize(Normal_modelspace);
#ifdef PBR
vs.FaceNormal_worldspace = normalize((Model * vec4(vs.FaceNormal_modelspace, 0)).xyz);
// Vector that goes from the vertex to the camera, in camera space.
// In camera space, the camera is at the origin (0,0,0).
vs.EyeDirection_cameraspace = vec3(0,0,0) - (View * Model * vec4(Position_modelspace,1)).xyz;
// Vector that goes from the vertex to the light, in camera space
vs.LightDirection_cameraspace = (View * vec4(LightInvDirection_worldspace,0)).xyz;
#endif
}

View File

@ -1,8 +0,0 @@
#version 330 core
// Ouput data
layout(location = 0) out vec4 color;
void main(){
color = vec4(1, 0, 0, 0);
}

View File

@ -1,9 +0,0 @@
#version 330 core
layout(location = 0) in vec3 Position_modelspace;
uniform mat4 MVP;
void main(){
gl_Position = MVP * vec4(Position_modelspace,1);
}

BIN
content/textures/terrain/1024-realistic/Air.dds (Stored with Git LFS) Normal file

Binary file not shown.

BIN
content/textures/terrain/1024-realistic/Air.hos.dds (Stored with Git LFS) Normal file

Binary file not shown.

BIN
content/textures/terrain/1024-realistic/Air.nrm.dds (Stored with Git LFS) Normal file

Binary file not shown.

View File

@ -0,0 +1,32 @@
#pragma once
#include "../world/Chunk.hpp"
#include "boxing.hpp"
class FlatBox {
public:
FlatBox();
~FlatBox();
private:
static inline bool isTransparent(const Voxel *voxels, const std::optional<ushort>& idx) {
return idx.has_value() ? voxels[idx.value()].Density < UCHAR_MAX : true; // MAYBE: materials::transparent
}
public:
static void render(const Voxel *voxels, std::vector<VertexData> &vertices)
{
vertices.clear();
for (ushort i = 0; i < CHUNK_SIZE; i++) {
if (voxels[i].Density > 0) {
Faces faces = voxels[i].Density < UCHAR_MAX ? Faces::All :
(isTransparent(voxels, Chunk::getNeighborIdx(i, Face::Right)) & Faces::Right) |
(isTransparent(voxels, Chunk::getNeighborIdx(i, Face::Left)) & Faces::Left) |
(isTransparent(voxels, Chunk::getNeighborIdx(i, Face::Up)) & Faces::Up) |
(isTransparent(voxels, Chunk::getNeighborIdx(i, Face::Down)) & Faces::Down) |
(isTransparent(voxels, Chunk::getNeighborIdx(i, Face::Forward)) & Faces::Forward) |
(isTransparent(voxels, Chunk::getNeighborIdx(i, Face::Backward)) & Faces::Backward);
contouring::box::addCube(vertices, Chunk::getPosition(i), voxels[i].Material, faces, glm::vec3(voxels[i].Density * 1.f / UCHAR_MAX));
}
}
}
};

52
src/contouring/boxing.hpp Normal file
View File

@ -0,0 +1,52 @@
#pragma once
#include <vector>
#include <glm/gtc/matrix_transform.hpp>
#include "../data/geometry/Faces.hpp"
#include "../render/buffer/VertexData.hpp"
namespace contouring::box {
static const auto BOX_FACES = 6;
static const std::vector<glm::vec3> g_quad_vertices = {
glm::vec3(0.5f, 0.5f, 0.5f), glm::vec3(0.5f, -0.5f, 0.5f), glm::vec3(0.5f, -0.5f, -0.5f),
glm::vec3(0.5f, -0.5f, -0.5f), glm::vec3(0.5f, 0.5f, -0.5f), glm::vec3(0.5f, 0.5f, 0.5f)};
static const glm::vec3 g_cube_normals[BOX_FACES] = {
glm::vec3(1, 0, 0), glm::vec3(-1, 0, 0), glm::vec3(0, 1, 0), glm::vec3(0, -1, 0), glm::vec3(0, 0, 1), glm::vec3(0, 0, -1)};
static const glm::mat4 g_cube_rotate[BOX_FACES] = {
glm::mat4(1),
glm::rotate<float>(glm::mat4(1), glm::pi<float>(), glm::vec3(0, 1, 0)),
glm::rotate<float>(glm::mat4(1), glm::half_pi<float>(), glm::vec3(0, 0, 1)),
glm::rotate<float>(glm::mat4(1), glm::half_pi<float>(), glm::vec3(0, 0, -1)),
glm::rotate<float>(glm::mat4(1), glm::half_pi<float>(), glm::vec3(0, -1, 0)),
glm::rotate<float>(glm::mat4(1), glm::half_pi<float>(), glm::vec3(0, 1, 0)),
};
static void addQuad(std::vector<VertexData> & out, glm::vec3 position, ushort material, Face face, glm::vec3 size) {
for (auto vertex : g_quad_vertices) {
out.push_back(VertexData{glm::vec3(g_cube_rotate[static_cast<int>(face)] * glm::vec4(vertex, 1)) * size + position, material, g_cube_normals[static_cast<int>(face)]});
}
}
static void addCube(std::vector<VertexData>& out, glm::vec3 position, uint material, Faces faces = Faces::All, glm::vec3 size = glm::vec3(1)) {
if (faces && Faces::Right)
addQuad(out, position, material, Face::Right, size);
if(faces && Faces::Left)
addQuad(out, position, material, Face::Left, size);
if (faces && Faces::Up)
addQuad(out, position, material, Face::Up, size);
if (faces && Faces::Down)
addQuad(out, position, material, Face::Down, size);
if (faces && Faces::Forward)
addQuad(out, position, material, Face::Forward, size);
if (faces && Faces::Backward)
addQuad(out, position, material, Face::Backward, size);
}
}

View File

@ -0,0 +1,28 @@
#pragma once
enum class Face {
Right, Left, Up, Down, Forward, Backward
};
enum class Faces {
None = 0,
Right = 1,
Left = 2,
Up = 4,
Down = 8,
Forward = 16,
Backward = 32,
All = 63
};
inline Faces operator|(Faces a, Faces b) {
return static_cast<Faces>(static_cast<int>(a) | static_cast<int>(b));
}
inline Faces operator&(Faces a, Faces b) {
return static_cast<Faces>(static_cast<int>(a) & static_cast<int>(b));
}
inline Faces operator&(bool a, Faces b) {
return (a ? Faces::All : Faces::None) & b;
}
inline bool operator&&(Faces a, Faces b) {
return static_cast<int>(a) & static_cast<int>(b);
}

View File

@ -5,6 +5,36 @@
namespace glm {
typedef vec<3, long long> lvec3;
typedef vec<4, long long> lvec4;
}
typedef glm::vec3 camera_pos;
typedef glm::lvec3 voxel_pos;
typedef glm::ivec3 chunk_pos;
typedef glm::vec<3, ushort> chunk_voxel_pos;
namespace glm {
ivec3 inline iround(const vec3& p) {
return ivec3(std::round<int>(p.x), std::round<int>(p.y), std::round<int>(p.z));
}
int inline length2(const ivec3& a) {
return a.x * a.x + a.y * a.y + a.z * a.z;
}
ivec3 inline diff(const ivec3& a, const ivec3& b) {
return glm::abs(glm::abs(a) - glm::abs(b));
}
uint inline rem(long long value, uint m) {
return value < 0 ? ((value+1) % (long long)m) + m - 1 : value % (long long)m;
}
int inline div(long long value, uint m) {
return value < 0 ? ((value+1) / (long long)m) - 1 : value / (long long)m;
}
chunk_voxel_pos inline modulo(const voxel_pos& value, const chunk_voxel_pos& m) {
return chunk_voxel_pos(rem(value.x, m.x), rem(value.y, m.y), rem(value.z, m.z));
}
chunk_pos inline divide(const voxel_pos &value, const chunk_voxel_pos &m)
{
return chunk_pos(div(value.x, m.x), div(value.y, m.y), div(value.z, m.z));
}
}

View File

@ -0,0 +1,76 @@
#ifndef SAFE_UNIQUE_QUEUE_HPP
#define SAFE_UNIQUE_QUEUE_HPP
#include <queue>
#include <unordered_set>
#include <mutex>
#include <condition_variable>
/// Thread safe queue discarding duplicate values
template <class T>
class safe_unique_queue {
private:
std::queue<T> queue;
std::unordered_set<T> set;
std::mutex mutex;
std::condition_variable cv;
public:
bool push(const T& in) {
std::unique_lock<std::mutex> lock(mutex);
if(set.insert(in).second) {
queue.push(in);
cv.notify_one();
return true;
}
return false;
}
bool pop(T& out) {
std::unique_lock<std::mutex> lock(mutex);
if (queue.empty())
return false;
out = queue.front();
set.erase(out);
queue.pop();
return true;
}
bool take(T& out) {
std::unique_lock<std::mutex> lock(mutex);
if (queue.empty())
return false;
out = queue.front();
queue.pop();
return true;
}
void release(const T& taken) {
std::unique_lock<std::mutex> lock(mutex);
set.erase(taken);
}
bool empty() {
std::unique_lock<std::mutex> lock(mutex);
return queue.empty();
}
size_t size() {
std::unique_lock<std::mutex> lock(mutex);
return set.size();
}
void notify() {
cv.notify_all();
}
void wait() {
std::unique_lock<std::mutex> lock(mutex);
if(queue.empty())
cv.wait(lock);
}
};
#endif

View File

@ -8,6 +8,8 @@
#include <optional>
#include "glm.hpp"
#include "circular_buffer.hpp"
#include "../render/Renderer.hpp"
#include "../world/World.hpp"
/// Savable game options
struct options {
@ -17,13 +19,13 @@ struct options {
int samples = -1;
bool fullscreen = false;
ImVec4 clear_color = ImColor(85, 56, 104);
bool renderer_triplanar = false;
bool renderer_blend = false;
bool renderer_fog = true;
bool renderer_wireframe = false;
Renderer::options renderer;
bool debug_wireframe = false;
bool culling = true;
bool show_debug_world = false;
World::options world;
float voxelSize = 1;
bool show_debug_contouring = false;
int contouring_idx = 0;
@ -56,9 +58,5 @@ struct reports {
circular_buffer<float> swap = circular_buffer<float>(REPORT_BUFFER_SIZE, 0);
circular_buffer<float> wait = circular_buffer<float>(REPORT_BUFFER_SIZE, 0);
} main;
struct world {
circular_buffer<float> chunk_count = circular_buffer<float>(REPORT_BUFFER_SIZE, 0); // MAYBE: store int
circular_buffer<float> chunk_load = circular_buffer<float>(REPORT_BUFFER_SIZE, 0);
circular_buffer<float> chunk_unload = circular_buffer<float>(REPORT_BUFFER_SIZE, 0);
} world;
World::report world;
};

39
src/data/unique_queue.hpp Normal file
View File

@ -0,0 +1,39 @@
#ifndef UNIQUE_QUEUE_HPP
#define UNIQUE_QUEUE_HPP
#include <queue>
#include <unordered_set>
#include <cassert>
/// Queue discarding duplicate values
/// @note not thread safe
template <class T>
struct unique_queue {
std::queue<T> queue;
std::unordered_set<T> set;
bool push(T in) {
if(set.insert(in).second) {
queue.push(in);
return true;
}
return false;
}
T pop() {
const auto out = queue.front();
set.erase(out);
queue.pop();
return out;
}
bool empty() const {
return queue.empty();
}
size_t size() const {
return queue.size();
}
};
#endif

View File

@ -17,8 +17,9 @@
#include "control/InputMap.hpp"
#include "control/Camera.hpp"
#include "render/pass/SimpleProgram.hpp"
#include "render/buffer/VertexBuffer.hpp"
#include "render/Renderer.hpp"
#include "world/World.hpp"
#include "contouring/FlatBox.hpp"
#include "data/state.h"
@ -38,24 +39,15 @@ int main(int, char *[]){
InputMap inputs(window);
Camera camera(window, inputs);
SimpleProgram *testPass = new SimpleProgram();
Renderer *renderer = new Renderer(options.renderer);
UI::setup(window);
static const GLfloat g_vertex_buffer_data[] = {
-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, -1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
};
Buffer *quadBuffer = new VertexBuffer(GL_TRIANGLES, 6, sizeof(g_vertex_buffer_data), g_vertex_buffer_data);
GLuint aimTexture = Program::loadTexture("ui/Aim", false);
GLuint VertexArrayID;
glGenVertexArrays(1, &VertexArrayID);
glBindVertexArray(VertexArrayID);
const auto model = glm::scale(glm::mat4(1), glm::vec3(2));
renderer->LightInvDir = glm::vec3(-0.5f, 2, -2);
World world = World(options.world);
do {
const double startTime = glfwGetTime();
@ -65,19 +57,16 @@ int main(int, char *[]){
inputs.toggle(options.show_debug_menu, Input::Debug);
camera.update(state.capture_mouse, !UI::isFocus());
/*renderer.update(camera);
look_at = world.raycast(camera.getViewRay() / scale);
renderer->lookFrom(camera);
/*look_at = world.raycast(camera.getViewRay() / scale);
if (capture_mouse && look_at.has_value()) {
if (inputs.isPressing(Mouse::Left))
world.setCube(look_at.value().first, 2, Voxel{0, 0});
else if (inputs.isPressing(Mouse::Right))
world.setCube(look_at.value().first, 2, Voxel{2, 1});
}
const auto report = world.update(camera.getPosition() / scale);
chunk_count_tracker.push(report.count);
chunk_load_tracker.push(report.load);
chunk_unload_tracker.push(report.unload);*/
}*/
world.update(camera.getPosition() / options.voxelSize, reports.world);
inputs.saveKeys();
reports.main.update.push((glfwGetTime() - partTime) * 1000);
}
@ -100,18 +89,29 @@ int main(int, char *[]){
if(actions && UI::Actions::ClearColor) {
glClearColor(options.clear_color.x, options.clear_color.y, options.clear_color.z, options.clear_color.w);
}
if(actions && UI::Actions::RendererSharders) {
renderer->reloadShaders(options.renderer.main);
}
if(actions && UI::Actions::RendererTextures) {
renderer->reloadTextures(options.renderer.textures, options.renderer.mipMapLOD);
}
if(actions && UI::Actions::World) {
world.setOptions(options.world);
}
}
{ // Rendering
const double partTime = glfwGetTime();
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
testPass->useIt();
const auto mvp = camera.getProjectionMatrix() * camera.getViewMatrix() * glm::mat4(1.0f);
testPass->setMVP(&mvp[0][0]);
quadBuffer->draw(Buffer::params{.vertexOnly = false});
auto pass = renderer->getPass();
pass.start();
reports.main.tris_count = 0;
std::vector<std::pair<glm::mat4, Buffer *>> models;
world.getModels(models, options.voxelSize);
reports.main.models_count = 0;
reports.main.tris_count = 0;
for (auto [model, buffer] : models) {
reports.main.models_count++;
reports.main.tris_count += buffer->draw(pass.setup(model));
}
UI::render();
reports.main.render.push((glfwGetTime() - partTime) * 1000);
@ -139,6 +139,7 @@ int main(int, char *[]){
glfwWindowShouldClose(window) == 0);
UI::unload();
delete renderer;
// Close OpenGL window and terminate GLFW
glfwTerminate();

52
src/render/Renderer.cpp Normal file
View File

@ -0,0 +1,52 @@
#include "Renderer.hpp"
#include "../world/materials.hpp"
#include "../control/Camera.hpp"
Renderer::Renderer(const Renderer::options& options) {
glGenVertexArrays(1, &VertexArrayID);
glBindVertexArray(VertexArrayID);
MainPass = new MainProgram(options.main);
loadTextures(options.textures, options.mipMapLOD);
}
Renderer::~Renderer() {
unloadTextures();
delete MainPass;
glDeleteVertexArrays(1, &VertexArrayID);
}
PassContext Renderer::getPass() {
return PassContext(this, MainPass);
}
void Renderer::reloadShaders(const MainProgram::options& options) {
delete MainPass;
MainPass = new MainProgram(options);
}
void Renderer::reloadTextures(const std::string& texturePath, float mipMapLOD) {
unloadTextures();
loadTextures(texturePath, mipMapLOD);
}
void Renderer::unloadTextures() {
glDeleteTextures(1, &HOSAtlas);
glDeleteTextures(1, &NormalAtlas);
glDeleteTextures(1, &TextureAtlas);
}
void Renderer::loadTextures(const std::string& texturePath, float mipMapLOD) {
std::vector<std::string> textures;
for(const auto texture: materials::textures) {
textures.push_back("terrain/" + texturePath + "/" + texture);
}
TextureAtlas = Program::loadTextureArray(textures, "", mipMapLOD);
NormalAtlas = Program::loadTextureArray(textures, ".nrm", mipMapLOD);
HOSAtlas = Program::loadTextureArray(textures, ".hos", mipMapLOD);
}
void Renderer::lookFrom(const Camera& camera) {
ProjectionMatrix = camera.getProjectionMatrix();
ViewMatrix = camera.getViewMatrix();
}

64
src/render/Renderer.hpp Normal file
View File

@ -0,0 +1,64 @@
#pragma once
#include <GL/glew.h>
#include "pass/MainProgram.hpp"
#include "pass/PassContext.hpp"
class Camera;
/// Handle rendering passes and params
class Renderer {
public:
struct options {
MainProgram::options main;
bool wireframe = false;
std::string textures = "1024-realistic";
float mipMapLOD = -.5;
};
Renderer(const options&);
Renderer(Renderer &&) = delete;
Renderer(const Renderer &) = delete;
Renderer &operator=(Renderer &&) = delete;
Renderer &operator=(const Renderer &) = delete;
~Renderer();
glm::vec3 LightInvDir = glm::vec3(0.5f, 2, 2);
glm::mat4 getProjectionMatrix() const {
return ProjectionMatrix;
}
glm::mat4 getViewMatrix() const {
return ViewMatrix;
}
GLuint getTextureAtlas() const {
return TextureAtlas;
}
GLuint getNormalAtlas() const {
return NormalAtlas;
}
GLuint getHOSAtlas() const {
return HOSAtlas;
}
PassContext getPass();
void lookFrom(const Camera&);
void reloadShaders(const MainProgram::options&);
void reloadTextures(const std::string &, float mipMapLOD = 0);
private:
GLuint VertexArrayID;
MainProgram *MainPass;
glm::mat4 ProjectionMatrix;
glm::mat4 ViewMatrix;
GLuint TextureAtlas;
GLuint NormalAtlas;
GLuint HOSAtlas;
void loadTextures(const std::string &, float mipMapLOD = 0);
void unloadTextures();
};

View File

@ -65,33 +65,35 @@ UI::Actions UI::draw(options &options, state &state, const reports &reports, GLu
{
bool changeRenderer = false;
changeRenderer |= ImGui::Checkbox("Triplanar", &options.renderer_triplanar);
changeRenderer |= ImGui::Checkbox("PBR", &options.renderer.main.pbr);
ImGui::SameLine();
changeRenderer |= ImGui::Checkbox("Blend", &options.renderer_blend);
ImGui::SameLine();
changeRenderer |= ImGui::Checkbox("Fog", &options.renderer_fog);
changeRenderer |= ImGui::Checkbox("Triplanar", &options.renderer.main.triplanar);
if (changeRenderer) {
//TODO:
actions = actions | Actions::RendererSharders;
}
if (ImGui::Checkbox("Wireframe", &options.renderer_wireframe))
glPolygonMode(GL_FRONT_AND_BACK, options.renderer_wireframe ? GL_LINE : GL_FILL);
}
if (ImGui::Checkbox("Wireframe", &options.debug_wireframe))
glPolygonMode(GL_FRONT_AND_BACK, options.debug_wireframe ? GL_LINE : GL_FILL);
ImGui::Checkbox("Culling", &options.culling);
ImGui::Text("Textures '%s'", options.renderer.textures.c_str()); // MAYBE: select
if (ImGui::SliderFloat("LOD", &options.renderer.mipMapLOD, -1, 1)) {
actions = actions | Actions::RendererTextures;
}
ImGui::End();
}
/*if (show_debug_world) {
ImGui::Begin("Debug: World", &show_debug_world, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::PlotHistogram("Count", chunk_count_tracker.buffer.get(), chunk_count_tracker.size, 0, std::to_string(chunk_count_tracker.current()).c_str(), 0);
ImGui::PlotHistogram("Loading", chunk_load_tracker.buffer.get(), chunk_load_tracker.size, 0, std::to_string(chunk_load_tracker.current()).c_str(), 0);
ImGui::PlotHistogram("Saving", chunk_unload_tracker.buffer.get(), chunk_unload_tracker.size, 0, std::to_string(chunk_unload_tracker.current()).c_str(), 0);
if (options.show_debug_world) {
ImGui::Begin("Debug: World", &options.show_debug_world, ImGuiWindowFlags_AlwaysAutoResize);
ImGui::PlotHistogram("Count", reports.world.chunk_count.buffer.get(), reports.world.chunk_count.size, 0, std::to_string(reports.world.chunk_count.current()).c_str(), 0);
ImGui::PlotHistogram("Loading", reports.world.chunk_load.buffer.get(), reports.world.chunk_load.size, 0, std::to_string(reports.world.chunk_load.current()).c_str(), 0);
ImGui::PlotHistogram("Saving", reports.world.chunk_unload.buffer.get(), reports.world.chunk_unload.size, 0, std::to_string(reports.world.chunk_unload.current()).c_str(), 0);
ImGui::Separator();
if (ImGui::SliderInt("Load distance", &world.loadDistance, 1, world.keepDistance) |
ImGui::SliderInt("Keep distance", &world.keepDistance, world.loadDistance + 1, 21)) {
world.triggerChunkChange();
if (ImGui::SliderInt("Load distance", &options.world.loadDistance, 1, options.world.keepDistance) |
ImGui::SliderInt("Keep distance", &options.world.keepDistance, options.world.loadDistance + 1, 21)) {
actions = actions | Actions::World;
}
ImGui::End();
}*/
}
/*if (show_debug_contouring) {
ImGui::Begin("Debug: Contouring", &show_debug_contouring, ImGuiWindowFlags_AlwaysAutoResize);

View File

@ -10,7 +10,10 @@ namespace UI {
None = 0,
FPS = 1 << 0,
FullScreen = 1 << 1,
ClearColor = 1 << 2
ClearColor = 1 << 2,
RendererSharders = 1 << 3,
RendererTextures = 1 << 4,
World = 1 << 5,
};
inline Actions operator|(Actions a, Actions b) {
return static_cast<Actions>(static_cast<int>(a) | static_cast<int>(b));

View File

@ -3,8 +3,6 @@
#include <GL/glew.h>
#include <sys/types.h>
class PassParams;
/// Abstract OpenGL Buffer
class Buffer {
public:

View File

@ -0,0 +1,108 @@
#include "ShortIndexedBuffer.hpp"
#include "vboindexer.hpp"
ShortIndexedBuffer::Data::Data(const std::vector<VertexData> &vs, const std::vector<GLushort> &indices): indices(indices) {
for (auto vertex : vs) {
vertices.push_back(vertex.Position);
materials.push_back(vertex.Material);
normals.push_back(vertex.Normal);
}
}
void ShortIndexedBuffer::Data::index(const std::vector<VertexData>& vs) {
indexVBO(vs, indices, vertices, materials, normals);
}
ShortIndexedBuffer::ShortIndexedBuffer(GLenum shape, const std::vector<VertexData> &vertices): Buffer(shape) {
setData(ShortIndexedBuffer::Data(vertices));
}
ShortIndexedBuffer::ShortIndexedBuffer(GLenum shape, const std::vector<VertexData> &vertices, const std::vector<GLushort>& indices): Buffer(shape) {
setData(ShortIndexedBuffer::Data(vertices, indices));
}
ShortIndexedBuffer::ShortIndexedBuffer(GLenum shape, const ShortIndexedBuffer::Data &data): Buffer(shape) {
setData(data);
}
ShortIndexedBuffer::~ShortIndexedBuffer() {
glDeleteBuffers(1, &NormalBufferID);
glDeleteBuffers(1, &MaterialBufferID);
glDeleteBuffers(1, &IndexBufferID);
}
void ShortIndexedBuffer::enableAllAttribs() {
enableVertexAttrib();
glEnableVertexAttribArray(1);
glBindBuffer(GL_ARRAY_BUFFER, MaterialBufferID);
glVertexAttribIPointer(
1, // attribute
1, // size
GL_UNSIGNED_SHORT, // type
0, // stride
(void *)0 // array buffer offset
);
glEnableVertexAttribArray(2);
glBindBuffer(GL_ARRAY_BUFFER, NormalBufferID);
glVertexAttribPointer(
2, // attribute
3, // size
GL_FLOAT, // type
GL_FALSE, // normalized?
0, // stride
(void *)0 // array buffer offset
);
}
void ShortIndexedBuffer::disableAllAttribs() {
glDisableVertexAttribArray(2);
glDisableVertexAttribArray(1);
disableVertexAttrib();
}
void ShortIndexedBuffer::enableIndex() {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, IndexBufferID);
}
uint ShortIndexedBuffer::draw(Buffer::params params) {
if(IndexSize == 0)
return 0;
if(params.vertexOnly) {
enableVertexAttrib();
} else {
enableAllAttribs();
}
enableIndex();
glDrawElements(Shape, IndexSize, GL_UNSIGNED_SHORT, (void *)0);
if(params.vertexOnly) {
disableVertexAttrib();
} else {
disableAllAttribs();
}
return IndexSize;
}
void ShortIndexedBuffer::setData(const ShortIndexedBuffer::Data& data) {
glGenBuffers(1, &IndexBufferID);
glGenBuffers(1, &MaterialBufferID);
glGenBuffers(1, &NormalBufferID);
IndexSize = data.indices.size();
setIndicies(IndexSize * sizeof(GLushort), &data.indices[0]);
setVertices(data.vertices.size() * sizeof(glm::vec3), &data.vertices[0]);
setMaterials(data.materials.size() * sizeof(GLushort), &data.materials[0]);
setNormals(data.normals.size() * sizeof(glm::vec3), &data.normals[0]);
}
void ShortIndexedBuffer::setIndicies(const unsigned long size, const void *data) {
glBindBuffer(GL_ARRAY_BUFFER, IndexBufferID);
glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
}
void ShortIndexedBuffer::setMaterials(const unsigned long size, const void *data) {
glBindBuffer(GL_ARRAY_BUFFER, MaterialBufferID);
glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
}
void ShortIndexedBuffer::setNormals(const unsigned long size, const void *data) {
glBindBuffer(GL_ARRAY_BUFFER, NormalBufferID);
glBufferData(GL_ARRAY_BUFFER, size, data, GL_STATIC_DRAW);
}

View File

@ -0,0 +1,59 @@
#pragma once
#include <GL/glew.h>
#include <glm/glm.hpp>
#include <vector>
#include "Buffer.hpp"
#include "VertexData.hpp"
/// OpenGL VertexBuffer with IndexBuffer
class ShortIndexedBuffer: public Buffer {
public:
struct Data {
std::vector<GLushort> indices;
std::vector<glm::vec3> vertices;
std::vector<GLushort> materials;
std::vector<glm::vec3> normals;
Data() { }
Data(const std::vector<VertexData> &vertices, const std::vector<GLushort> &indices);
Data(const std::vector<VertexData> &vertices) { index(vertices); }
void index(const std::vector<VertexData> &vertices);
bool empty() const {
return indices.empty();
}
void clear() {
indices.clear();
vertices.clear();
materials.clear();
normals.clear();
}
};
ShortIndexedBuffer(GLenum shape, const typename std::vector<VertexData> &vertices);
ShortIndexedBuffer(GLenum shape, const typename std::vector<VertexData> &vertices, const typename std::vector<GLushort> &indices);
ShortIndexedBuffer(GLenum shape, const typename ShortIndexedBuffer::Data &data);
virtual ~ShortIndexedBuffer();
void enableAllAttribs();
void disableAllAttribs();
void enableIndex();
uint draw(Buffer::params params) override;
private:
GLuint IndexBufferID;
GLushort IndexSize = 0;
GLuint MaterialBufferID;
GLuint NormalBufferID;
void setData(const ShortIndexedBuffer::Data &data);
void setIndicies(const unsigned long size, const void *data);
void setMaterials(const unsigned long size, const void *data);
void setNormals(const unsigned long size, const void *data);
};

View File

@ -0,0 +1,18 @@
#ifndef VERTEX_DATA_HPP
#define VERTEX_DATA_HPP
#include <glm/glm.hpp>
#include <string.h> // for memcmp
#include <GL/glew.h>
/// Vertex properties
struct VertexData {
glm::vec3 Position;
GLushort Material;
glm::vec3 Normal;
bool operator<(const VertexData that) const {
return memcmp((void *)this, (void *)&that, sizeof(VertexData)) > 0;
};
};
#endif

View File

@ -0,0 +1,136 @@
#include <vector>
#include <map>
#include <glm/glm.hpp>
#include "vboindexer.hpp"
bool getSimilarVertexIndex_fast(
const VertexData &packed,
std::map<VertexData, GLushort> &VertexToOutIndex,
GLushort &result)
{
std::map<VertexData, GLushort>::iterator it = VertexToOutIndex.find(packed);
if ( it == VertexToOutIndex.end() ){
return false;
}else{
result = it->second;
return true;
}
}
bool getSimilarVertexIndex_fast(
const VertexData &packed,
std::map<VertexData, unsigned int> &VertexToOutIndex,
unsigned int &result)
{
std::map<VertexData, unsigned int>::iterator it = VertexToOutIndex.find(packed);
if ( it == VertexToOutIndex.end() ){
return false;
}else{
result = it->second;
return true;
}
}
void indexVBO(
std::vector<glm::vec3> &in_vertices,
std::vector<GLushort> &in_materials,
std::vector<glm::vec3> &in_normals,
std::vector<unsigned int> &out_indices,
std::vector<glm::vec3> &out_vertices,
std::vector<GLushort> &out_materials,
std::vector<glm::vec3> &out_normals)
{
std::map<VertexData,unsigned int> VertexToOutIndex;
// For each input vertex
for ( unsigned int i=0; i<in_vertices.size(); i++ ){
VertexData packed = {in_vertices[i], in_materials[i], in_normals[i]};
// Try to find a similar vertex in out_XXXX
unsigned int index;
bool found = getSimilarVertexIndex_fast( packed, VertexToOutIndex, index);
if ( found ){ // A similar vertex is already in the VBO, use it instead !
out_indices.push_back( index );
}else{ // If not, it needs to be added in the output data.
out_vertices.push_back( in_vertices[i]);
out_materials.push_back( in_materials[i]);
out_normals.push_back( in_normals[i]);
unsigned int newindex = (unsigned int)out_vertices.size() - 1;
out_indices.push_back( newindex );
VertexToOutIndex[ packed ] = newindex;
}
}
}
void indexVBO(
const std::vector<VertexData> &in_vertices,
std::vector<GLushort> &out_indices,
std::vector<glm::vec3> &out_vertices,
std::vector<GLushort> &out_materials,
std::vector<glm::vec3> &out_normals)
{
std::map<VertexData, GLushort> VertexToOutIndex;
// For each input vertex
for (size_t i=0; i<in_vertices.size(); i++ ){
// Try to find a similar vertex in out_XXXX
GLushort index;
bool found = getSimilarVertexIndex_fast(in_vertices[i], VertexToOutIndex, index);
if ( found ){ // A similar vertex is already in the VBO, use it instead !
out_indices.push_back( index );
}else{ // If not, it needs to be added in the output data.
out_vertices.push_back( in_vertices[i].Position);
out_materials.push_back( in_vertices[i].Material);
out_normals.push_back( in_vertices[i].Normal);
GLushort newindex = (GLushort)out_vertices.size() - 1;
out_indices.push_back( newindex );
VertexToOutIndex[ in_vertices[i] ] = newindex;
}
}
}
/*
void indexVBO_TBN(
std::vector<glm::vec3> & in_vertices,
std::vector<glm::vec2> & in_uvs,
std::vector<glm::vec3> & in_normals,
std::vector<glm::vec3> & in_tangents,
std::vector<glm::vec3> & in_bitangents,
std::vector<unsigned int> & out_indices,
std::vector<glm::vec3> & out_vertices,
std::vector<glm::vec2> & out_uvs,
std::vector<glm::vec3> & out_normals,
std::vector<glm::vec3> & out_tangents,
std::vector<glm::vec3> & out_bitangents
){
// For each input vertex
for ( unsigned int i=0; i<in_vertices.size(); i++ ){
// Try to find a similar vertex in out_XXXX
unsigned int index;
bool found = getSimilarVertexIndex(in_vertices[i], in_uvs[i], in_normals[i], out_vertices, out_uvs, out_normals, index);
if ( found ){ // A similar vertex is already in the VBO, use it instead !
out_indices.push_back( index );
// Average the tangents and the bitangents
out_tangents[index] += in_tangents[i];
out_bitangents[index] += in_bitangents[i];
}else{ // If not, it needs to be added in the output data.
out_vertices.push_back( in_vertices[i]);
out_uvs .push_back( in_uvs[i]);
out_normals .push_back( in_normals[i]);
out_tangents .push_back( in_tangents[i]);
out_bitangents .push_back( in_bitangents[i]);
out_indices .push_back( (unsigned int)out_vertices.size() - 1 );
}
}
}
*/

View File

@ -0,0 +1,27 @@
#ifndef VBOINDEXER_HPP
#define VBOINDEXER_HPP
#include <vector>
#include <glm/glm.hpp>
#include "VertexData.hpp"
#include <GL/glew.h>
void indexVBO(
std::vector<glm::vec3> &in_vertices,
std::vector<GLushort> &in_materials,
std::vector<glm::vec3> &in_normals,
std::vector<unsigned int> &out_indices,
std::vector<glm::vec3> &out_vertices,
std::vector<GLushort> &out_materials,
std::vector<glm::vec3> &out_normals);
void indexVBO(
const std::vector<VertexData> &in_vertices,
std::vector<GLushort> &out_indices,
std::vector<glm::vec3> &out_vertices,
std::vector<GLushort> &out_materials,
std::vector<glm::vec3> &out_normals);
#endif

View File

@ -0,0 +1,76 @@
#include "MainProgram.hpp"
#include "../Renderer.hpp"
MainProgram::MainProgram(const MainProgram::options& opts): Program() {
std::vector<std::string> flags;
if(opts.pbr)
flags.push_back("PBR");
if(opts.triplanar)
flags.push_back("TRIPLANAR");
std::vector<Shader*> shaders;
shaders.push_back(loadShader(GL_VERTEX_SHADER, flags));
shaders.push_back(loadShader(GL_FRAGMENT_SHADER, flags));
load(shaders);
MVPMatrixID = glGetUniformLocation(ProgramID, "MVP");
ModelMatrixID = glGetUniformLocation(ProgramID, "Model");
ViewMatrixID = glGetUniformLocation(ProgramID, "View");
TextureID = glGetUniformLocation(ProgramID, "TextureAtlas");
NormalID = glGetUniformLocation(ProgramID, "NormalAtlas");
HOSID = glGetUniformLocation(ProgramID, "HOSAtlas");
LightInvDirID = glGetUniformLocation(ProgramID, "LightInvDirection_worldspace");
}
MainProgram::~MainProgram() { }
std::string MainProgram::getName() const {
return "Main";
}
void MainProgram::start(Renderer *renderer) {
bindTexture(renderer->getTextureAtlas());
bindNormal(renderer->getNormalAtlas());
bindHOS(renderer->getHOSAtlas());
setLightInvDir(&renderer->LightInvDir[0]);
}
Buffer::params MainProgram::setup(Renderer *renderer, glm::mat4 modelMatrix) {
setModel(&modelMatrix[0][0]);
setView(&renderer->getViewMatrix()[0][0]);
const auto mvp = renderer->getProjectionMatrix() * renderer->getViewMatrix() * modelMatrix;
setMVP(&mvp[0][0]);
return Buffer::params{.vertexOnly = false};
}
void MainProgram::setMVP(const GLfloat *matrix) {
glUniformMatrix4fv(MVPMatrixID, 1, GL_FALSE, matrix);
}
void MainProgram::setModel(const GLfloat *matrix) {
glUniformMatrix4fv(ModelMatrixID, 1, GL_FALSE, matrix);
}
void MainProgram::setView(const GLfloat *matrix) {
glUniformMatrix4fv(ViewMatrixID, 1, GL_FALSE, matrix);
}
void MainProgram::bindTexture(GLuint textureID) {
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D_ARRAY, textureID);
glUniform1i(TextureID, 0);
}
void MainProgram::bindNormal(GLuint textureID) {
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D_ARRAY, textureID);
glUniform1i(NormalID, 1);
}
void MainProgram::bindHOS(GLuint textureID) {
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D_ARRAY, textureID);
glUniform1i(HOSID, 2);
}
void MainProgram::setLightInvDir(const GLfloat *pos) {
glUniform3fv(LightInvDirID, 1, pos);
}

View File

@ -0,0 +1,42 @@
#pragma once
#include "Program.hpp"
/// Final pass
class MainProgram: public Program {
public:
struct options {
bool pbr = true;
bool triplanar = false;
//TODO: bool blend = false;
//TODO: bool fog = true;
};
MainProgram(const options &opts);
~MainProgram();
std::string getName() const override;
void start(Renderer *) override;
Buffer::params setup(Renderer *, glm::mat4 modelMatrix) override;
void setMVP(const GLfloat *matrix);
void setModel(const GLfloat *matrix);
void setView(const GLfloat *matrix);
void bindTexture(const GLuint textureID);
void bindNormal(const GLuint textureID);
void bindHOS(const GLuint textureID);
void setLightInvDir(const GLfloat *pos);
private:
GLuint MVPMatrixID;
GLuint ModelMatrixID;
GLuint ViewMatrixID;
GLuint TextureID;
GLuint NormalID;
GLuint HOSID;
GLuint LightInvDirID;
};

View File

@ -0,0 +1,14 @@
#include "PassContext.hpp"
#include "../Renderer.hpp"
PassContext::PassContext(Renderer* Renderer, Program *Program):
renderer(Renderer), program(Program) { }
void PassContext::start() {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
program->useIt();
program->start(renderer);
}
Buffer::params PassContext::setup(glm::mat4 modelMatrix) {
return program->setup(renderer, modelMatrix);
}

View File

@ -0,0 +1,19 @@
#pragma once
#include "../buffer/Buffer.hpp"
#include <glm/glm.hpp>
class Renderer;
class Program;
/// Program execution container
struct PassContext {
public:
PassContext(Renderer *Renderer, Program *Program);
void start();
Buffer::params setup(glm::mat4 modelMatrix);
private:
Renderer *renderer;
Program *program;
};

View File

@ -46,12 +46,12 @@ void Program::useIt() {
GLuint Program::loadTexture(const std::string& name, bool linear) {
return loadDDS(TEXTURES_DIR + name + ".dds", linear);
}
GLuint Program::loadTextureArray(const std::vector<std::string> &names, const std::string& suffix) {
GLuint Program::loadTextureArray(const std::vector<std::string> &names, const std::string& suffix, float mipMapLOD) {
std::vector<std::string> paths;
std::transform(names.begin(), names.end(), std::back_inserter(paths),
[suffix](const std::string &name) -> std::string { return TEXTURES_DIR + name + suffix + ".dds"; });
return loadDDSArray(paths);
return loadDDSArray(paths, mipMapLOD);
}
GLuint Program::loadRawTexture(const std::string& name) {
return loadBMP_custom(name.c_str());

View File

@ -3,9 +3,12 @@
#include <GL/glew.h>
#include <string>
#include <vector>
#include <glm/glm.hpp>
#include "Shader.hpp"
#include "../buffer/Buffer.hpp"
class Renderer;
/// OpenGL shaders pipeline
class Program {
public:
@ -14,9 +17,12 @@ public:
void useIt();
virtual std::string getName() const = 0;
virtual void start(Renderer*) = 0;
virtual Buffer::params setup(Renderer*, glm::mat4 modelMatrix) = 0;
static GLuint loadTexture(const std::string &name, bool linear = true);
static GLuint loadRawTexture(const std::string &name);
static GLuint loadTextureArray(const std::vector<std::string> &names, const std::string &suffix = "");
static GLuint loadTextureArray(const std::vector<std::string> &names, const std::string &suffix = "", float mipMapLOD = 0);
protected:
void load(const std::vector<Shader *> &shaders);

View File

@ -1,22 +0,0 @@
#include "SimpleProgram.hpp"
SimpleProgram::SimpleProgram() : Program() {
std::vector<std::string> flags;
std::vector<Shader*> shaders;
shaders.push_back(loadShader(GL_VERTEX_SHADER, flags));
shaders.push_back(loadShader(GL_FRAGMENT_SHADER, flags));
load(shaders);
MVPMatrixID = glGetUniformLocation(ProgramID, "MVP");
}
SimpleProgram::~SimpleProgram() { }
std::string SimpleProgram::getName() const {
return "Simple";
}
void SimpleProgram::setMVP(const GLfloat *matrix) {
glUniformMatrix4fv(MVPMatrixID, 1, GL_FALSE, matrix);
}

View File

@ -1,17 +0,0 @@
#pragma once
#include "Program.hpp"
/// Basic final pass
class SimpleProgram: public Program {
public:
SimpleProgram();
~SimpleProgram();
std::string getName() const override;
void setMVP(const GLfloat *matrix);
private:
GLuint MVPMatrixID;
};

View File

@ -3,11 +3,11 @@
#include <string.h>
#include <string>
#include <cassert>
#include <cmath>
#include <vector>
#include <iostream>
#include <GL/glew.h>
#include <GLFW/glfw3.h>
@ -137,7 +137,7 @@ GLuint loadDDS(const std::string& imagepath, bool linear){
/* try to open the file */
fp = fopen(imagepath.c_str(), "rb");
if (fp == NULL){
printf("%s could not be opened.\n", imagepath); getchar();
printf("%s could not be opened.\n", imagepath.c_str()); getchar();
return 0;
}
@ -168,7 +168,6 @@ GLuint loadDDS(const std::string& imagepath, bool linear){
/* close the file pointer */
fclose(fp);
unsigned int components = (fourCC == FOURCC_DXT1) ? 3 : 4;
unsigned int format;
switch(fourCC)
{
@ -197,7 +196,7 @@ GLuint loadDDS(const std::string& imagepath, bool linear){
unsigned int blockSize = (format == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) ? 8 : 16;
unsigned int offset = 0;
/* load the mipmaps */
/* load the mipmaps */
for (unsigned int level = 0; level < mipMapCount && (width || height); ++level)
{
unsigned int size = ((width+3)/4)*((height+3)/4)*blockSize;
@ -218,7 +217,6 @@ GLuint loadDDS(const std::string& imagepath, bool linear){
glTextureParameteri(textureID, GL_TEXTURE_MAG_FILTER, linear ? GL_LINEAR : GL_NEAREST);
glTextureParameteri(textureID, GL_TEXTURE_MIN_FILTER, linear ? GL_LINEAR_MIPMAP_LINEAR : GL_NEAREST);
glTextureParameterf(textureID, GL_TEXTURE_LOD_BIAS, 1.f);
glGenerateTextureMipmap(textureID);
@ -226,7 +224,7 @@ GLuint loadDDS(const std::string& imagepath, bool linear){
}
GLuint loadDDSArray(const std::vector<std::string>& imagepaths) {
GLuint loadDDSArray(const std::vector<std::string>& imagepaths, float mipMapLOD) {
unsigned char header[124];
@ -238,7 +236,7 @@ GLuint loadDDSArray(const std::vector<std::string>& imagepaths) {
fp = fopen(imagepath->c_str(), "rb");
if (fp == NULL)
{
printf("%s could not be opened.\n", imagepath);
printf("%s could not be opened.\n", imagepath->c_str());
getchar();
return 0;
}
@ -257,8 +255,6 @@ GLuint loadDDSArray(const std::vector<std::string>& imagepaths) {
unsigned int mainHeight = *(unsigned int *)&(header[8]);
unsigned int mainWidth = *(unsigned int *)&(header[12]);
unsigned int mainLinearSize = *(unsigned int *)&(header[16]);
unsigned int mainMipMapCount = *(unsigned int *)&(header[24]);
unsigned int mainFourCC = *(unsigned int *)&(header[80]);
fclose(fp);
@ -281,7 +277,7 @@ GLuint loadDDSArray(const std::vector<std::string>& imagepaths) {
// Create one OpenGL texture array
GLuint textureID;
glCreateTextures(GL_TEXTURE_2D_ARRAY, 1, &textureID);
glTextureStorage3D(textureID, mainMipMapCount, mainFormat, mainWidth, mainHeight, imagepaths.size());
glTextureStorage3D(textureID, 1 + std::floor(std::log2(std::max(mainWidth, mainHeight))), mainFormat, mainWidth, mainHeight, imagepaths.size());
ushort layer = 0;
for (imagepath = imagepaths.begin(); imagepath != imagepaths.end(); ++imagepath, ++layer)
@ -291,6 +287,12 @@ GLuint loadDDSArray(const std::vector<std::string>& imagepaths) {
glDeleteTextures(1, &subTextureID);
}
glTextureParameteri(textureID, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTextureParameteri(textureID, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTextureParameterf(textureID, GL_TEXTURE_LOD_BIAS, mipMapLOD);
glGenerateTextureMipmap(textureID);
return textureID;
}

View File

@ -10,6 +10,6 @@ GLuint loadBMP_custom(const char * imagepath);
// Load a .DDS file using GLFW's own loader
GLuint loadDDS(const std::string& imagepath, bool linear = true);
// Load a list of .DDS files
GLuint loadDDSArray(const std::vector<std::string> &imagepaths);
GLuint loadDDSArray(const std::vector<std::string> &imagepaths, float mipMapLOD = 0);
#endif

64
src/world/Chunk.cpp Normal file
View File

@ -0,0 +1,64 @@
#include "Chunk.hpp"
#include <FastNoiseSIMD.h>
#include "materials.hpp"
#define DENSITY 0.f
#define GRANULARITY 30.f
Chunk::Chunk(const chunk_pos& pos, Generator& rnd) {
const auto [densitySet, materialSet] = rnd.getChunk(pos, CHUNK_LENGTH);
for (size_t i = 0; i < CHUNK_SIZE; i++) {
voxels[i].Density = std::clamp((densitySet[i] + DENSITY) * GRANULARITY, 0.f, 1.f) * UCHAR_MAX;
voxels[i].Material = voxels[i].Density > 0 ? 1 + std::clamp(static_cast<int>(std::lrint((materialSet[i] + 1) / 2 * (materials::count - 1))),
0, materials::count - 1) : 0; //NOTE: map (approx -1, 1) to (1, mat_max)
}
}
Chunk::~Chunk() { }
bool Chunk::update() {
if(upToDate) {
return false;
} else {
upToDate = true;
return true;
}
}
std::optional<ushort> Chunk::getNeighborIdx(ushort idx, Face face) {
switch (face) {
case Face::Forward:
if (idx % CHUNK_LENGTH >= CHUNK_LENGTH - 1)
return {};
return idx + 1;
case Face::Backward:
if (idx % CHUNK_LENGTH <= 0)
return {};
return idx - 1;
case Face::Up:
if ((idx / CHUNK_LENGTH) % CHUNK_LENGTH >= CHUNK_LENGTH - 1)
return {};
return idx + CHUNK_LENGTH;
case Face::Down:
if ((idx / CHUNK_LENGTH) % CHUNK_LENGTH <= 0)
return {};
return idx - CHUNK_LENGTH;
case Face::Right:
if (idx / CHUNK_LENGTH2 >= CHUNK_LENGTH - 1)
return {};
return idx + CHUNK_LENGTH2;
case Face::Left:
if (idx / CHUNK_LENGTH2 <= 0)
return {};
return idx - CHUNK_LENGTH2;
default:
return {};
}
}

45
src/world/Chunk.hpp Normal file
View File

@ -0,0 +1,45 @@
#pragma once
#include "Generator.hpp"
#include "Voxel.hpp"
#include "../data/geometry/Faces.hpp"
#define CHUNK_LENGTH 16
#define CHUNK_LENGTH2 (CHUNK_LENGTH * CHUNK_LENGTH)
#define CHUNK_SIZE (CHUNK_LENGTH2 * CHUNK_LENGTH)
class Buffer;
/// World part as linear 3d voxel array
struct Chunk {
public:
Chunk(const chunk_pos& pos, Generator& rnd);
~Chunk();
/// Update voxels
/// @return true if modified
bool update();
//TODO: get, set
const Voxel* begin() const {
return voxels.begin();
}
static inline chunk_voxel_pos getPosition(ushort idx) {
return chunk_voxel_pos(idx / CHUNK_LENGTH2, (idx / CHUNK_LENGTH) % CHUNK_LENGTH, idx % CHUNK_LENGTH);
}
static inline ushort getIdx(chunk_voxel_pos pos) {
return getIdx(pos.x, pos.y, pos.z);
}
static inline ushort getIdx(uint x, uint y, uint z) {
return (x * CHUNK_LENGTH + y) * CHUNK_LENGTH + z;
}
static std::optional<ushort> getNeighborIdx(ushort idx, Face dir);
Buffer *buffer = NULL;
private:
/// Chunk data
std::array<Voxel, CHUNK_SIZE> voxels;
/// Require update
bool upToDate = false;
};

36
src/world/Generator.hpp Normal file
View File

@ -0,0 +1,36 @@
#pragma once
#include <FastNoiseSIMD.h>
#include <tuple>
#include "../data/glm.hpp"
class Generator {
public:
Generator(int seed = 42) {
densityNoise = FastNoiseSIMD::NewFastNoiseSIMD(seed);
materialNoise = FastNoiseSIMD::NewFastNoiseSIMD(seed * 5);
materialNoise->SetNoiseType(FastNoiseSIMD::Cellular); // NOTE: probably heavy
materialNoise->SetCellularReturnType(FastNoiseSIMD::CellValue);
materialNoise->SetCellularDistanceFunction(FastNoiseSIMD::Natural);
materialNoise->SetFrequency(.1);
}
~Generator() {
delete densityNoise;
delete materialNoise;
}
inline std::pair<float*, float*> getChunk(const chunk_pos& pos, int size) {
return {
densityNoise->GetNoiseSet(pos.x * size, pos.y * size, pos.z * size, size, size, size),
materialNoise->GetNoiseSet(pos.x * size, pos.y * size, pos.z * size, size, size, size),
};
}
inline void freeChunk(std::pair<float*, float*>& set) {
FastNoiseSIMD::FreeNoiseSet(set.first);
FastNoiseSIMD::FreeNoiseSet(set.second);
}
private:
FastNoiseSIMD *densityNoise;
FastNoiseSIMD *materialNoise;
};

6
src/world/Voxel.hpp Normal file
View File

@ -0,0 +1,6 @@
#pragma once
struct Voxel {
unsigned char Density;
unsigned short Material;
};

85
src/world/World.cpp Normal file
View File

@ -0,0 +1,85 @@
#include "World.hpp"
#include "../contouring/FlatBox.hpp"
#include "../render/buffer/ShortIndexedBuffer.hpp"
World::World(const World::options& options) {
setOptions(options);
}
World::~World() { }
void World::update(const camera_pos& pos, World::report& rep) {
const chunk_pos newPos = glm::divide(pos, chunk_voxel_pos(CHUNK_LENGTH));
const auto chunkChange = newPos != last_pos;
last_pos = newPos;
// Update alive chunks
{
for(auto [chunkPos, chunk]: chunks) {
const glm::ivec3 dist = last_pos - chunkPos;
if (dist.x * dist.x + dist.y * dist.y + dist.z * dist.z > keepDistance * keepDistance
&& unloadQueue.push(chunkPos)) {
//TODO: unloadCount++;
continue;
}
if (chunk->update()) { // MAYBE: update joints
if(chunk->buffer != NULL)
delete chunk->buffer;
std::vector<VertexData> vertices;
FlatBox::render(chunk->begin(), vertices);
chunk->buffer = new ShortIndexedBuffer(GL_TRIANGLES, vertices);
}
}
}
rep.chunk_unload.push(unloadQueue.size());
// Unload dead chunks
for (size_t i = 0; i < 8 && !unloadQueue.empty(); i++) {
chunks.extract(unloadQueue.pop());
//TODO: save to file
}
// Find missing chunks (~240ms from max loadDistance)
if(chunkChange) {
std::vector<chunk_pos> to_load;
for (int x = -loadDistance; x <= loadDistance; x++) {
for (int y = -loadDistance; y <= loadDistance; y++) {
for (int z = -loadDistance; z <= loadDistance; z++) {
if (x * x + y * y + z * z <= loadDistance * loadDistance) {
const chunk_pos p = last_pos + glm::ivec3(x, y, z);
if (chunks.find(p) == chunks.end()) {
to_load.push_back(p);
}
}
}}}
std::sort(to_load.begin(), to_load.end(), [](const chunk_pos &a, const chunk_pos &b) {
return glm::length2(a) < glm::length2(b);
});
for(auto p: to_load) {
loadQueue.push(p);
}
}
rep.chunk_load.push(loadQueue.size());
// Load chunks
for (size_t i = 0; i < 2 && !loadQueue.empty(); i++) {
const auto pos = loadQueue.pop();
const auto chunk = std::make_shared<Chunk>(pos, generator);
chunks.insert({pos, chunk});
}
rep.chunk_count.push(chunks.size());
}
void World::setOptions(const World::options& options) {
loadDistance = options.loadDistance;
keepDistance = options.keepDistance;
}
void World::getModels(std::vector<std::pair<glm::mat4, Buffer*>> &out, float scale) const {
const auto scaling = glm::scale(glm::mat4(1), glm::vec3(scale));
for(const auto [pos, chunk]: chunks) {
if(chunk->buffer != NULL)
out.push_back({glm::translate(scaling, glm::vec3(pos) * glm::vec3(CHUNK_LENGTH)), chunk->buffer});
}
}

47
src/world/World.hpp Normal file
View File

@ -0,0 +1,47 @@
#pragma once
#include <memory>
#include "../data/unique_queue.hpp"
#include "../data/circular_buffer.hpp"
#include "Chunk.hpp"
#define REPORT_BUFFER_SIZE 128
class Buffer;
class World {
public:
struct options {
int loadDistance = 3;
int keepDistance = 4;
};
struct report {
circular_buffer<float> chunk_count = circular_buffer<float>(REPORT_BUFFER_SIZE, 0); // MAYBE: store int
circular_buffer<float> chunk_load = circular_buffer<float>(REPORT_BUFFER_SIZE, 0);
circular_buffer<float> chunk_unload = circular_buffer<float>(REPORT_BUFFER_SIZE, 0);
};
World(const options&);
~World();
void update(const camera_pos& pos, report& rep);
void setOptions(const options &);
std::optional<std::shared_ptr<Chunk>> at(const chunk_pos& pos) const {
const auto it = chunks.find(pos);
if(it == chunks.end())
return {};
return {it->second};
}
void getModels(std::vector<std::pair<glm::mat4, Buffer*>> &models, float scale) const;
private:
chunk_pos last_pos = chunk_pos(INT_MAX);
Generator generator;
std::unordered_map<chunk_pos, std::shared_ptr<Chunk>> chunks;
unique_queue<chunk_pos> loadQueue; // TODO: unique moving priority
unique_queue<chunk_pos> unloadQueue;
int loadDistance;
int keepDistance;
};

9
src/world/materials.hpp Normal file
View File

@ -0,0 +1,9 @@
#pragma once
#include <array>
#include <string>
namespace materials {
static const auto count = 6;
static const std::array<std::string, count> textures = {{"Air", "Sand", "Stone_path", "Seaside_rock", "Stone_wall", "Rough_rock"}};
}