added voxel types

This commit is contained in:
JISAUAY 2025-11-12 14:06:57 -06:00
parent 1ee72dae9a
commit 46be697e2f
10 changed files with 91 additions and 38 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 61 KiB

View File

@ -23,3 +23,9 @@ Instead of making the _entire_ world a single mesh, now the world is made up of
Chunks now generate around the player in a square based on a render distance. This allows the flat plane to be infinite! Chunks now generate around the player in a square based on a render distance. This allows the flat plane to be infinite!
![infinite flat cube world](.attachments/chunk-generation.png) ![infinite flat cube world](.attachments/chunk-generation.png)
## 0.5.0 : Voxel Types - 11/12/25
Different voxel types are now supported in the data structures and passed to the shaders.
![a world of dirt with some stone blocks](.attachments/voxel-types.png)

View File

@ -2,11 +2,23 @@
out vec4 FragColor; out vec4 FragColor;
flat in float v_VoxelKind;
// [NEW] This variable will be set from your C++ code // [NEW] This variable will be set from your C++ code
uniform vec3 u_Color; uniform vec3 u_Color;
void main() void main()
{ {
// Use the color from C++ // Use the color from C++
FragColor = vec4(u_Color, 1.0); // You can now use v_VoxelKind to modify the color
// Example: Different colors for different voxel types
vec3 color = u_Color;
if (v_VoxelKind == 0.0) {
// Dirt - brownish
color *= vec3(0.6, 0.4, 0.2);
} else if (v_VoxelKind == 1.0) {
// Stone - grayish (keep as is)
}
FragColor = vec4(color, 1.0);
} }

View File

@ -4,12 +4,14 @@
// 'layout (location = 0)' matches the first attribute you enable. // 'layout (location = 0)' matches the first attribute you enable.
layout (location = 0) in vec3 aPos; layout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal; layout (location = 1) in vec3 aNormal;
layout (location = 2) in float aVoxelKind;
// A "uniform" is a global variable you set from your C++/Java/etc. code. // A "uniform" is a global variable you set from your C++/Java/etc. code.
// This matrix combines your model, view, and projection matrices. // This matrix combines your model, view, and projection matrices.
uniform mat4 u_mvp; uniform mat4 u_mvp;
flat out vec3 v_Normal; flat out vec3 v_Normal;
flat out float v_VoxelKind;
void main() void main()
{ {
@ -18,4 +20,5 @@ void main()
gl_Position = u_mvp * vec4(aPos, 1.0); gl_Position = u_mvp * vec4(aPos, 1.0);
v_Normal = aNormal; v_Normal = aNormal;
v_VoxelKind = aVoxelKind;
} }

View File

@ -2,7 +2,7 @@
#include <vector> #include <vector>
#include <iostream> #include <iostream>
const int VERTEX_SIZE = 6; const int VERTEX_SIZE = 7; // x, y, z, nx, ny, nz, voxelKind
const float topFace[24] = { const float topFace[24] = {
// x, y, z, nx, ny, nz // x, y, z, nx, ny, nz
@ -42,16 +42,17 @@ const float backFace[24] = {
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f
}; };
void addFace(glm::ivec3 pos, const float *faceVertices, std::vector<float> &vbo, std::vector<unsigned int> &ebo) { void addFace(glm::ivec3 pos, const float *faceVertices, float voxelKind, std::vector<float> &vbo, std::vector<unsigned int> &ebo) {
unsigned int vIndex = vbo.size() / VERTEX_SIZE; unsigned int vIndex = vbo.size() / VERTEX_SIZE;
for (int i = 0; i < 4; i++) { for (int i = 0; i < 4; i++) {
const float* v = &faceVertices[i * VERTEX_SIZE]; const float* v = &faceVertices[i * 6]; // Face data is still 6 floats per vertex
vbo.push_back(v[0] + pos.x); vbo.push_back(v[0] + pos.x);
vbo.push_back(v[1] + pos.y); vbo.push_back(v[1] + pos.y);
vbo.push_back(v[2] + pos.z); vbo.push_back(v[2] + pos.z);
vbo.push_back(v[3]); vbo.push_back(v[3]);
vbo.push_back(v[4]); vbo.push_back(v[4]);
vbo.push_back(v[5]); vbo.push_back(v[5]);
vbo.push_back(voxelKind); // Add voxel kind as 7th component
} }
ebo.push_back(vIndex + 0); ebo.push_back(vIndex + 0);
ebo.push_back(vIndex + 1); ebo.push_back(vIndex + 1);
@ -65,7 +66,7 @@ Chunk::Chunk() {
// mesh is initially null (nullptr) // mesh is initially null (nullptr)
} }
Chunk::Chunk(std::unordered_set<glm::ivec3> voxels) { Chunk::Chunk(std::unordered_map<glm::ivec3, VoxelKind> voxels) {
this->voxels = voxels; this->voxels = voxels;
} }
@ -83,24 +84,25 @@ void Chunk::generateMesh() {
// std::cout << "Generating Chunk mesh..." << std::endl; // std::cout << "Generating Chunk mesh..." << std::endl;
for (const glm::ivec3 pos : voxels) { const std::pair<glm::ivec3, const float*> neighbors[6] = {
if (voxels.count(pos + glm::ivec3(0, 1, 0)) == 0) { std::pair(glm::ivec3(0, 1, 0), topFace), // top
addFace(pos, topFace, vboData, eboData); std::pair(glm::ivec3(0, -1, 0), bottomFace), // bottom
} std::pair(glm::ivec3(1, 0, 0), rightFace), // right
if (voxels.count(pos + glm::ivec3(0, -1, 0)) == 0) { std::pair(glm::ivec3(-1, 0, 0), leftFace), // left
addFace(pos, bottomFace, vboData, eboData); std::pair(glm::ivec3(0, 0, 1), frontFace), // front
} std::pair(glm::ivec3(0, 0, -1), backFace) // back
if (voxels.count(pos + glm::ivec3(1, 0, 0)) == 0) { };
addFace(pos, rightFace, vboData, eboData);
} for (const std::pair<const glm::ivec3, VoxelKind>& pair : voxels) {
if (voxels.count(pos + glm::ivec3(-1, 0, 0)) == 0) { const glm::ivec3& pos = pair.first;
addFace(pos, leftFace, vboData, eboData); const VoxelKind& kind = pair.second;
}
if (voxels.count(pos + glm::ivec3(0, 0, 1)) == 0) { for (const std::pair<glm::ivec3, const float*>& neighborPair : neighbors) {
addFace(pos, frontFace, vboData, eboData); glm::ivec3 dir = neighborPair.first;
} const float* face = neighborPair.second;
if (voxels.count(pos + glm::ivec3(0, 0, -1)) == 0) { if (voxels.count(pos + dir) == 0) {
addFace(pos, backFace, vboData, eboData); addFace(pos, face, static_cast<float>(kind), vboData, eboData);
}
} }
} }
@ -113,3 +115,7 @@ void Chunk::generateMesh() {
// Now, upload this data to our mesh object // Now, upload this data to our mesh object
mesh->uploadData(vboData, eboData); mesh->uploadData(vboData, eboData);
} }
void Chunk::regenerateMesh() {
this->generateMesh();
}

View File

@ -1,13 +1,18 @@
#ifndef CHUNK_H #ifndef CHUNK_H
#define CHUNK_H #define CHUNK_H
#include <unordered_set> #include <unordered_map>
#include <memory> #include <memory>
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include "Mesh.h" #include "Mesh.h"
enum VoxelKind {
Dirt,
Stone
};
const int CHUNK_SIZE = 32; const int CHUNK_SIZE = 32;
// Hash function for glm::ivec3 to use with unordered_set // Hash function for glm::ivec3 to use with unordered_set
@ -28,14 +33,15 @@ namespace std {
class Chunk { class Chunk {
public: public:
std::unordered_set<glm::ivec3> voxels; std::unordered_map<glm::ivec3, VoxelKind> voxels;
Chunk(); Chunk();
Chunk(std::unordered_set<glm::ivec3> voxels); Chunk(std::unordered_map<glm::ivec3, VoxelKind> voxels);
Mesh* getMesh(); // Returns pointer, can be null if not generated yet Mesh* getMesh(); // Returns pointer, can be null if not generated yet
void generateMesh(); void generateMesh();
void regenerateMesh();
private: private:
std::unique_ptr<Mesh> mesh; // Nullable mesh std::unique_ptr<Mesh> mesh; // Nullable mesh

View File

@ -24,11 +24,14 @@ void Mesh::init() {
// 2. Set up attributes (this layout is now saved in the VAO) // 2. Set up attributes (this layout is now saved in the VAO)
// Position Attribute // Position Attribute
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)0); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 7 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0); glEnableVertexAttribArray(0);
// Normal Attribute // Normal Attribute
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(float), (void*)(3 * sizeof(float))); glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 7 * sizeof(float), (void*)(3 * sizeof(float)));
glEnableVertexAttribArray(1); glEnableVertexAttribArray(1);
// VoxelKind Attribute
glVertexAttribPointer(2, 1, GL_FLOAT, GL_FALSE, 7 * sizeof(float), (void*)(6 * sizeof(float)));
glEnableVertexAttribArray(2);
// 3. Bind EBO // 3. Bind EBO
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_EBO); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_EBO);

View File

@ -2,27 +2,38 @@
World::World() {} World::World() {}
void World::flip_voxel(glm::ivec3 position) { void World::set_voxel(glm::ivec3 position, std::optional<VoxelKind> kind) {
glm::ivec3 chunk_position = position / CHUNK_SIZE; glm::ivec3 chunk_position = position / CHUNK_SIZE;
glm::ivec3 local_position = position % CHUNK_SIZE; glm::ivec3 local_position = position % CHUNK_SIZE;
Chunk * chunk = get_or_create_chunk(chunk_position); Chunk * chunk = get_or_create_chunk(chunk_position);
if (chunk->voxels.count(local_position) != 0) { if (!kind.has_value()) {
chunk->voxels.erase(chunk->voxels.find(local_position)); chunk->voxels.erase(chunk->voxels.find(local_position));
} else { } else {
chunk->voxels.insert(local_position); chunk->voxels[local_position] = kind.value();
} }
chunk->regenerateMesh();
} }
Chunk* World::get_or_create_chunk(glm::ivec3 chunk_position) { Chunk* World::get_or_create_chunk(glm::ivec3 chunk_position) {
if (this->chunks.count(chunk_position) == 0) { if (this->chunks.count(chunk_position) == 0) {
// Fill in new chunk with a flat plane of voxels at y=0 // Fill in new chunk with a flat plane of voxels at y=0
std::unordered_set<glm::ivec3> voxels; std::unordered_map<glm::ivec3, VoxelKind> voxels;
if (chunk_position.y == 0) { if (chunk_position.y == 0) {
for (int z = 0; z <= 32; z++) { for (int z = 0; z <= CHUNK_SIZE; z++) {
for (int x = 0; x <= 32; x++) { for (int y = 0; y <= CHUNK_SIZE; y++) {
voxels.insert(glm::ivec3(x, 0, z)); for (int x = 0; x <= CHUNK_SIZE; x++) {
glm::ivec3 offset = glm::ivec3(x, y, z);
glm::ivec3 world_voxel_position = chunk_position * 32 + offset;
if (world_voxel_position.y == 0) {
voxels[offset] = VoxelKind::Dirt;
} else if (world_voxel_position.y < 0) {
voxels[offset] = VoxelKind::Stone;
}
}
} }
} }
} }

View File

@ -2,6 +2,7 @@
#define WORLD_H #define WORLD_H
#include <unordered_map> #include <unordered_map>
#include <optional>
#include <glm/glm.hpp> #include <glm/glm.hpp>
@ -14,7 +15,7 @@ class World
World(); World();
void flip_voxel(glm::ivec3 position); void set_voxel(glm::ivec3 position, std::optional<VoxelKind> kind);
Chunk* get_or_create_chunk(glm::ivec3 chunk_position); Chunk* get_or_create_chunk(glm::ivec3 chunk_position);
}; };

View File

@ -54,6 +54,8 @@ bool first_mouse = true;
bool mouse_lock = true; bool mouse_lock = true;
float last_x = 400, last_y = 300; float last_x = 400, last_y = 300;
World world;
// Mouse Look // Mouse Look
void mouse_callback(GLFWwindow * window, double xpos, double ypos) void mouse_callback(GLFWwindow * window, double xpos, double ypos)
{ {
@ -84,6 +86,10 @@ void mouse_callback(GLFWwindow * window, double xpos, double ypos)
direction.y = -sin(glm::radians(pitch)); direction.y = -sin(glm::radians(pitch));
direction.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch)); direction.z = sin(glm::radians(yaw)) * cos(glm::radians(pitch));
camera.target = glm::normalize(direction); camera.target = glm::normalize(direction);
if(glfwGetMouseButton(window, GLFW_MOUSE_BUTTON_2) == GLFW_PRESS) {
world.set_voxel(glm::ivec3(camera.position), VoxelKind::Stone);
}
} }
// process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly // process all input: query GLFW whether relevant keys are pressed/released this frame and react accordingly
@ -183,7 +189,7 @@ int main() {
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
// 2. --- Create a Window --- // 2. --- Create a Window ---
GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "Voxel Engine 0.4.0", NULL, NULL); GLFWwindow* window = glfwCreateWindow(SCR_WIDTH, SCR_HEIGHT, "Voxel Engine 0.5.0", NULL, NULL);
if (window == NULL) { if (window == NULL) {
std::cerr << "Failed to create GLFW window" << std::endl; std::cerr << "Failed to create GLFW window" << std::endl;
glfwTerminate(); glfwTerminate();
@ -206,7 +212,6 @@ int main() {
// 5. --- Set up Vertex Data and Buffers --- // 5. --- Set up Vertex Data and Buffers ---
// Load Cube OBJ // Load Cube OBJ
World world;
// Object3D cube = Object3D("objs/cube.obj"); // Object3D cube = Object3D("objs/cube.obj");
// std::cout << "World created with " << world.chunks.size() << " chunks" << std::endl; // std::cout << "World created with " << world.chunks.size() << " chunks" << std::endl;