Broken Greedy Mesher
This commit is contained in:
parent
4dcf88380b
commit
7e5261047e
9
cpp-voxel-engine/.vscode/settings.json
vendored
Normal file
9
cpp-voxel-engine/.vscode/settings.json
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"files.associations": {
|
||||||
|
"*.pyx": "python",
|
||||||
|
"*.js": "javascript",
|
||||||
|
"*.c": "c",
|
||||||
|
"*.scene": "yaml",
|
||||||
|
"vector": "cpp"
|
||||||
|
}
|
||||||
|
}
|
@ -1,27 +1,117 @@
|
|||||||
#include "GreedyMesher.h"
|
#include "GreedyMesher.h"
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
std::vector<Quad> GreedyMesher::mesh(const std::vector<std::vector<std::vector<int>>>& voxelData) {
|
// Helper to index into voxels with bounds checking
|
||||||
|
inline int voxelAt(const std::vector<std::vector<std::vector<int>>>& v, int x, int y, int z) {
|
||||||
|
if (x<0||y<0||z<0) return 0;
|
||||||
|
if (x>= (int)v.size() || y>= (int)v[0].size() || z>= (int)v[0][0].size()) return 0;
|
||||||
|
return v[x][y][z];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Quad> GreedyMesher::mesh(const std::vector<std::vector<std::vector<int>>>& voxels) {
|
||||||
std::vector<Quad> quads;
|
std::vector<Quad> quads;
|
||||||
|
int sizeX = voxels.size();
|
||||||
|
int sizeY = voxels[0].size();
|
||||||
|
int sizeZ = voxels[0][0].size();
|
||||||
|
|
||||||
int sizeX = voxelData.size();
|
// directions: {dx,dy,dz}, and their “du” and “dv” axes
|
||||||
if (sizeX == 0) return quads;
|
static const int dirs[6][3] = {
|
||||||
int sizeY = voxelData[0].size();
|
{-1,0,0},{1,0,0},{0,-1,0},{0,1,0},{0,0,-1},{0,0,1}
|
||||||
int sizeZ = voxelData[0][0].size();
|
};
|
||||||
|
static const int axisU[6] = {2,2,0,0,0,0}; // u-axis index per face
|
||||||
|
static const int axisV[6] = {1,1,1,1,2,2}; // v-axis index per face
|
||||||
|
|
||||||
// For simplicity, we create one quad per non-empty voxel.
|
// For each face direction
|
||||||
// A complete greedy mesher would merge adjacent quads.
|
for (int d = 0; d < 6; d++) {
|
||||||
for (int x = 0; x < sizeX; ++x) {
|
int dx = dirs[d][0], dy = dirs[d][1], dz = dirs[d][2];
|
||||||
for (int y = 0; y < sizeY; ++y) {
|
int u = axisU[d], v = axisV[d];
|
||||||
for (int z = 0; z < sizeZ; ++z) {
|
|
||||||
if (voxelData[x][y][z] != 0) {
|
// dims: dimensions along u,v, and w (the face-normal axis)
|
||||||
Quad q;
|
int dimU = (u==0?sizeX:(u==1?sizeY:sizeZ));
|
||||||
q.x = (float)x;
|
int dimV = (v==0?sizeX:(v==1?sizeY:sizeZ));
|
||||||
q.y = (float)y;
|
int dimW = (dx!=0?sizeX:(dy!=0?sizeY:sizeZ));
|
||||||
q.z = (float)z;
|
|
||||||
q.width = 1.0f;
|
// Allocate mask[dimU][dimV]
|
||||||
q.height = 1.0f;
|
std::vector<int> mask(dimU * dimV, 0);
|
||||||
q.textureID = voxelData[x][y][z];
|
|
||||||
quads.push_back(q);
|
// Sweep along the w axis
|
||||||
|
for (int w = 0; w <= dimW; w++) {
|
||||||
|
// build mask
|
||||||
|
for (int i = 0; i < dimU; i++) {
|
||||||
|
for (int j = 0; j < dimV; j++) {
|
||||||
|
int x,y,z;
|
||||||
|
// map (i,j,w) to (x,y,z)
|
||||||
|
int coord[3];
|
||||||
|
coord[u] = i;
|
||||||
|
coord[v] = j;
|
||||||
|
coord[ (dx!=0?0:(dy!=0?1:2)) ] = w;
|
||||||
|
x = coord[0]; y = coord[1]; z = coord[2];
|
||||||
|
int a = voxelAt(voxels, x, y, z);
|
||||||
|
coord[ (dx!=0?0:(dy!=0?1:2)) ] = w-1;
|
||||||
|
int b = voxelAt(voxels, coord[0], coord[1], coord[2]);
|
||||||
|
// if face between a and b should be drawn
|
||||||
|
if ((a!=0) != (b!=0)) {
|
||||||
|
// a is solid, b is empty → draw face on b side
|
||||||
|
mask[i + j*dimU] = (a!=0 ? a : -b);
|
||||||
|
} else {
|
||||||
|
mask[i + j*dimU] = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Greedy merge mask
|
||||||
|
for (int j = 0; j < dimV; j++) {
|
||||||
|
for (int i = 0; i < dimU; ) {
|
||||||
|
int c = mask[i + j*dimU];
|
||||||
|
if (c != 0) {
|
||||||
|
// determine width
|
||||||
|
int wdt = 1;
|
||||||
|
while (i+wdt < dimU && mask[i+wdt + j*dimU] == c) wdt++;
|
||||||
|
// determine height
|
||||||
|
int hgt = 1;
|
||||||
|
bool done = false;
|
||||||
|
while (j+hgt < dimV && !done) {
|
||||||
|
for (int k = 0; k < wdt; k++) {
|
||||||
|
if (mask[i+k + (j+hgt)*dimU] != c) {
|
||||||
|
done = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!done) hgt++;
|
||||||
|
}
|
||||||
|
// emit quad
|
||||||
|
Quad q;
|
||||||
|
// base position
|
||||||
|
float pos[3] = {0,0,0};
|
||||||
|
pos[0] = pos[1] = pos[2] = 0;
|
||||||
|
// set w axis coordinate
|
||||||
|
pos[(dx!=0?0:(dy!=0?1:2))] = w;
|
||||||
|
// but if this is a “back” face (a was empty, b solid), shift pos back
|
||||||
|
if (c < 0) pos[(dx!=0?0:(dy!=0?1:2))] = w-1;
|
||||||
|
// then set u,v
|
||||||
|
pos[u] = i;
|
||||||
|
pos[v] = j;
|
||||||
|
q.x = pos[0];
|
||||||
|
q.y = pos[1];
|
||||||
|
q.z = pos[2];
|
||||||
|
// set du and dv vectors
|
||||||
|
q.du[0] = q.du[1] = q.du[2] = 0;
|
||||||
|
q.dv[0] = q.dv[1] = q.dv[2] = 0;
|
||||||
|
q.du[u] = wdt;
|
||||||
|
q.dv[v] = hgt;
|
||||||
|
// normal
|
||||||
|
q.normal = d;
|
||||||
|
// textureID
|
||||||
|
q.textureID = abs(c);
|
||||||
|
quads.push_back(q);
|
||||||
|
// zero out mask
|
||||||
|
for (int jj = 0; jj < hgt; jj++)
|
||||||
|
for (int ii = 0; ii < wdt; ii++)
|
||||||
|
mask[i+ii + (j+jj)*dimU] = 0;
|
||||||
|
// advance
|
||||||
|
i += wdt;
|
||||||
|
} else {
|
||||||
|
i++;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,21 +3,25 @@
|
|||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
// Simple structure to hold a quad (a single face of a voxel block)
|
// A single quad face in world space.
|
||||||
struct Quad {
|
struct Quad {
|
||||||
// Starting position of the quad in world space
|
// Position of the minimal corner of the quad
|
||||||
float x, y, z;
|
float x, y, z;
|
||||||
// Dimensions of the quad (for this demo we assume unit quads)
|
// Dimensions of the quad along its two axes
|
||||||
float width, height;
|
float du[3], dv[3];
|
||||||
// Texture ID (1 = grass, 2 = dirt, 3 = wood)
|
// Offset of the second corner: (x+du[0]+dv[0], y+du[1]+dv[1], z+du[2]+dv[2])
|
||||||
|
// Normal direction (0..5) indicates which face this is:
|
||||||
|
// 0 = -X, 1 = +X, 2 = -Y, 3 = +Y, 4 = -Z, 5 = +Z
|
||||||
|
int normal;
|
||||||
|
// Texture ID for this face (block type)
|
||||||
int textureID;
|
int textureID;
|
||||||
};
|
};
|
||||||
|
|
||||||
class GreedyMesher {
|
// GreedyMesher namespace
|
||||||
public:
|
namespace GreedyMesher {
|
||||||
// Very basic greedy meshing algorithm:
|
// Given a 3D voxel grid [x][y][z] with integer block IDs (0 = empty),
|
||||||
// For demonstration, each non-zero voxel produces a quad.
|
// returns a list of merged Quad faces.
|
||||||
static std::vector<Quad> mesh(const std::vector<std::vector<std::vector<int>>>& voxelData);
|
std::vector<Quad> mesh(const std::vector<std::vector<std::vector<int>>>& voxels);
|
||||||
};
|
}
|
||||||
|
|
||||||
#endif // GREEDYMESHER_H
|
#endif // GREEDYMESHER_H
|
||||||
|
BIN
cpp-voxel-engine/GreedyMesher.o
Normal file
BIN
cpp-voxel-engine/GreedyMesher.o
Normal file
Binary file not shown.
@ -1,7 +1,7 @@
|
|||||||
# Compiler settings
|
# Compiler settings
|
||||||
CXX = g++
|
CXX = g++
|
||||||
CXXFLAGS = -std=c++11 -I. -Iimgui-docking -IC:/msys64/mingw64/include
|
CXXFLAGS = -std=c++20 -I. -Iimgui-docking -IC:/msys64/mingw64/include -DGLEW_STATIC -g
|
||||||
LDFLAGS = -LC:/msys64/mingw64/lib -lglfw3 -lopengl32 -lgdi32
|
LDFLAGS = -LC:/msys64/mingw64/lib -lglfw3 -lopengl32 -lgdi32 -lglew32 -lglu32
|
||||||
|
|
||||||
# List ImGui source files
|
# List ImGui source files
|
||||||
IMGUISRCS = imgui-docking/imgui.cpp \
|
IMGUISRCS = imgui-docking/imgui.cpp \
|
||||||
|
@ -1,16 +1,81 @@
|
|||||||
|
#define STB_IMAGE_IMPLEMENTATION
|
||||||
#include "VoxelGame.h"
|
#include "VoxelGame.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <GL/glew.h>
|
#include <GL/glew.h>
|
||||||
|
#include <GL/glu.h>
|
||||||
// Include stb_image implementation (ensure stb_image.h is in your include path)
|
|
||||||
#define STB_IMAGE_IMPLEMENTATION
|
|
||||||
#include "stb_image.h"
|
#include "stb_image.h"
|
||||||
|
#include <cmath>
|
||||||
|
#include <unordered_set>
|
||||||
|
#include "imgui.h"
|
||||||
|
#include "GreedyMesher.h"
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// --- Perlin Noise Helpers ---
|
||||||
|
float fade_(float t) {
|
||||||
|
return t * t * t * (t * (t * 6 - 15) + 10);
|
||||||
|
}
|
||||||
|
float lerp_(float a, float b, float t) {
|
||||||
|
return a + t * (b - a);
|
||||||
|
}
|
||||||
|
float grad_(int hash, float x, float y) {
|
||||||
|
int h = hash & 7;
|
||||||
|
float u = h < 4 ? x : y;
|
||||||
|
float v = h < 4 ? y : x;
|
||||||
|
return ((h & 1) ? -u : u) + ((h & 2) ? -v : v);
|
||||||
|
}
|
||||||
|
float perlin(float x, float y) {
|
||||||
|
static int p[512];
|
||||||
|
static bool init = false;
|
||||||
|
if (!init) {
|
||||||
|
int perm[256] = {
|
||||||
|
151,160,137,91,90,15,131,13,201,95,96,53,194,233,7,225,
|
||||||
|
140,36,103,30,69,142,8,99,37,240,21,10,23,190,6,148,
|
||||||
|
247,120,234,75,0,26,197,62,94,252,219,203,117,35,11,32,
|
||||||
|
57,177,33,88,237,149,56,87,174,20,125,136,171,168,68,175,
|
||||||
|
74,165,71,134,139,48,27,166,77,146,158,231,83,111,229,122,
|
||||||
|
60,211,133,230,220,105,92,41,55,46,245,40,244,102,143,54,
|
||||||
|
65,25,63,161,1,216,80,73,209,76,132,187,208,89,18,169,
|
||||||
|
200,196,135,130,116,188,159,86,164,100,109,198,173,186,3,64,
|
||||||
|
52,217,226,250,124,123,5,202,38,147,118,126,255,82,85,212,
|
||||||
|
207,206,59,227,47,16,58,17,182,189,28,42,223,183,170,213,
|
||||||
|
119,248,152,2,44,154,163,70,221,153,101,155,167,43,172,9,
|
||||||
|
129,22,39,253,19,98,108,110,79,113,224,232,178,185,112,104,
|
||||||
|
218,246,97,228,251,34,242,193,238,210,144,12,191,179,162,241,
|
||||||
|
81,51,145,235,249,14,239,107,49,192,214,31,181,199,106,157,
|
||||||
|
184,84,204,176,115,121,50,45,127,4,150,254,138,236,205,93,
|
||||||
|
222,114,67,29,24,72,243,141,128,195,78,66,215,61,156,180
|
||||||
|
};
|
||||||
|
for (int i = 0; i < 256; i++) {
|
||||||
|
p[i] = perm[i];
|
||||||
|
p[i + 256] = perm[i];
|
||||||
|
}
|
||||||
|
init = true;
|
||||||
|
}
|
||||||
|
int X = int(floor(x)) & 255;
|
||||||
|
int Y = int(floor(y)) & 255;
|
||||||
|
x -= floor(x);
|
||||||
|
y -= floor(y);
|
||||||
|
float u = fade_(x);
|
||||||
|
float v = fade_(y);
|
||||||
|
int A = p[X] + Y, B = p[X + 1] + Y;
|
||||||
|
return lerp_( v,
|
||||||
|
lerp_( u, grad_(p[A], x, y), grad_(p[A+1], x-1, y) ),
|
||||||
|
lerp_( u, grad_(p[B], x, y-1), grad_(p[B+1], x-1, y-1) )
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper to safely query voxel (returns 0 if out of bounds)
|
||||||
|
inline int voxelAt(const Chunk& c, int x, int y, int z) {
|
||||||
|
if (x<0||y<0||z<0||x>=16||y>=16||z>=16) return 0;
|
||||||
|
return c.voxels[x][y][z];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
VoxelGame::VoxelGame()
|
VoxelGame::VoxelGame()
|
||||||
: textureGrass(0), textureDirt(0), textureWood(0)
|
: textureGrass(0), textureDirt(0), textureWood(0),
|
||||||
|
totalChunksLoaded(0), totalChunksEverLoaded(0)
|
||||||
{
|
{
|
||||||
// Initialize a 16x16x16 voxel grid with zeros
|
|
||||||
voxelData.resize(16, std::vector<std::vector<int>>(16, std::vector<int>(16, 0)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
VoxelGame::~VoxelGame() {
|
VoxelGame::~VoxelGame() {
|
||||||
@ -19,112 +84,125 @@ VoxelGame::~VoxelGame() {
|
|||||||
glDeleteTextures(1, &textureWood);
|
glDeleteTextures(1, &textureWood);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
bool VoxelGame::init() {
|
bool VoxelGame::init() {
|
||||||
if (!loadTextures())
|
if (!loadTextures()) return false;
|
||||||
return false;
|
// Initial camera position
|
||||||
generateVoxelData();
|
float cam[3] = {32,32,80};
|
||||||
generateMesh();
|
updateChunks(cam);
|
||||||
|
// Build mesh for each chunk
|
||||||
|
for (auto &p : chunks) {
|
||||||
|
p.second.mesh = GreedyMesher::mesh(p.second.voxels);
|
||||||
|
}
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
glEnable(GL_TEXTURE_2D);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VoxelGame::loadTextures() {
|
bool VoxelGame::loadTextures() {
|
||||||
int width, height, nrChannels;
|
int w,h,n;
|
||||||
unsigned char *data;
|
unsigned char* data;
|
||||||
|
data = stbi_load("grass.jpg",&w,&h,&n,4);
|
||||||
// Load grass texture
|
if (!data) { std::cerr<<"grass.jpg load fail\n"; return false; }
|
||||||
data = stbi_load("grass.jpg", &width, &height, &nrChannels, 0);
|
glGenTextures(1,&textureGrass); glBindTexture(GL_TEXTURE_2D,textureGrass);
|
||||||
if (data) {
|
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
|
||||||
glGenTextures(1, &textureGrass);
|
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
|
||||||
glBindTexture(GL_TEXTURE_2D, textureGrass);
|
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,w,h,0,GL_RGBA,GL_UNSIGNED_BYTE,data);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
glGenerateMipmap(GL_TEXTURE_2D);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
stbi_image_free(data);
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
|
data = stbi_load("dirt.jpg",&w,&h,&n,4);
|
||||||
nrChannels == 4 ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, data);
|
if (!data) { std::cerr<<"dirt.jpg load fail\n"; return false; }
|
||||||
glGenerateMipmap(GL_TEXTURE_2D);
|
glGenTextures(1,&textureDirt); glBindTexture(GL_TEXTURE_2D,textureDirt);
|
||||||
stbi_image_free(data);
|
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
|
||||||
} else {
|
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
|
||||||
std::cerr << "Failed to load grass texture\n";
|
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
|
||||||
return false;
|
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
|
||||||
}
|
glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,w,h,0,GL_RGBA,GL_UNSIGNED_BYTE,data);
|
||||||
|
glGenerateMipmap(GL_TEXTURE_2D);
|
||||||
// Load dirt texture
|
stbi_image_free(data);
|
||||||
data = stbi_load("dirt.jpg", &width, &height, &nrChannels, 0);
|
data = stbi_load("wood.png",&w,&h,&n,4);
|
||||||
if (data) {
|
if (!data) { std::cerr<<"wood.png load fail\n"; return false; }
|
||||||
glGenTextures(1, &textureDirt);
|
glGenTextures(1,&textureWood); glBindTexture(GL_TEXTURE_2D,textureWood);
|
||||||
glBindTexture(GL_TEXTURE_2D, textureDirt);
|
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,w,h,0,GL_RGBA,GL_UNSIGNED_BYTE,data);
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
|
glGenerateMipmap(GL_TEXTURE_2D);
|
||||||
nrChannels == 4 ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, data);
|
stbi_image_free(data);
|
||||||
glGenerateMipmap(GL_TEXTURE_2D);
|
|
||||||
stbi_image_free(data);
|
|
||||||
} else {
|
|
||||||
std::cerr << "Failed to load dirt texture\n";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load wood texture
|
|
||||||
data = stbi_load("wood.png", &width, &height, &nrChannels, 0);
|
|
||||||
if (data) {
|
|
||||||
glGenTextures(1, &textureWood);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, textureWood);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0,
|
|
||||||
nrChannels == 4 ? GL_RGBA : GL_RGB, GL_UNSIGNED_BYTE, data);
|
|
||||||
glGenerateMipmap(GL_TEXTURE_2D);
|
|
||||||
stbi_image_free(data);
|
|
||||||
} else {
|
|
||||||
std::cerr << "Failed to load wood texture\n";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void VoxelGame::generateVoxelData() {
|
Chunk VoxelGame::generateChunk(int cx,int cz) {
|
||||||
// For demo purposes:
|
Chunk c; c.x=cx; c.z=cz;
|
||||||
// - Fill the bottom layer with grass (value 1)
|
c.voxels.assign(16, std::vector<std::vector<int>>(16, std::vector<int>(16,0)));
|
||||||
// - Fill the next layer with dirt (value 2)
|
float scale=0.1f;
|
||||||
// - Add a couple of wood blocks (value 3)
|
for(int x=0;x<16;x++)for(int z=0;z<16;z++){
|
||||||
for (int x = 0; x < 16; ++x) {
|
float wx = cx*16 + x, wz = cz*16 + z;
|
||||||
for (int z = 0; z < 16; ++z) {
|
float n = perlin(wx*scale, wz*scale);
|
||||||
voxelData[x][0][z] = 1; // grass layer
|
int h = int(((n+1)*0.5f)*16);
|
||||||
voxelData[x][1][z] = 2; // dirt layer
|
h = std::clamp(h,0,16);
|
||||||
|
for(int y=0;y<h;y++){
|
||||||
|
c.voxels[x][y][z] = (y==h-1?1:2);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Add some wood blocks as an example
|
return c;
|
||||||
voxelData[5][2][5] = 3;
|
|
||||||
voxelData[6][2][5] = 3;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VoxelGame::generateMesh() {
|
void VoxelGame::updateChunks(const float cam[3]) {
|
||||||
// Use the GreedyMesher to create mesh quads from voxel data.
|
int cx = int(floor(cam[0]/16)), cz=int(floor(cam[2]/16));
|
||||||
meshQuads = GreedyMesher::mesh(voxelData);
|
int r=2;
|
||||||
|
std::unordered_set<ChunkKey,ChunkKeyHash> keep;
|
||||||
|
for(int dx=-r;dx<=r;dx++)for(int dz=-r;dz<=r;dz++){
|
||||||
|
ChunkKey key{cx+dx,cz+dz};
|
||||||
|
keep.insert(key);
|
||||||
|
if(chunks.find(key)==chunks.end()){
|
||||||
|
Chunk nc = generateChunk(key.x,key.z);
|
||||||
|
nc.mesh = GreedyMesher::mesh(nc.voxels);
|
||||||
|
chunks[key]=std::move(nc);
|
||||||
|
totalChunksEverLoaded++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for(auto it=chunks.begin();it!=chunks.end();){
|
||||||
|
if(!keep.count(it->first)) it=chunks.erase(it);
|
||||||
|
else ++it;
|
||||||
|
}
|
||||||
|
totalChunksLoaded = chunks.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
void VoxelGame::update(float deltaTime) {
|
void VoxelGame::update(float dt,const float cam[3]){
|
||||||
// Update game logic (animations, input, physics, etc.)
|
updateChunks(cam);
|
||||||
// (This is left minimal for demo purposes)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void VoxelGame::render() {
|
void VoxelGame::render(){
|
||||||
// Here you would typically bind a shader, set uniforms, bind textures,
|
for(auto &p:chunks){
|
||||||
// and render the mesh (e.g. drawing quads as two triangles each).
|
float ox = p.first.x*16, oz = p.first.z*16;
|
||||||
// For this demo, we simply output the number of quads.
|
for(auto &q:p.second.mesh){
|
||||||
std::cout << "Rendering " << meshQuads.size() << " quads." << std::endl;
|
// shift by chunk offset
|
||||||
// You can extend this function to perform real OpenGL drawing.
|
float v0[3]={q.x+ox,q.y,q.z+oz},
|
||||||
|
v1[3]={q.x+q.du[0]+ox,q.y+q.du[1],q.z+q.du[2]+oz},
|
||||||
|
v2[3]={q.x+q.du[0]+q.dv[0]+ox,q.y+q.du[1]+q.dv[1],q.z+q.du[2]+q.dv[2]+oz},
|
||||||
|
v3[3]={q.x+q.dv[0]+ox,q.y+q.dv[1],q.z+q.dv[2]+oz};
|
||||||
|
unsigned int tex = (q.textureID==1?textureGrass:(q.textureID==2?textureDirt:textureWood));
|
||||||
|
glBindTexture(GL_TEXTURE_2D,tex);
|
||||||
|
glBegin(GL_QUADS);
|
||||||
|
glTexCoord2f(0,0); glVertex3fv(v0);
|
||||||
|
glTexCoord2f(1,0); glVertex3fv(v1);
|
||||||
|
glTexCoord2f(1,1); glVertex3fv(v2);
|
||||||
|
glTexCoord2f(0,1); glVertex3fv(v3);
|
||||||
|
glEnd();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VoxelGame::debugUI() {
|
void VoxelGame::debugUI(){
|
||||||
|
|
||||||
ImGui::Begin("Debug");
|
ImGui::Begin("Debug");
|
||||||
ImGui::Text("Voxel Game Debug Window");
|
ImGui::Text("Chunks Loaded: %d", totalChunksLoaded);
|
||||||
ImGui::Text("Mesh quads count: %d", (int)meshQuads.size());
|
ImGui::Text("Chunks Ever Loaded: %d", totalChunksEverLoaded);
|
||||||
|
static bool wf=false;
|
||||||
|
if(ImGui::Checkbox("Wireframe",&wf))
|
||||||
|
glPolygonMode(GL_FRONT_AND_BACK, wf?GL_LINE:GL_FILL);
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
@ -2,40 +2,57 @@
|
|||||||
#define VOXELGAME_H
|
#define VOXELGAME_H
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <unordered_map>
|
||||||
#include "GreedyMesher.h"
|
#include "GreedyMesher.h"
|
||||||
|
|
||||||
|
// Structure representing a chunk (16×16×16 voxels)
|
||||||
|
struct Chunk {
|
||||||
|
int x, z; // Chunk grid coordinates
|
||||||
|
// 3D voxel data: dimensions [16][16][16]
|
||||||
|
std::vector<std::vector<std::vector<int>>> voxels;
|
||||||
|
// **Merged mesh faces** produced by the greedy mesher
|
||||||
|
std::vector<Quad> mesh;
|
||||||
|
};
|
||||||
|
|
||||||
#include "imgui.h"
|
// A key for identifying a chunk by its grid coordinates.
|
||||||
#include "imgui_impl_glfw.h"
|
struct ChunkKey {
|
||||||
#include "imgui_impl_opengl3.h"
|
int x, z;
|
||||||
|
bool operator==(const ChunkKey &other) const {
|
||||||
|
return x == other.x && z == other.z;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ChunkKeyHash {
|
||||||
|
std::size_t operator()(const ChunkKey &key) const {
|
||||||
|
return std::hash<int>()(key.x) ^ (std::hash<int>()(key.z) << 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
class VoxelGame {
|
class VoxelGame {
|
||||||
public:
|
public:
|
||||||
VoxelGame();
|
VoxelGame();
|
||||||
~VoxelGame();
|
~VoxelGame();
|
||||||
|
|
||||||
// Initialize textures, voxel data, and mesher
|
|
||||||
bool init();
|
bool init();
|
||||||
|
void update(float deltaTime, const float cameraPos[3]);
|
||||||
// Game loop functions
|
|
||||||
void update(float deltaTime);
|
|
||||||
void render();
|
void render();
|
||||||
void debugUI();
|
void debugUI();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// 3D voxel data (0 = empty, 1 = grass, 2 = dirt, 3 = wood)
|
bool loadTextures();
|
||||||
std::vector<std::vector<std::vector<int>>> voxelData;
|
Chunk generateChunk(int cx, int cz);
|
||||||
// Mesh quads produced by the greedy mesher
|
void updateChunks(const float cameraPos[3]);
|
||||||
std::vector<Quad> meshQuads;
|
void drawCube(float x, float y, float z, int voxelType);
|
||||||
|
|
||||||
|
std::unordered_map<ChunkKey, Chunk, ChunkKeyHash> chunks;
|
||||||
|
int totalChunksLoaded;
|
||||||
|
int totalChunksEverLoaded;
|
||||||
|
|
||||||
// OpenGL texture IDs for each block type
|
|
||||||
unsigned int textureGrass;
|
unsigned int textureGrass;
|
||||||
unsigned int textureDirt;
|
unsigned int textureDirt;
|
||||||
unsigned int textureWood;
|
unsigned int textureWood;
|
||||||
|
|
||||||
bool loadTextures();
|
|
||||||
void generateVoxelData();
|
|
||||||
void generateMesh();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // VOXELGAME_H
|
#endif // VOXELGAME_H
|
||||||
|
BIN
cpp-voxel-engine/VoxelGame.o
Normal file
BIN
cpp-voxel-engine/VoxelGame.o
Normal file
Binary file not shown.
BIN
cpp-voxel-engine/imgui-docking/imgui.o
Normal file
BIN
cpp-voxel-engine/imgui-docking/imgui.o
Normal file
Binary file not shown.
BIN
cpp-voxel-engine/imgui-docking/imgui_demo.o
Normal file
BIN
cpp-voxel-engine/imgui-docking/imgui_demo.o
Normal file
Binary file not shown.
BIN
cpp-voxel-engine/imgui-docking/imgui_draw.o
Normal file
BIN
cpp-voxel-engine/imgui-docking/imgui_draw.o
Normal file
Binary file not shown.
BIN
cpp-voxel-engine/imgui-docking/imgui_impl_glfw.o
Normal file
BIN
cpp-voxel-engine/imgui-docking/imgui_impl_glfw.o
Normal file
Binary file not shown.
BIN
cpp-voxel-engine/imgui-docking/imgui_impl_opengl3.o
Normal file
BIN
cpp-voxel-engine/imgui-docking/imgui_impl_opengl3.o
Normal file
Binary file not shown.
BIN
cpp-voxel-engine/imgui-docking/imgui_tables.o
Normal file
BIN
cpp-voxel-engine/imgui-docking/imgui_tables.o
Normal file
Binary file not shown.
BIN
cpp-voxel-engine/imgui-docking/imgui_widgets.o
Normal file
BIN
cpp-voxel-engine/imgui-docking/imgui_widgets.o
Normal file
Binary file not shown.
10
cpp-voxel-engine/imgui.ini
Normal file
10
cpp-voxel-engine/imgui.ini
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
[Window][Debug##Default]
|
||||||
|
Pos=60,60
|
||||||
|
Size=400,400
|
||||||
|
Collapsed=0
|
||||||
|
|
||||||
|
[Window][Debug]
|
||||||
|
Pos=492,48
|
||||||
|
Size=214,119
|
||||||
|
Collapsed=0
|
||||||
|
|
@ -1,24 +1,67 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <cmath>
|
||||||
#include <GL/glew.h>
|
#include <GL/glew.h>
|
||||||
#include <GLFW/glfw3.h>
|
#include <GLFW/glfw3.h>
|
||||||
#include "VoxelGame.h"
|
#include <GL/glu.h>
|
||||||
|
|
||||||
// ImGui headers (make sure these files are available in your include path)
|
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
|
#include "imgui_internal.h" // For Im_PI
|
||||||
#include "imgui_impl_glfw.h"
|
#include "imgui_impl_glfw.h"
|
||||||
#include "imgui_impl_opengl3.h"
|
#include "imgui_impl_opengl3.h"
|
||||||
|
#include "VoxelGame.h"
|
||||||
|
|
||||||
|
// Global camera variables.
|
||||||
|
float yaw = -90.0f;
|
||||||
|
float pitch = 0.0f;
|
||||||
|
float lastX = 400.0f, lastY = 300.0f;
|
||||||
|
bool firstMouse = true;
|
||||||
|
float cameraPos[3] = {32.0f, 32.0f, 80.0f};
|
||||||
|
float cameraFront[3] = {0.0f, 0.0f, -1.0f};
|
||||||
|
float cameraUp[3] = {0.0f, 1.0f, 0.0f};
|
||||||
|
|
||||||
|
void mouse_callback(GLFWwindow* window, double xpos, double ypos) {
|
||||||
|
if (glfwGetInputMode(window, GLFW_CURSOR) == GLFW_CURSOR_NORMAL)
|
||||||
|
return;
|
||||||
|
if (firstMouse) {
|
||||||
|
lastX = static_cast<float>(xpos);
|
||||||
|
lastY = static_cast<float>(ypos);
|
||||||
|
firstMouse = false;
|
||||||
|
}
|
||||||
|
float xoffset = static_cast<float>(xpos) - lastX;
|
||||||
|
float yoffset = lastY - static_cast<float>(ypos);
|
||||||
|
lastX = static_cast<float>(xpos);
|
||||||
|
lastY = static_cast<float>(ypos);
|
||||||
|
|
||||||
|
float sensitivity = 0.1f;
|
||||||
|
xoffset *= sensitivity;
|
||||||
|
yoffset *= sensitivity;
|
||||||
|
|
||||||
|
yaw += xoffset;
|
||||||
|
pitch += yoffset;
|
||||||
|
if (pitch > 89.0f) pitch = 89.0f;
|
||||||
|
if (pitch < -89.0f) pitch = -89.0f;
|
||||||
|
|
||||||
|
float radYaw = yaw * static_cast<float>(IM_PI) / 180.0f;
|
||||||
|
float radPitch = pitch * static_cast<float>(IM_PI) / 180.0f;
|
||||||
|
|
||||||
|
cameraFront[0] = cos(radYaw) * cos(radPitch);
|
||||||
|
cameraFront[1] = sin(radPitch);
|
||||||
|
cameraFront[2] = sin(radYaw) * cos(radPitch);
|
||||||
|
float len = std::sqrt(cameraFront[0]*cameraFront[0] +
|
||||||
|
cameraFront[1]*cameraFront[1] +
|
||||||
|
cameraFront[2]*cameraFront[2]);
|
||||||
|
cameraFront[0] /= len;
|
||||||
|
cameraFront[1] /= len;
|
||||||
|
cameraFront[2] /= len;
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize GLFW and create a window
|
|
||||||
GLFWwindow* initWindow(int width, int height, const char* title) {
|
GLFWwindow* initWindow(int width, int height, const char* title) {
|
||||||
if (!glfwInit()) {
|
if (!glfwInit()) {
|
||||||
std::cerr << "Failed to initialize GLFW\n";
|
std::cerr << "Failed to initialize GLFW\n";
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
// Request OpenGL 3.3 Core Profile
|
|
||||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
|
||||||
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
|
||||||
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
|
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_COMPAT_PROFILE);
|
||||||
|
|
||||||
GLFWwindow* window = glfwCreateWindow(width, height, title, nullptr, nullptr);
|
GLFWwindow* window = glfwCreateWindow(width, height, title, nullptr, nullptr);
|
||||||
if (!window) {
|
if (!window) {
|
||||||
std::cerr << "Failed to create GLFW window\n";
|
std::cerr << "Failed to create GLFW window\n";
|
||||||
@ -26,8 +69,14 @@ GLFWwindow* initWindow(int width, int height, const char* title) {
|
|||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
glfwMakeContextCurrent(window);
|
glfwMakeContextCurrent(window);
|
||||||
|
glfwSetCursorPosCallback(window, mouse_callback);
|
||||||
|
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
|
||||||
|
|
||||||
|
glewExperimental = GL_TRUE;
|
||||||
|
if (glewInit() != GLEW_OK) {
|
||||||
|
std::cerr << "GLEW Error\n";
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
return window;
|
return window;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -36,58 +85,109 @@ int main() {
|
|||||||
if (!window)
|
if (!window)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
// Setup ImGui context
|
|
||||||
IMGUI_CHECKVERSION();
|
IMGUI_CHECKVERSION();
|
||||||
ImGui::CreateContext();
|
ImGui::CreateContext();
|
||||||
ImGuiIO &io = ImGui::GetIO();
|
ImGuiIO &io = ImGui::GetIO(); (void)io;
|
||||||
(void)io;
|
|
||||||
ImGui::StyleColorsDark();
|
ImGui::StyleColorsDark();
|
||||||
ImGui_ImplGlfw_InitForOpenGL(window, true);
|
ImGui_ImplGlfw_InitForOpenGL(window, true);
|
||||||
ImGui_ImplOpenGL3_Init("#version 330");
|
ImGui_ImplOpenGL3_Init("#version 330");
|
||||||
|
|
||||||
// Create game instance and initialize it
|
|
||||||
VoxelGame game;
|
VoxelGame game;
|
||||||
if (!game.init()) {
|
if (!game.init()) {
|
||||||
std::cerr << "Failed to initialize game\n";
|
std::cerr << "Failed to initialize game\n";
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main loop
|
float deltaTime = 0.0f, lastFrame = 0.0f;
|
||||||
|
static bool cursorEnabled = false;
|
||||||
|
static bool f1Pressed = false;
|
||||||
|
|
||||||
while (!glfwWindowShouldClose(window)) {
|
while (!glfwWindowShouldClose(window)) {
|
||||||
|
float currentFrame = static_cast<float>(glfwGetTime());
|
||||||
|
deltaTime = currentFrame - lastFrame;
|
||||||
|
lastFrame = currentFrame;
|
||||||
glfwPollEvents();
|
glfwPollEvents();
|
||||||
|
|
||||||
// Update game logic (assume fixed deltaTime for demo)
|
// Toggle cursor lock with F1 (if ImGui isn’t capturing the mouse).
|
||||||
game.update(0.016f);
|
if (glfwGetKey(window, GLFW_KEY_F1) == GLFW_PRESS && !ImGui::GetIO().WantCaptureMouse) {
|
||||||
|
if (!f1Pressed) {
|
||||||
|
cursorEnabled = !cursorEnabled;
|
||||||
|
glfwSetInputMode(window, GLFW_CURSOR, cursorEnabled ? GLFW_CURSOR_NORMAL : GLFW_CURSOR_DISABLED);
|
||||||
|
f1Pressed = true;
|
||||||
|
firstMouse = true;
|
||||||
|
}
|
||||||
|
} else if (glfwGetKey(window, GLFW_KEY_F1) == GLFW_RELEASE) {
|
||||||
|
f1Pressed = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
float cameraSpeed = 5.0f * deltaTime;
|
||||||
|
if (glfwGetKey(window, GLFW_KEY_W) == GLFW_PRESS) {
|
||||||
|
cameraPos[0] += cameraFront[0] * cameraSpeed;
|
||||||
|
cameraPos[1] += cameraFront[1] * cameraSpeed;
|
||||||
|
cameraPos[2] += cameraFront[2] * cameraSpeed;
|
||||||
|
}
|
||||||
|
if (glfwGetKey(window, GLFW_KEY_S) == GLFW_PRESS) {
|
||||||
|
cameraPos[0] -= cameraFront[0] * cameraSpeed;
|
||||||
|
cameraPos[1] -= cameraFront[1] * cameraSpeed;
|
||||||
|
cameraPos[2] -= cameraFront[2] * cameraSpeed;
|
||||||
|
}
|
||||||
|
if (glfwGetKey(window, GLFW_KEY_A) == GLFW_PRESS) {
|
||||||
|
float right[3] = {
|
||||||
|
cameraFront[1]*cameraUp[2] - cameraFront[2]*cameraUp[1],
|
||||||
|
cameraFront[2]*cameraUp[0] - cameraFront[0]*cameraUp[2],
|
||||||
|
cameraFront[0]*cameraUp[1] - cameraFront[1]*cameraUp[0]
|
||||||
|
};
|
||||||
|
float rLen = std::sqrt(right[0]*right[0] + right[1]*right[1] + right[2]*right[2]);
|
||||||
|
right[0] /= rLen; right[1] /= rLen; right[2] /= rLen;
|
||||||
|
cameraPos[0] -= right[0] * cameraSpeed;
|
||||||
|
cameraPos[1] -= right[1] * cameraSpeed;
|
||||||
|
cameraPos[2] -= right[2] * cameraSpeed;
|
||||||
|
}
|
||||||
|
if (glfwGetKey(window, GLFW_KEY_D) == GLFW_PRESS) {
|
||||||
|
float right[3] = {
|
||||||
|
cameraFront[1]*cameraUp[2] - cameraFront[2]*cameraUp[1],
|
||||||
|
cameraFront[2]*cameraUp[0] - cameraFront[0]*cameraUp[2],
|
||||||
|
cameraFront[0]*cameraUp[1] - cameraFront[1]*cameraUp[0]
|
||||||
|
};
|
||||||
|
float rLen = std::sqrt(right[0]*right[0] + right[1]*right[1] + right[2]*right[2]);
|
||||||
|
right[0] /= rLen; right[1] /= rLen; right[2] /= rLen;
|
||||||
|
cameraPos[0] += right[0] * cameraSpeed;
|
||||||
|
cameraPos[1] += right[1] * cameraSpeed;
|
||||||
|
cameraPos[2] += right[2] * cameraSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
// Start new ImGui frame
|
|
||||||
ImGui_ImplOpenGL3_NewFrame();
|
ImGui_ImplOpenGL3_NewFrame();
|
||||||
ImGui_ImplGlfw_NewFrame();
|
ImGui_ImplGlfw_NewFrame();
|
||||||
ImGui::NewFrame();
|
ImGui::NewFrame();
|
||||||
|
|
||||||
// Draw debug UI from game
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
|
glMatrixMode(GL_PROJECTION);
|
||||||
|
glLoadIdentity();
|
||||||
|
gluPerspective(45.0, 800.0/600.0, 0.1, 200.0);
|
||||||
|
|
||||||
|
glMatrixMode(GL_MODELVIEW);
|
||||||
|
glLoadIdentity();
|
||||||
|
float centerX = cameraPos[0] + cameraFront[0];
|
||||||
|
float centerY = cameraPos[1] + cameraFront[1];
|
||||||
|
float centerZ = cameraPos[2] + cameraFront[2];
|
||||||
|
gluLookAt(cameraPos[0], cameraPos[1], cameraPos[2],
|
||||||
|
centerX, centerY, centerZ,
|
||||||
|
cameraUp[0], cameraUp[1], cameraUp[2]);
|
||||||
|
|
||||||
|
game.update(deltaTime, cameraPos);
|
||||||
|
game.render();
|
||||||
game.debugUI();
|
game.debugUI();
|
||||||
|
|
||||||
ImGui::Render();
|
ImGui::Render();
|
||||||
|
|
||||||
// Clear the screen
|
|
||||||
glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT);
|
|
||||||
|
|
||||||
// Render the voxel game (mesh drawing, etc.)
|
|
||||||
game.render();
|
|
||||||
|
|
||||||
// Render ImGui on top
|
|
||||||
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());
|
||||||
|
|
||||||
glfwSwapBuffers(window);
|
glfwSwapBuffers(window);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Cleanup ImGui and GLFW
|
|
||||||
ImGui_ImplOpenGL3_Shutdown();
|
ImGui_ImplOpenGL3_Shutdown();
|
||||||
ImGui_ImplGlfw_Shutdown();
|
ImGui_ImplGlfw_Shutdown();
|
||||||
ImGui::DestroyContext();
|
ImGui::DestroyContext();
|
||||||
glfwDestroyWindow(window);
|
glfwDestroyWindow(window);
|
||||||
glfwTerminate();
|
glfwTerminate();
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
BIN
cpp-voxel-engine/main.o
Normal file
BIN
cpp-voxel-engine/main.o
Normal file
Binary file not shown.
BIN
cpp-voxel-engine/voxelgame.exe
Normal file
BIN
cpp-voxel-engine/voxelgame.exe
Normal file
Binary file not shown.
Loading…
Reference in New Issue
Block a user