Use indexing to drastically reduce the number of vertices
parent
c955c07ab4
commit
c111d5c1b7
|
@ -43,7 +43,7 @@ CMAKE_AR:FILEPATH=emar
|
|||
|
||||
//Choose the type of build, options are: None Debug Release RelWithDebInfo
|
||||
// MinSizeRel ...
|
||||
CMAKE_BUILD_TYPE:STRING=Debug
|
||||
CMAKE_BUILD_TYPE:STRING=Release
|
||||
|
||||
//Enable/Disable color output during build.
|
||||
CMAKE_COLOR_MAKEFILE:BOOL=ON
|
||||
|
|
|
@ -6,5 +6,5 @@ CXX_DEFINES =
|
|||
|
||||
CXX_INCLUDES = @CMakeFiles/FinalProject.dir/includes_CXX.rsp
|
||||
|
||||
CXX_FLAGS = -g -std=c++17 -std=gnu++17
|
||||
CXX_FLAGS = -O3 -DNDEBUG -std=c++17 -std=gnu++17
|
||||
|
||||
|
|
|
@ -1 +1 @@
|
|||
/usr/bin/em++ -g -sMAX_WEBGL_VERSION=2 -s ASSERTIONS=1 -sUSE_GLFW=3 --preload-file 'assets' @CMakeFiles/FinalProject.dir/objects1 -o FinalProject.js @CMakeFiles/FinalProject.dir/linkLibs.rsp
|
||||
/usr/bin/em++ -O3 -DNDEBUG -sMAX_WEBGL_VERSION=2 -s ASSERTIONS=1 -sUSE_GLFW=3 --preload-file 'assets' @CMakeFiles/FinalProject.dir/objects1 -o FinalProject.js @CMakeFiles/FinalProject.dir/linkLibs.rsp
|
||||
|
|
|
@ -327,4 +327,5 @@ CMakeFiles/FinalProject.dir/src/main.cpp.o: \
|
|||
/home/brett/Documents/Brock/CS\ 3P98/Final\ Project/include/render/camera.h \
|
||||
/home/brett/Documents/Brock/CS\ 3P98/Final\ Project/include/world/chunk/world.h \
|
||||
/home/brett/Documents/Brock/CS\ 3P98/Final\ Project/include/world/chunk/storage.h \
|
||||
/home/brett/Documents/Brock/CS\ 3P98/Final\ Project/include/world/chunk/typedefs.h
|
||||
/home/brett/Documents/Brock/CS\ 3P98/Final\ Project/include/world/chunk/typedefs.h \
|
||||
/home/brett/Documents/Brock/CS\ 3P98/Final\ Project/include/world/registry.h
|
||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -12,7 +12,7 @@ if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME)
|
|||
string(REGEX REPLACE "^[^A-Za-z0-9_]+" ""
|
||||
CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}")
|
||||
else()
|
||||
set(CMAKE_INSTALL_CONFIG_NAME "Debug")
|
||||
set(CMAKE_INSTALL_CONFIG_NAME "Release")
|
||||
endif()
|
||||
message(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"")
|
||||
endif()
|
||||
|
|
|
@ -6,5 +6,5 @@ CXX_DEFINES =
|
|||
|
||||
CXX_INCLUDES = @CMakeFiles/BLT.dir/includes_CXX.rsp
|
||||
|
||||
CXX_FLAGS = -g -std=c++17 -std=gnu++17
|
||||
CXX_FLAGS = -O3 -DNDEBUG -std=c++17 -std=gnu++17
|
||||
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -12,7 +12,7 @@ if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME)
|
|||
string(REGEX REPLACE "^[^A-Za-z0-9_]+" ""
|
||||
CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}")
|
||||
else()
|
||||
set(CMAKE_INSTALL_CONFIG_NAME "Debug")
|
||||
set(CMAKE_INSTALL_CONFIG_NAME "Release")
|
||||
endif()
|
||||
message(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"")
|
||||
endif()
|
||||
|
|
Binary file not shown.
|
@ -12,6 +12,7 @@
|
|||
#include "blt/std/logging.h"
|
||||
#include <world/chunk/typedefs.h>
|
||||
#include <world/registry.h>
|
||||
#include <unordered_map>
|
||||
|
||||
// contains storage classes for block IDs inside chunks plus eventual lookup of block states
|
||||
|
||||
|
@ -51,26 +52,24 @@ namespace fp {
|
|||
|
||||
class mesh_storage {
|
||||
private:
|
||||
std::vector<float> vertices;
|
||||
inline void add_and_translate(const float* array, const block_pos& pos) {
|
||||
// since a chunk mesh contains all the faces for all the blocks inside the chunk
|
||||
// we can add the translated values of predefined "unit" faces. This is for the simple "fast" chunk mesh generator.
|
||||
for (int i = 0; i < VTX_ARR_SIZE; i+=3){
|
||||
auto new_x = array[i] + (float)pos.x;
|
||||
auto new_y = array[i + 1] + (float)pos.y;
|
||||
auto new_z = array[i + 2] + (float)pos.z;
|
||||
// BLT_TRACE("Creating translated vertex {%f, %f, %f} from array position [%d, %d, %d]", new_x, new_y, new_z, i, i + 1, i + 2);
|
||||
vertices.push_back(new_x);
|
||||
vertices.push_back(new_y);
|
||||
vertices.push_back(new_z);
|
||||
}
|
||||
}
|
||||
std::unordered_map<vertex, unsigned int, _static::vertex_hash, _static::vertex_equality> created_vertices_index;
|
||||
std::vector<vertex> vertices;
|
||||
std::vector<unsigned int> indices;
|
||||
public:
|
||||
/**
|
||||
* since a chunk mesh contains all the faces for all the blocks inside the chunk
|
||||
* we can add the translated values of predefined "unit" faces. This is for the simple "fast" chunk mesh generator.
|
||||
* @param face the direction the face is facing to be added to the mesh.
|
||||
* @param pos position of the face
|
||||
*/
|
||||
void addFace(face face, const block_pos& pos);
|
||||
|
||||
inline std::vector<float>& getVertices() {
|
||||
inline std::vector<vertex>& getVertices() {
|
||||
return vertices;
|
||||
}
|
||||
inline std::vector<unsigned int>& getIndices() {
|
||||
return indices;
|
||||
}
|
||||
};
|
||||
|
||||
namespace mesh {
|
||||
|
|
|
@ -11,7 +11,9 @@
|
|||
constexpr int CHUNK_SIZE = 32;
|
||||
const int CHUNK_SHIFT = (int)(log(CHUNK_SIZE) / log(2));
|
||||
// size that the base vertex arrays are assumed to be (per face)
|
||||
constexpr int VTX_ARR_SIZE = 18;
|
||||
constexpr int VTX_ARR_SIZE = 4;
|
||||
|
||||
constexpr float EPSILON = 0.0001f;
|
||||
|
||||
namespace fp {
|
||||
|
||||
|
@ -52,6 +54,13 @@ namespace fp {
|
|||
block_pos(float x, float y, float z): block_pos(int(x), int(y), int(z)) {}
|
||||
};
|
||||
|
||||
// to ensure this is a POD we define the vertex as a C-struct. This allows us to store one large vertex array and pass that to the GPU
|
||||
// instead of sending arrays for the positions, UVs, normals, etc.
|
||||
// since OpenGL allows us to specify attributes based on offsets from the same VBO.
|
||||
typedef struct {
|
||||
float x, y, z;
|
||||
} vertex;
|
||||
|
||||
namespace _static {
|
||||
|
||||
// std::unordered_map requires a type. As a result the functions are encapsulated.
|
||||
|
@ -64,11 +73,29 @@ namespace fp {
|
|||
}
|
||||
};
|
||||
|
||||
struct vertex_hash {
|
||||
inline size_t operator()(const vertex& pos) const {
|
||||
size_t p1 = std::hash<float>()(pos.x);
|
||||
size_t p2 = std::hash<float>()(pos.y);
|
||||
size_t p3 = std::hash<float>()(pos.z);
|
||||
return (p1 ^ (p2 << 1)) ^ p3;
|
||||
}
|
||||
};
|
||||
|
||||
struct chunk_pos_equality {
|
||||
inline bool operator()(const chunk_pos& p1, const chunk_pos& p2) const {
|
||||
return p1.x == p2.x && p1.y == p2.y && p1.z == p2.z;
|
||||
}
|
||||
};
|
||||
|
||||
struct vertex_equality {
|
||||
inline bool operator()(const vertex& p1, const vertex& p2) const {
|
||||
return p1.x >= p2.x - EPSILON && p1.x <= p2.x + EPSILON && p1.y >= p2.y - EPSILON && p1.y <= p2.y + EPSILON && p1.z >= p2.z - EPSILON && p1.z <= p2.z + EPSILON;
|
||||
}
|
||||
// inline bool operator()(const vertex& p1, const vertex& p2) const {
|
||||
// return p1.x == p2.x && p1.y == p2.y && p1.z == p2.z;
|
||||
// }
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -85,7 +85,8 @@ namespace fp {
|
|||
|
||||
// since they both use the same amount of memory we will only store the vertices and draw with drawArrays, since it is less complex.
|
||||
// set up the VBOs which will be later updated when the mesh is generated.
|
||||
chunk_vao->bindVBO(new VBO(ARRAY_BUFFER, nullptr, 0), 0, 3);
|
||||
chunk_vao->bindVBO(new VBO(ARRAY_BUFFER, nullptr, 0), 0, 3, GL_FLOAT, 3 * sizeof(float), 0);
|
||||
chunk_vao->bindElementVBO(new VBO(ELEMENT_BUFFER, nullptr, 0));
|
||||
}
|
||||
|
||||
~chunk() {
|
||||
|
|
|
@ -45,8 +45,10 @@ int main() {
|
|||
chunk_shader = new fp::shader(shader_chunk_vert, shader_chunk_frag);
|
||||
world = new fp::world();
|
||||
|
||||
world->setBlock({0,0,0}, 1);
|
||||
|
||||
for (int i = 1; i < CHUNK_SIZE; i++)
|
||||
for (int j = 0; j < 3; j++)
|
||||
for (int j = 0; j < 2; j++)
|
||||
for (int k = 5; k < CHUNK_SIZE; k++)
|
||||
world->setBlock({i,j,k}, 1);
|
||||
world->setBlock({-2, 2, 2}, 1);
|
||||
|
|
|
@ -8,69 +8,55 @@
|
|||
|
||||
constexpr float scale = 0.5f;
|
||||
|
||||
const float x_positive_vertices[VTX_ARR_SIZE] = {
|
||||
// +x first triangle
|
||||
scale, -scale, scale, // +x top left
|
||||
scale, scale, -scale, // +x bottom right
|
||||
scale, scale, scale, // +x top right
|
||||
// +x second triangle
|
||||
scale, -scale, scale, // +x top left
|
||||
scale, -scale, -scale, // +x bottom left
|
||||
scale, scale, -scale, // +x bottom right
|
||||
const fp::vertex x_positive_vertices[VTX_ARR_SIZE] = {
|
||||
{scale, scale, scale}, // +x top right
|
||||
{scale, scale, -scale}, // +x bottom right
|
||||
{scale, -scale, -scale}, // +x bottom left
|
||||
{scale, -scale, scale} // +x top left
|
||||
};
|
||||
const float x_negative_vertices[VTX_ARR_SIZE] = {
|
||||
// -x first triangle
|
||||
-scale, scale, scale, // -x top right
|
||||
-scale, scale, -scale, // -x bottom right
|
||||
-scale, -scale, scale, // -x top left
|
||||
// -x second triangle
|
||||
-scale, scale, -scale, // -x bottom right
|
||||
-scale, -scale, -scale, // -x bottom left
|
||||
-scale, -scale, scale, // -x top left
|
||||
const fp::vertex x_negative_vertices[VTX_ARR_SIZE] = {
|
||||
{-scale, scale, scale}, // -x top right
|
||||
{-scale, scale, -scale}, // -x bottom right
|
||||
{-scale, -scale, -scale}, // -x bottom left
|
||||
{-scale, -scale, scale} // -x top left
|
||||
};
|
||||
const float y_positive_vertices[VTX_ARR_SIZE] = {
|
||||
// first triangle
|
||||
scale, scale, -scale, // top left
|
||||
-scale, scale, scale, // bottom right
|
||||
scale, scale, scale, // top right
|
||||
// second triangle
|
||||
scale, scale, -scale, // top left
|
||||
-scale, scale, -scale, // bottom left
|
||||
-scale, scale, scale, // bottom right
|
||||
const fp::vertex y_positive_vertices[VTX_ARR_SIZE] = {
|
||||
{scale, scale, scale}, // +y top right
|
||||
{-scale, scale, scale}, // +y bottom right
|
||||
{-scale, scale, -scale}, // +y bottom left
|
||||
{scale, scale, -scale}, // +y top left
|
||||
};
|
||||
const float y_negative_vertices[VTX_ARR_SIZE] = {
|
||||
// first triangle
|
||||
scale, -scale, scale, // top right
|
||||
-scale, -scale, scale, // bottom right
|
||||
scale, -scale, -scale, // top left
|
||||
// second triangle
|
||||
-scale, -scale, scale, // bottom right
|
||||
-scale, -scale, -scale, // bottom left
|
||||
scale, -scale, -scale, // top left
|
||||
const fp::vertex y_negative_vertices[VTX_ARR_SIZE] = {
|
||||
{scale, -scale, scale}, // -y top right
|
||||
{-scale, -scale, scale}, // -y bottom right
|
||||
{-scale, -scale, -scale}, // -y bottom left
|
||||
{scale, -scale, -scale}, // -y top left
|
||||
};
|
||||
const float z_positive_vertices[VTX_ARR_SIZE] = {
|
||||
// first triangle
|
||||
-scale, scale, scale, // top left
|
||||
scale, -scale, scale, // bottom right
|
||||
scale, scale, scale, // top right
|
||||
// second triangle
|
||||
-scale, scale, scale, // top left
|
||||
-scale, -scale, scale, // bottom left
|
||||
scale, -scale, scale, // bottom right
|
||||
const fp::vertex z_positive_vertices[VTX_ARR_SIZE] = {
|
||||
{scale, scale, scale}, // +z top right
|
||||
{scale, -scale, scale}, // +z bottom right
|
||||
{-scale, -scale, scale}, // +z bottom left
|
||||
{-scale, scale, scale}, // +z top left
|
||||
};
|
||||
const float z_negative_vertices[VTX_ARR_SIZE] = {
|
||||
// first triangle
|
||||
scale, scale, -scale, // top right
|
||||
scale, -scale, -scale, // bottom right
|
||||
-scale, scale, -scale, // top left
|
||||
// second triangle
|
||||
scale, -scale, -scale, // bottom right
|
||||
-scale, -scale, -scale, // bottom left
|
||||
-scale, scale, -scale, // top left
|
||||
const fp::vertex z_negative_vertices[VTX_ARR_SIZE] = {
|
||||
{scale, scale, -scale}, // -z top right
|
||||
{scale, -scale, -scale}, // -z bottom right
|
||||
{-scale, -scale, -scale}, // -z bottom left
|
||||
{-scale, scale, -scale}, // -z top left
|
||||
};
|
||||
|
||||
// indices are the same on all axis but are flipped between negative / positive as a result of back-face culling.
|
||||
const std::vector<unsigned int> negative_indices = {
|
||||
0, 1, 3,
|
||||
1, 2, 3
|
||||
};
|
||||
const std::vector<unsigned int> positive_indices = {
|
||||
3, 1, 0,
|
||||
3, 2, 1
|
||||
};
|
||||
|
||||
// always ordered the same as the enum!
|
||||
const float* face_decode[] = {
|
||||
const fp::vertex* face_decode[] = {
|
||||
x_positive_vertices,
|
||||
x_negative_vertices,
|
||||
y_positive_vertices,
|
||||
|
@ -80,5 +66,35 @@ const float* face_decode[] = {
|
|||
};
|
||||
|
||||
void fp::mesh_storage::addFace(fp::face face, const block_pos& pos) {
|
||||
add_and_translate(face_decode[face], pos);
|
||||
const auto* face_vertices = face_decode[face];
|
||||
// negatives are odd numbered, positives are even.
|
||||
const auto& face_indices = face % 2 == 0 ? positive_indices : negative_indices;
|
||||
|
||||
vertex translated_face_vertices[VTX_ARR_SIZE];
|
||||
|
||||
// generate translated vertices
|
||||
for (int i = 0; i < VTX_ARR_SIZE; i++) {
|
||||
translated_face_vertices[i].x = face_vertices[i].x + (float) pos.x;
|
||||
translated_face_vertices[i].y = face_vertices[i].y + (float) pos.y;
|
||||
translated_face_vertices[i].z = face_vertices[i].z + (float) pos.z;
|
||||
}
|
||||
|
||||
for (unsigned int face_index : face_indices) {
|
||||
// vertex associated with the index
|
||||
auto index_vertex = translated_face_vertices[face_index];
|
||||
// search to see if the vertex exists already inside the vertices face_vertices
|
||||
auto find_existing_vertex = created_vertices_index.find(index_vertex);
|
||||
if (find_existing_vertex == created_vertices_index.end()) {
|
||||
// vertex doesn't already exist in the face_vertices.
|
||||
// the current size contains the position we are inserting the vertex into. This is our index in the vertex face_vertices
|
||||
auto current_index_pos = vertices.size();
|
||||
vertices.push_back(index_vertex);
|
||||
// Since we are inserting using the order of the face_indices this will ensure that the triangle vertices are ordered correctly (outward facing)
|
||||
created_vertices_index.insert({index_vertex, current_index_pos});
|
||||
indices.push_back(current_index_pos);
|
||||
} else {
|
||||
// does exist in the face_vertices we can use that knowledge to reduce the total # of vertices
|
||||
indices.push_back(find_existing_vertex->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,9 +9,9 @@ void fp::world::generateFullMesh(mesh_storage* mesh, fp::chunk* chunk) {
|
|||
// checks to outside the bounds of the chunk should not have faces added. this will be handled by the partial mesh!
|
||||
bool outside = false;
|
||||
|
||||
for (int i = 1; i < CHUNK_SIZE - 1; i++) {
|
||||
for (int j = 1; j < CHUNK_SIZE - 1; j++) {
|
||||
for (int k = 1; k < CHUNK_SIZE - 1; k++) {
|
||||
for (int i = 0; i < CHUNK_SIZE; i++) {
|
||||
for (int j = 0; j < CHUNK_SIZE; j++) {
|
||||
for (int k = 0; k < CHUNK_SIZE; k++) {
|
||||
auto block = chunk->storage->get({i, j, k});
|
||||
// opaque visibility is always 0. Non-zero values (true) are what we care about since opaque blocks are completely hidden
|
||||
if (!fp::registry::get(block).visibility) {
|
||||
|
@ -46,7 +46,6 @@ inline void checkEdgeFaces(
|
|||
}
|
||||
|
||||
void fp::world::generateEdgeMesh(mesh_storage* mesh, fp::chunk* chunk) {
|
||||
BLT_TRACE("NOPE");
|
||||
// don't try to regen the chunk mesh unless there is a chance all neighbours are not null
|
||||
if (chunk->status != chunk_update_status::NEIGHBOUR_CREATE)
|
||||
return;
|
||||
|
@ -54,7 +53,6 @@ void fp::world::generateEdgeMesh(mesh_storage* mesh, fp::chunk* chunk) {
|
|||
chunk_neighbours neighbours{};
|
||||
getNeighbours(chunk->pos, neighbours);
|
||||
|
||||
BLT_TRACE("GOODBYE");
|
||||
|
||||
// if none of the neighbours exist we cannot continue!
|
||||
for (auto* neighbour : neighbours.neighbours) {
|
||||
|
@ -75,8 +73,6 @@ void fp::world::generateEdgeMesh(mesh_storage* mesh, fp::chunk* chunk) {
|
|||
}
|
||||
}
|
||||
|
||||
BLT_TRACE("HELLO");
|
||||
|
||||
chunk->status = NONE;
|
||||
chunk->dirtiness = REFRESH;
|
||||
}
|
||||
|
@ -91,8 +87,6 @@ void fp::world::generateChunkMesh(fp::chunk* chunk) {
|
|||
if (chunk->dirtiness == PARTIAL_MESH) { // partial chunk mesh (had null neighbours)
|
||||
generateEdgeMesh(chunk->mesh, chunk);
|
||||
}
|
||||
|
||||
chunk->dirtiness = REFRESH;
|
||||
}
|
||||
|
||||
void fp::world::update() {
|
||||
|
@ -114,14 +108,18 @@ void fp::world::render(fp::shader& shader) {
|
|||
|
||||
if (chunk->dirtiness == REFRESH) {
|
||||
auto& vertices = chunk->mesh->getVertices();
|
||||
auto& indices = chunk->mesh->getIndices();
|
||||
|
||||
// 11436 vert, 137,232 bytes
|
||||
|
||||
BLT_INFO("Chunk [%d, %d, %d] mesh updated with %d vertices and %d indices taking (%d, %d) bytes!",
|
||||
chunk->pos.x, chunk->pos.y, chunk->pos.z,
|
||||
vertices.size(), 0, vertices.size() * sizeof(float), 0 * sizeof(unsigned int));
|
||||
vertices.size(), indices.size(), vertices.size() * sizeof(vertex), indices.size() * sizeof(unsigned int));
|
||||
|
||||
// upload the new vertices to the GPU
|
||||
chunk->chunk_vao->getVBO(0)->update(vertices);
|
||||
chunk->render_size = vertices.size() / 3;
|
||||
chunk->chunk_vao->getVBO(-1)->update(indices);
|
||||
chunk->render_size = indices.size();
|
||||
|
||||
// delete the memory from the CPU.
|
||||
delete (chunk->mesh);
|
||||
|
@ -135,7 +133,8 @@ void fp::world::render(fp::shader& shader) {
|
|||
shader.setMatrix("translation", translation);
|
||||
chunk->chunk_vao->bind();
|
||||
glEnableVertexAttribArray(0);
|
||||
glDrawArrays(GL_TRIANGLES, 0, (int) chunk->render_size);
|
||||
//glDrawArrays(GL_TRIANGLES, 0, (int) chunk->render_size);
|
||||
glDrawElements(GL_TRIANGLES, (int)chunk->render_size, GL_UNSIGNED_INT, nullptr);
|
||||
glDisableVertexAttribArray(0);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue