added voxel types
This commit is contained in:
parent
1ee72dae9a
commit
46be697e2f
BIN
.attachments/voxel-types.png
Normal file
BIN
.attachments/voxel-types.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 61 KiB |
@ -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!
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
## 0.5.0 : Voxel Types - 11/12/25
|
||||||
|
|
||||||
|
Different voxel types are now supported in the data structures and passed to the shaders.
|
||||||
|
|
||||||
|

|
||||||
|
|||||||
@ -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);
|
||||||
}
|
}
|
||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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();
|
||||||
|
}
|
||||||
|
|||||||
12
src/Chunk.h
12
src/Chunk.h
@ -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
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user