Infinite box universe
This commit is contained in:
parent
2aff0cea95
commit
77d10611ae
12
TODO.md
12
TODO.md
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -1,8 +0,0 @@
|
|||
#version 330 core
|
||||
|
||||
// Ouput data
|
||||
layout(location = 0) out vec4 color;
|
||||
|
||||
void main(){
|
||||
color = vec4(1, 0, 0, 0);
|
||||
}
|
|
@ -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);
|
||||
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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;
|
||||
};
|
|
@ -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
|
63
src/main.cpp
63
src/main.cpp
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -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();
|
||||
};
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
#include <GL/glew.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
class PassParams;
|
||||
|
||||
/// Abstract OpenGL Buffer
|
||||
class Buffer {
|
||||
public:
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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);
|
||||
};
|
||||
|
|
@ -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
|
|
@ -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 );
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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 {};
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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;
|
||||
};
|
|
@ -0,0 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
struct Voxel {
|
||||
unsigned char Density;
|
||||
unsigned short Material;
|
||||
};
|
|
@ -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});
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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"}};
|
||||
}
|
Loading…
Reference in New Issue