Added infinite terrain
This commit is contained in:
28
.vscode/tasks.json
vendored
Normal file
28
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
{
|
||||||
|
"tasks": [
|
||||||
|
{
|
||||||
|
"type": "cppbuild",
|
||||||
|
"label": "C/C++: gcc build active file",
|
||||||
|
"command": "/usr/bin/gcc",
|
||||||
|
"args": [
|
||||||
|
"-fdiagnostics-color=always",
|
||||||
|
"-g",
|
||||||
|
"${file}",
|
||||||
|
"-o",
|
||||||
|
"${fileDirname}/${fileBasenameNoExtension}"
|
||||||
|
],
|
||||||
|
"options": {
|
||||||
|
"cwd": "${fileDirname}"
|
||||||
|
},
|
||||||
|
"problemMatcher": [
|
||||||
|
"$gcc"
|
||||||
|
],
|
||||||
|
"group": {
|
||||||
|
"kind": "build",
|
||||||
|
"isDefault": true
|
||||||
|
},
|
||||||
|
"detail": "Task generated by Debugger."
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"version": "2.0.0"
|
||||||
|
}
|
||||||
565
main.cpp
565
main.cpp
@@ -7,150 +7,159 @@
|
|||||||
|
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <array>
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
#include <unordered_map>
|
#include <unordered_map>
|
||||||
|
#include <memory>
|
||||||
|
#include <algorithm>
|
||||||
|
#include <climits>
|
||||||
|
|
||||||
// Instalation
|
// Build:
|
||||||
// On root:
|
// mkdir build && cd build && cmake .. && make -j$(nproc)
|
||||||
// mkdir build
|
|
||||||
// cmake .
|
|
||||||
// make -j$(nproc)
|
|
||||||
// ./minicraft
|
// ./minicraft
|
||||||
|
|
||||||
// ─── Constants ───────────────────────────────────────────────────────────────
|
|
||||||
static const int SCREEN_W = 1280;
|
static const int SCREEN_W = 1280;
|
||||||
static const int SCREEN_H = 720;
|
static const int SCREEN_H = 720;
|
||||||
static const int CHUNK_SIZE = 16;
|
static const int CHUNK_SIZE = 16;
|
||||||
static const int WORLD_H = 16;
|
static const int WORLD_H = 64;
|
||||||
static const float MOVE_SPEED = 5.0f;
|
static const int RENDER_DIST = 6;
|
||||||
|
static const float MOVE_SPEED = 6.0f;
|
||||||
static const float MOUSE_SENS = 0.12f;
|
static const float MOUSE_SENS = 0.12f;
|
||||||
static const float GRAVITY = -20.0f;
|
static const float GRAVITY = -22.0f;
|
||||||
static const float JUMP_VEL = 8.0f;
|
static const float JUMP_VEL = 9.0f;
|
||||||
|
static const float HW = 0.3f;
|
||||||
|
static const float PH = 1.8f;
|
||||||
|
|
||||||
// ─── Block types ─────────────────────────────────────────────────────────────
|
enum BlockType : uint8_t { AIR=0, GRASS, DIRT, STONE, WOOD, LEAVES, SAND, SNOW, WATER };
|
||||||
enum BlockType : uint8_t { AIR=0, GRASS, DIRT, STONE, WOOD, LEAVES, SAND, WATER };
|
|
||||||
|
|
||||||
struct BlockColor { float r,g,b; };
|
struct BlockColor { float r,g,b; };
|
||||||
static const BlockColor BLOCK_COLORS[] = {
|
static const BlockColor BLOCK_COLORS[] = {
|
||||||
{0,0,0}, // AIR
|
{0,0,0},
|
||||||
{0.40f,0.72f,0.24f}, // GRASS
|
{0.40f,0.72f,0.24f},
|
||||||
{0.55f,0.40f,0.22f}, // DIRT
|
{0.55f,0.40f,0.22f},
|
||||||
{0.55f,0.55f,0.55f}, // STONE
|
{0.50f,0.50f,0.50f},
|
||||||
{0.45f,0.30f,0.15f}, // WOOD
|
{0.45f,0.30f,0.15f},
|
||||||
{0.20f,0.60f,0.15f}, // LEAVES
|
{0.22f,0.55f,0.18f},
|
||||||
{0.90f,0.85f,0.55f}, // SAND
|
{0.88f,0.83f,0.52f},
|
||||||
{0.20f,0.45f,0.90f}, // WATER
|
{0.92f,0.95f,0.98f},
|
||||||
|
{0.18f,0.42f,0.88f},
|
||||||
|
};
|
||||||
|
static const float FACE_SHADE[] = {1.0f, 0.5f, 0.8f, 0.7f, 0.65f, 0.65f};
|
||||||
|
|
||||||
|
// Perlin noise
|
||||||
|
static int P[512];
|
||||||
|
static void initNoise(int seed){
|
||||||
|
int perm[256]; for(int i=0;i<256;i++) perm[i]=i;
|
||||||
|
unsigned rng=(unsigned)seed;
|
||||||
|
for(int i=255;i>0;i--){ rng=rng*1664525u+1013904223u; int j=(rng>>16)%(i+1); std::swap(perm[i],perm[j]); }
|
||||||
|
for(int i=0;i<512;i++) P[i]=perm[i&255];
|
||||||
|
}
|
||||||
|
static float fade(float t){return t*t*t*(t*(t*6-15)+10);}
|
||||||
|
static float lerp(float a,float b,float t){return a+t*(b-a);}
|
||||||
|
static float grad(int h,float x,float y){h&=3;float u=(h<2)?x:y,v=(h<2)?y:x;return((h&1)?-u:u)+((h&2)?-v:v);}
|
||||||
|
static float noise2(float x,float y){
|
||||||
|
int xi=(int)floorf(x)&255,yi=(int)floorf(y)&255;
|
||||||
|
float xf=x-floorf(x),yf=y-floorf(y),u=fade(xf),v=fade(yf);
|
||||||
|
int aa=P[P[xi]+yi],ab=P[P[xi]+yi+1],ba=P[P[xi+1]+yi],bb=P[P[xi+1]+yi+1];
|
||||||
|
return lerp(lerp(grad(aa,xf,yf),grad(ba,xf-1,yf),u),lerp(grad(ab,xf,yf-1),grad(bb,xf-1,yf-1),u),v);
|
||||||
|
}
|
||||||
|
static float fbm(float x,float y,int oct=6){
|
||||||
|
float val=0,amp=1,freq=1,max=0;
|
||||||
|
for(int i=0;i<oct;i++){val+=noise2(x*freq,y*freq)*amp;max+=amp;amp*=0.5f;freq*=2.0f;}
|
||||||
|
return val/max;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Chunk {
|
||||||
|
int cx,cz;
|
||||||
|
uint8_t blocks[CHUNK_SIZE][WORLD_H][CHUNK_SIZE];
|
||||||
|
GLuint VAO=0,VBO=0,EBO=0;
|
||||||
|
int indexCount=0;
|
||||||
|
bool meshDirty=true,generated=false;
|
||||||
|
Chunk(int cx,int cz):cx(cx),cz(cz){memset(blocks,AIR,sizeof(blocks));}
|
||||||
|
~Chunk(){if(VAO){glDeleteVertexArrays(1,&VAO);glDeleteBuffers(1,&VBO);glDeleteBuffers(1,&EBO);}}
|
||||||
};
|
};
|
||||||
|
|
||||||
// face shade multipliers (top, bottom, front, back, left, right)
|
struct ChunkKey{int x,z;bool operator==(const ChunkKey& o)const{return x==o.x&&z==o.z;}};
|
||||||
static const float FACE_SHADE[] = {1.0f, 0.5f, 0.8f, 0.8f, 0.65f, 0.65f};
|
struct ChunkKeyHash{size_t operator()(const ChunkKey& k)const{return std::hash<long long>()((long long)k.x<<32|(unsigned)k.z);}};
|
||||||
|
using ChunkMap=std::unordered_map<ChunkKey,std::unique_ptr<Chunk>,ChunkKeyHash>;
|
||||||
|
static ChunkMap CHUNKS;
|
||||||
|
|
||||||
// ─── World ───────────────────────────────────────────────────────────────────
|
static void generateChunk(Chunk& c){
|
||||||
static uint8_t WORLD[CHUNK_SIZE][WORLD_H][CHUNK_SIZE];
|
int wx0=c.cx*CHUNK_SIZE,wz0=c.cz*CHUNK_SIZE;
|
||||||
|
for(int x=0;x<CHUNK_SIZE;x++)
|
||||||
static int terrainHeight(int x, int z) {
|
for(int z=0;z<CHUNK_SIZE;z++){
|
||||||
float h = 5.0f
|
float wx=(wx0+x)*0.008f,wz=(wz0+z)*0.008f;
|
||||||
+ 3.0f * sinf(x * 0.25f) * cosf(z * 0.20f)
|
float cont=fbm(wx*0.4f,wz*0.4f,4);
|
||||||
+ 2.0f * sinf(x * 0.10f + z * 0.13f)
|
float detail=fbm(wx*2.0f+5.3f,wz*2.0f+1.7f,5);
|
||||||
+ 1.0f * cosf(x * 0.40f - z * 0.35f);
|
float ridge=1.0f-fabsf(fbm(wx*0.8f+3.1f,wz*0.8f+8.9f,4));
|
||||||
return (int)h;
|
float h=12.0f+cont*18.0f+detail*6.0f+ridge*14.0f*std::max(0.0f,cont);
|
||||||
|
int top=(int)h;
|
||||||
|
top=std::max(2,std::min(WORLD_H-2,top));
|
||||||
|
bool isSand=(top<16),isSnow=(top>38);
|
||||||
|
for(int y=0;y<WORLD_H;y++){
|
||||||
|
if(y==0) c.blocks[x][y][z]=STONE;
|
||||||
|
else if(y<top-4) c.blocks[x][y][z]=STONE;
|
||||||
|
else if(y<top) c.blocks[x][y][z]=isSand?SAND:DIRT;
|
||||||
|
else if(y==top){
|
||||||
|
if(isSand) c.blocks[x][y][z]=SAND;
|
||||||
|
else if(isSnow) c.blocks[x][y][z]=SNOW;
|
||||||
|
else c.blocks[x][y][z]=GRASS;
|
||||||
|
} else if(y<=14) c.blocks[x][y][z]=WATER;
|
||||||
|
else c.blocks[x][y][z]=AIR;
|
||||||
}
|
}
|
||||||
|
if(!isSand&&!isSnow&&top>=16&&top<36&&top+6<WORLD_H){
|
||||||
static void generateWorld() {
|
unsigned col=(unsigned)((wx0+x)*73856093u^(unsigned)(wz0+z)*19349663u);
|
||||||
memset(WORLD, AIR, sizeof(WORLD));
|
if((col&0xFF)<18){
|
||||||
for (int x = 0; x < CHUNK_SIZE; ++x)
|
|
||||||
for (int z = 0; z < CHUNK_SIZE; ++z) {
|
|
||||||
int top = terrainHeight(x, z);
|
|
||||||
if (top < 1) top = 1;
|
|
||||||
if (top >= WORLD_H) top = WORLD_H - 1;
|
|
||||||
for (int y = 0; y < WORLD_H; ++y) {
|
|
||||||
if (y == 0) WORLD[x][y][z] = STONE;
|
|
||||||
else if (y < top - 3) WORLD[x][y][z] = STONE;
|
|
||||||
else if (y < top) WORLD[x][y][z] = DIRT;
|
|
||||||
else if (y == top) WORLD[x][y][z] = (top <= 3) ? SAND : GRASS;
|
|
||||||
}
|
|
||||||
// Small trees
|
|
||||||
if (top > 3 && top < WORLD_H - 5 && (x + z * 7) % 13 == 0) {
|
|
||||||
int trunk=top+1;
|
int trunk=top+1;
|
||||||
for (int t = trunk; t < trunk + 3 && t < WORLD_H; ++t)
|
for(int t=trunk;t<trunk+4&&t<WORLD_H;t++) c.blocks[x][t][z]=WOOD;
|
||||||
WORLD[x][t][z] = WOOD;
|
for(int dx=-2;dx<=2;dx++)
|
||||||
for (int dx = -1; dx <= 1; ++dx)
|
for(int dz=-2;dz<=2;dz++)
|
||||||
for (int dz = -1; dz <= 1; ++dz)
|
for(int dy=trunk+2;dy<=trunk+5;dy++){
|
||||||
for (int dy = trunk + 2; dy <= trunk + 4; ++dy) {
|
|
||||||
int nx=x+dx,nz=z+dz;
|
int nx=x+dx,nz=z+dz;
|
||||||
if(nx>=0&&nx<CHUNK_SIZE&&nz>=0&&nz<CHUNK_SIZE&&dy<WORLD_H)
|
if(nx>=0&&nx<CHUNK_SIZE&&nz>=0&&nz<CHUNK_SIZE&&dy<WORLD_H)
|
||||||
if (WORLD[nx][dy][nz] == AIR)
|
if(c.blocks[nx][dy][nz]==AIR) c.blocks[nx][dy][nz]=LEAVES;
|
||||||
WORLD[nx][dy][nz] = LEAVES;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
c.generated=true;
|
||||||
|
}
|
||||||
|
|
||||||
// ─── Shaders ─────────────────────────────────────────────────────────────────
|
static uint8_t getBlock(int wx,int wy,int wz){
|
||||||
static const char* VS_SRC = R"(
|
if(wy<0) return STONE;
|
||||||
#version 330 core
|
if(wy>=WORLD_H) return AIR;
|
||||||
layout(location=0) in vec3 aPos;
|
int cx=(int)floorf((float)wx/CHUNK_SIZE),cz=(int)floorf((float)wz/CHUNK_SIZE);
|
||||||
layout(location=1) in vec3 aColor;
|
auto it=CHUNKS.find({cx,cz});
|
||||||
out vec3 vColor;
|
if(it==CHUNKS.end()) return STONE;
|
||||||
uniform mat4 uMVP;
|
int lx=wx-cx*CHUNK_SIZE,lz=wz-cz*CHUNK_SIZE;
|
||||||
void main(){
|
return it->second->blocks[lx][wy][lz];
|
||||||
gl_Position = uMVP * vec4(aPos, 1.0);
|
|
||||||
vColor = aColor;
|
|
||||||
}
|
|
||||||
)";
|
|
||||||
|
|
||||||
static const char* FS_SRC = R"(
|
|
||||||
#version 330 core
|
|
||||||
in vec3 vColor;
|
|
||||||
out vec4 fragColor;
|
|
||||||
void main(){
|
|
||||||
fragColor = vec4(vColor, 1.0);
|
|
||||||
}
|
|
||||||
)";
|
|
||||||
|
|
||||||
static GLuint compileShader(GLenum type, const char* src) {
|
|
||||||
GLuint s = glCreateShader(type);
|
|
||||||
glShaderSource(s, 1, &src, nullptr);
|
|
||||||
glCompileShader(s);
|
|
||||||
GLint ok; glGetShaderiv(s, GL_COMPILE_STATUS, &ok);
|
|
||||||
if (!ok) {
|
|
||||||
char buf[512]; glGetShaderInfoLog(s, 512, nullptr, buf);
|
|
||||||
std::cerr << "Shader error: " << buf << "\n";
|
|
||||||
}
|
|
||||||
return s;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static GLuint buildProgram() {
|
static void setBlock(int wx,int wy,int wz,uint8_t val){
|
||||||
GLuint vs = compileShader(GL_VERTEX_SHADER, VS_SRC);
|
if(wy<0||wy>=WORLD_H) return;
|
||||||
GLuint fs = compileShader(GL_FRAGMENT_SHADER, FS_SRC);
|
int cx=(int)floorf((float)wx/CHUNK_SIZE),cz=(int)floorf((float)wz/CHUNK_SIZE);
|
||||||
GLuint p = glCreateProgram();
|
auto it=CHUNKS.find({cx,cz});
|
||||||
glAttachShader(p, vs); glAttachShader(p, fs);
|
if(it==CHUNKS.end()) return;
|
||||||
glLinkProgram(p);
|
int lx=wx-cx*CHUNK_SIZE,lz=wz-cz*CHUNK_SIZE;
|
||||||
glDeleteShader(vs); glDeleteShader(fs);
|
it->second->blocks[lx][wy][lz]=val;
|
||||||
return p;
|
it->second->meshDirty=true;
|
||||||
|
if(lx==0){auto n=CHUNKS.find({cx-1,cz});if(n!=CHUNKS.end())n->second->meshDirty=true;}
|
||||||
|
if(lx==CHUNK_SIZE-1){auto n=CHUNKS.find({cx+1,cz});if(n!=CHUNKS.end())n->second->meshDirty=true;}
|
||||||
|
if(lz==0){auto n=CHUNKS.find({cx,cz-1});if(n!=CHUNKS.end())n->second->meshDirty=true;}
|
||||||
|
if(lz==CHUNK_SIZE-1){auto n=CHUNKS.find({cx,cz+1});if(n!=CHUNKS.end())n->second->meshDirty=true;}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Mesh builder ────────────────────────────────────────────────────────────
|
|
||||||
struct Vertex{float x,y,z,r,g,b;};
|
struct Vertex{float x,y,z,r,g,b;};
|
||||||
|
|
||||||
// Checks if we should draw a face against a neighboring block
|
static bool shouldDrawFace(uint8_t cur,int wx,int wy,int wz){
|
||||||
static bool shouldDrawFace(uint8_t currentBlock, int nx, int ny, int nz) {
|
uint8_t nb=getBlock(wx,wy,wz);
|
||||||
if (nx<0 || nx>=CHUNK_SIZE || ny<0 || ny>=WORLD_H || nz<0 || nz>=CHUNK_SIZE) return true;
|
|
||||||
uint8_t nb = WORLD[nx][ny][nz];
|
|
||||||
if(nb==AIR) return true;
|
if(nb==AIR) return true;
|
||||||
// Transparent blocks like water/leaves hide internal faces of the *same* type,
|
if(nb==WATER||nb==LEAVES) return cur!=nb;
|
||||||
// but allow drawing faces of different adjacent blocks.
|
return false;
|
||||||
if (nb == WATER || nb == LEAVES) {
|
|
||||||
return currentBlock != nb;
|
|
||||||
}
|
|
||||||
return false; // Solid opaque block, hide the face
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void addFace(std::vector<Vertex>& verts,std::vector<GLuint>& idx,
|
static void addFace(std::vector<Vertex>& verts,std::vector<GLuint>& idx,
|
||||||
glm::vec3 v0, glm::vec3 v1, glm::vec3 v2, glm::vec3 v3,
|
glm::vec3 v0,glm::vec3 v1,glm::vec3 v2,glm::vec3 v3,float r,float g,float b,float shade){
|
||||||
float r, float g, float b, float shade) {
|
|
||||||
GLuint base=(GLuint)verts.size();
|
GLuint base=(GLuint)verts.size();
|
||||||
float sr=r*shade,sg=g*shade,sb=b*shade;
|
float sr=r*shade,sg=g*shade,sb=b*shade;
|
||||||
verts.push_back({v0.x,v0.y,v0.z,sr,sg,sb});
|
verts.push_back({v0.x,v0.y,v0.z,sr,sg,sb});
|
||||||
@@ -160,146 +169,142 @@ static void addFace(std::vector<Vertex>& verts, std::vector<GLuint>& idx,
|
|||||||
idx.insert(idx.end(),{base,base+1,base+2,base,base+2,base+3});
|
idx.insert(idx.end(),{base,base+1,base+2,base,base+2,base+3});
|
||||||
}
|
}
|
||||||
|
|
||||||
static void buildMesh(std::vector<Vertex>& verts, std::vector<GLuint>& idx) {
|
static void buildChunkMesh(Chunk& c){
|
||||||
verts.clear(); idx.clear();
|
std::vector<Vertex> verts; std::vector<GLuint> idx;
|
||||||
for (int x=0;x<CHUNK_SIZE;++x)
|
int wx0=c.cx*CHUNK_SIZE,wz0=c.cz*CHUNK_SIZE;
|
||||||
for (int y=0;y<WORLD_H;++y)
|
for(int x=0;x<CHUNK_SIZE;x++)
|
||||||
for (int z=0;z<CHUNK_SIZE;++z) {
|
for(int y=0;y<WORLD_H;y++)
|
||||||
uint8_t b = WORLD[x][y][z];
|
for(int z=0;z<CHUNK_SIZE;z++){
|
||||||
if (b == AIR) continue;
|
uint8_t b=c.blocks[x][y][z]; if(b==AIR) continue;
|
||||||
float r=BLOCK_COLORS[b].r,g=BLOCK_COLORS[b].g,bl=BLOCK_COLORS[b].b;
|
float r=BLOCK_COLORS[b].r,g=BLOCK_COLORS[b].g,bl=BLOCK_COLORS[b].b;
|
||||||
float bx=x,by=y,bz=z;
|
float bx=wx0+x,by=y,bz=wz0+z;
|
||||||
|
int wx=wx0+x,wz=wz0+z;
|
||||||
// FIXED Winding Orders:
|
if(shouldDrawFace(b,wx,y+1,wz)) addFace(verts,idx,{bx,by+1,bz+1},{bx+1,by+1,bz+1},{bx+1,by+1,bz},{bx,by+1,bz},r,g,bl,FACE_SHADE[0]);
|
||||||
// Top (+Y)
|
if(shouldDrawFace(b,wx,y-1,wz)) addFace(verts,idx,{bx,by,bz},{bx+1,by,bz},{bx+1,by,bz+1},{bx,by,bz+1},r,g,bl,FACE_SHADE[1]);
|
||||||
if (shouldDrawFace(b, x,y+1,z))
|
if(shouldDrawFace(b,wx,y,wz+1)) addFace(verts,idx,{bx,by,bz+1},{bx+1,by,bz+1},{bx+1,by+1,bz+1},{bx,by+1,bz+1},r,g,bl,FACE_SHADE[2]);
|
||||||
addFace(verts,idx,{bx,by+1,bz+1},{bx+1,by+1,bz+1},{bx+1,by+1,bz},{bx,by+1,bz},r,g,bl,FACE_SHADE[0]);
|
if(shouldDrawFace(b,wx,y,wz-1)) addFace(verts,idx,{bx+1,by,bz},{bx,by,bz},{bx,by+1,bz},{bx+1,by+1,bz},r,g,bl,FACE_SHADE[3]);
|
||||||
// Bottom (-Y)
|
if(shouldDrawFace(b,wx-1,y,wz)) addFace(verts,idx,{bx,by,bz},{bx,by,bz+1},{bx,by+1,bz+1},{bx,by+1,bz},r,g,bl,FACE_SHADE[4]);
|
||||||
if (shouldDrawFace(b, x,y-1,z))
|
if(shouldDrawFace(b,wx+1,y,wz)) addFace(verts,idx,{bx+1,by,bz+1},{bx+1,by,bz},{bx+1,by+1,bz},{bx+1,by+1,bz+1},r,g,bl,FACE_SHADE[5]);
|
||||||
addFace(verts,idx,{bx,by,bz},{bx+1,by,bz},{bx+1,by,bz+1},{bx,by,bz+1},r,g,bl,FACE_SHADE[1]);
|
|
||||||
// Front (+Z)
|
|
||||||
if (shouldDrawFace(b, x,y,z+1))
|
|
||||||
addFace(verts,idx,{bx,by,bz+1},{bx+1,by,bz+1},{bx+1,by+1,bz+1},{bx,by+1,bz+1},r,g,bl,FACE_SHADE[2]);
|
|
||||||
// Back (-Z)
|
|
||||||
if (shouldDrawFace(b, x,y,z-1))
|
|
||||||
addFace(verts,idx,{bx+1,by,bz},{bx,by,bz},{bx,by+1,bz},{bx+1,by+1,bz},r,g,bl,FACE_SHADE[3]);
|
|
||||||
// Left (-X)
|
|
||||||
if (shouldDrawFace(b, x-1,y,z))
|
|
||||||
addFace(verts,idx,{bx,by,bz},{bx,by,bz+1},{bx,by+1,bz+1},{bx,by+1,bz},r,g,bl,FACE_SHADE[4]);
|
|
||||||
// Right (+X)
|
|
||||||
if (shouldDrawFace(b, x+1,y,z))
|
|
||||||
addFace(verts,idx,{bx+1,by,bz+1},{bx+1,by,bz},{bx+1,by+1,bz},{bx+1,by+1,bz+1},r,g,bl,FACE_SHADE[5]);
|
|
||||||
}
|
}
|
||||||
|
if(!c.VAO){glGenVertexArrays(1,&c.VAO);glGenBuffers(1,&c.VBO);glGenBuffers(1,&c.EBO);}
|
||||||
|
glBindVertexArray(c.VAO);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER,c.VBO);
|
||||||
|
glBufferData(GL_ARRAY_BUFFER,verts.size()*sizeof(Vertex),verts.data(),GL_DYNAMIC_DRAW);
|
||||||
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,c.EBO);
|
||||||
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER,idx.size()*sizeof(GLuint),idx.data(),GL_DYNAMIC_DRAW);
|
||||||
|
glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,sizeof(Vertex),(void*)0);
|
||||||
|
glEnableVertexAttribArray(0);
|
||||||
|
glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,sizeof(Vertex),(void*)(3*sizeof(float)));
|
||||||
|
glEnableVertexAttribArray(1);
|
||||||
|
c.indexCount=(int)idx.size();
|
||||||
|
c.meshDirty=false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char* VS_SRC=R"(
|
||||||
|
#version 330 core
|
||||||
|
layout(location=0) in vec3 aPos;
|
||||||
|
layout(location=1) in vec3 aColor;
|
||||||
|
out vec3 vColor; out float vFog;
|
||||||
|
uniform mat4 uMVP; uniform vec3 uCamPos;
|
||||||
|
void main(){
|
||||||
|
gl_Position=uMVP*vec4(aPos,1.0);
|
||||||
|
vColor=aColor;
|
||||||
|
float dist=length(aPos-uCamPos);
|
||||||
|
vFog=clamp((dist-60.0)/40.0,0.0,1.0);
|
||||||
|
})";
|
||||||
|
static const char* FS_SRC=R"(
|
||||||
|
#version 330 core
|
||||||
|
in vec3 vColor; in float vFog;
|
||||||
|
out vec4 fragColor;
|
||||||
|
void main(){
|
||||||
|
vec3 fogColor=vec3(0.53,0.81,0.98);
|
||||||
|
fragColor=vec4(mix(vColor,fogColor,vFog),1.0);
|
||||||
|
})";
|
||||||
|
|
||||||
|
static GLuint compileShader(GLenum type,const char* src){
|
||||||
|
GLuint s=glCreateShader(type); glShaderSource(s,1,&src,nullptr); glCompileShader(s);
|
||||||
|
GLint ok; glGetShaderiv(s,GL_COMPILE_STATUS,&ok);
|
||||||
|
if(!ok){char buf[512];glGetShaderInfoLog(s,512,nullptr,buf);std::cerr<<"Shader: "<<buf<<"\n";}
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
static GLuint buildProgram(){
|
||||||
|
GLuint vs=compileShader(GL_VERTEX_SHADER,VS_SRC),fs=compileShader(GL_FRAGMENT_SHADER,FS_SRC);
|
||||||
|
GLuint p=glCreateProgram(); glAttachShader(p,vs); glAttachShader(p,fs); glLinkProgram(p);
|
||||||
|
glDeleteShader(vs); glDeleteShader(fs); return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Camera / Player ─────────────────────────────────────────────────────────
|
|
||||||
struct Camera{
|
struct Camera{
|
||||||
glm::vec3 pos{8,12,8};
|
glm::vec3 pos{0,40,0}; float yaw=0,pitch=0;
|
||||||
float yaw=-135.0f, pitch=0.0f;
|
glm::vec3 vel{0,0,0}; bool onGround=false;
|
||||||
glm::vec3 vel{0,0,0};
|
glm::vec3 forward()const{float y=glm::radians(yaw),p=glm::radians(pitch);return glm::normalize(glm::vec3(cosf(p)*cosf(y),sinf(p),cosf(p)*sinf(y)));}
|
||||||
bool onGround=false;
|
|
||||||
|
|
||||||
glm::vec3 forward() const {
|
|
||||||
float yRad=glm::radians(yaw), pRad=glm::radians(pitch);
|
|
||||||
return glm::normalize(glm::vec3(cosf(pRad)*cosf(yRad),sinf(pRad),cosf(pRad)*sinf(yRad)));
|
|
||||||
}
|
|
||||||
glm::vec3 right()const{return glm::normalize(glm::cross(forward(),{0,1,0}));}
|
glm::vec3 right()const{return glm::normalize(glm::cross(forward(),{0,1,0}));}
|
||||||
glm::mat4 view()const{return glm::lookAt(pos,pos+forward(),{0,1,0});}
|
glm::mat4 view()const{return glm::lookAt(pos,pos+forward(),{0,1,0});}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Player AABB: half-width HW on X/Z, height PH, eyes at top.
|
|
||||||
static const float HW = 0.3f; // half-width
|
|
||||||
static const float PH = 1.8f; // player height (feet to eye)
|
|
||||||
|
|
||||||
static bool solidAt(float fx,float fy,float fz){
|
static bool solidAt(float fx,float fy,float fz){
|
||||||
int x=(int)floorf(fx),y=(int)floorf(fy),z=(int)floorf(fz);
|
int x=(int)floorf(fx),y=(int)floorf(fy),z=(int)floorf(fz);
|
||||||
if (y < 0) return true; // solid floor under world
|
if(y<0) return true;
|
||||||
if (x < 0 || x >= CHUNK_SIZE || z < 0 || z >= CHUNK_SIZE) return true; // invisible walls
|
|
||||||
if(y>=WORLD_H) return false;
|
if(y>=WORLD_H) return false;
|
||||||
uint8_t b = WORLD[x][y][z];
|
uint8_t b=getBlock(x,y,z);
|
||||||
return b != AIR && b != WATER;
|
return b!=AIR&&b!=WATER&&b!=LEAVES;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXED: Now correctly checks all blocks intersecting the player's bounding volume
|
|
||||||
static bool aabbSolid(float px,float py,float pz){
|
static bool aabbSolid(float px,float py,float pz){
|
||||||
int minX = (int)floorf(px - HW);
|
int x0=(int)floorf(px-HW),x1=(int)floorf(px+HW);
|
||||||
int maxX = (int)floorf(px + HW);
|
int y0=(int)floorf(py-PH),y1=(int)floorf(py+0.05f);
|
||||||
int minY = (int)floorf(py - PH);
|
int z0=(int)floorf(pz-HW),z1=(int)floorf(pz+HW);
|
||||||
int maxY = (int)floorf(py + 0.1f);
|
for(int x=x0;x<=x1;x++) for(int y=y0;y<=y1;y++) for(int z=z0;z<=z1;z++)
|
||||||
int minZ = (int)floorf(pz - HW);
|
|
||||||
int maxZ = (int)floorf(pz + HW);
|
|
||||||
|
|
||||||
for (int x = minX; x <= maxX; ++x) {
|
|
||||||
for (int y = minY; y <= maxY; ++y) {
|
|
||||||
for (int z = minZ; z <= maxZ; ++z) {
|
|
||||||
if(solidAt((float)x,(float)y,(float)z)) return true;
|
if(solidAt((float)x,(float)y,(float)z)) return true;
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void moveCamera(Camera& cam,const glm::vec3& move,float dt){
|
static void moveCamera(Camera& cam,const glm::vec3& move,float dt){
|
||||||
// ── X axis ──
|
cam.pos.x+=move.x*dt; if(aabbSolid(cam.pos.x,cam.pos.y,cam.pos.z)) cam.pos.x-=move.x*dt;
|
||||||
cam.pos.x += move.x * dt;
|
cam.pos.z+=move.z*dt; if(aabbSolid(cam.pos.x,cam.pos.y,cam.pos.z)) cam.pos.z-=move.z*dt;
|
||||||
if (aabbSolid(cam.pos.x, cam.pos.y, cam.pos.z)) {
|
cam.vel.y+=GRAVITY*dt; cam.pos.y+=cam.vel.y*dt;
|
||||||
cam.pos.x -= move.x * dt; // Undo move if collided
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Z axis ──
|
|
||||||
cam.pos.z += move.z * dt;
|
|
||||||
if (aabbSolid(cam.pos.x, cam.pos.y, cam.pos.z)) {
|
|
||||||
cam.pos.z -= move.z * dt; // Undo move if collided
|
|
||||||
}
|
|
||||||
|
|
||||||
// ── Y axis (gravity + jump) ──
|
|
||||||
cam.vel.y += GRAVITY * dt;
|
|
||||||
cam.pos.y += cam.vel.y * dt;
|
|
||||||
|
|
||||||
if(cam.vel.y<0){
|
if(cam.vel.y<0){
|
||||||
// Moving downward
|
|
||||||
if(aabbSolid(cam.pos.x,cam.pos.y,cam.pos.z)){
|
if(aabbSolid(cam.pos.x,cam.pos.y,cam.pos.z)){
|
||||||
// Snap feet to top of the block below
|
|
||||||
cam.pos.y=floorf(cam.pos.y-PH)+1.0f+PH+0.001f;
|
cam.pos.y=floorf(cam.pos.y-PH)+1.0f+PH+0.001f;
|
||||||
cam.vel.y = 0.0f;
|
cam.vel.y=0; cam.onGround=true;
|
||||||
cam.onGround = true;
|
} else cam.onGround=false;
|
||||||
} else {
|
} else {
|
||||||
cam.onGround = false;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Moving upward
|
|
||||||
if(aabbSolid(cam.pos.x,cam.pos.y,cam.pos.z)){
|
if(aabbSolid(cam.pos.x,cam.pos.y,cam.pos.z)){
|
||||||
// Hit ceiling — snap head downwards
|
cam.pos.y=floorf(cam.pos.y+0.05f)-0.05f-0.001f; cam.vel.y=0;
|
||||||
cam.pos.y = floorf(cam.pos.y + 0.1f) - 0.1f - 0.001f;
|
|
||||||
cam.vel.y = 0.0f;
|
|
||||||
}
|
}
|
||||||
cam.onGround=false;
|
cam.onGround=false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Ray-cast for block placement/removal ────────────────────────────────────
|
|
||||||
static bool raycast(const Camera& cam,glm::ivec3& hit,glm::ivec3& prev){
|
static bool raycast(const Camera& cam,glm::ivec3& hit,glm::ivec3& prev){
|
||||||
glm::vec3 dir = cam.forward();
|
glm::vec3 dir=cam.forward(),p=cam.pos;
|
||||||
glm::vec3 p = cam.pos;
|
|
||||||
glm::ivec3 last{(int)floorf(p.x),(int)floorf(p.y),(int)floorf(p.z)};
|
glm::ivec3 last{(int)floorf(p.x),(int)floorf(p.y),(int)floorf(p.z)};
|
||||||
for(float t=0;t<8.0f;t+=0.05f){
|
for(float t=0;t<8.0f;t+=0.05f){
|
||||||
glm::vec3 rp=p+dir*t;
|
glm::vec3 rp=p+dir*t;
|
||||||
glm::ivec3 bp{(int)floorf(rp.x),(int)floorf(rp.y),(int)floorf(rp.z)};
|
glm::ivec3 bp{(int)floorf(rp.x),(int)floorf(rp.y),(int)floorf(rp.z)};
|
||||||
|
if(bp.y<0||bp.y>=WORLD_H){last=bp;continue;}
|
||||||
// Skip out of bounds, but don't break the ray
|
uint8_t b=getBlock(bp.x,bp.y,bp.z);
|
||||||
if (bp.x<0||bp.x>=CHUNK_SIZE||bp.y<0||bp.y>=WORLD_H||bp.z<0||bp.z>=CHUNK_SIZE) {
|
if(b!=AIR&&b!=WATER){hit=bp;prev=last;return true;}
|
||||||
last = bp;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (WORLD[bp.x][bp.y][bp.z] != AIR && WORLD[bp.x][bp.y][bp.z] != WATER) {
|
|
||||||
hit = bp; prev = last; return true;
|
|
||||||
}
|
|
||||||
last=bp;
|
last=bp;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ─── Main ────────────────────────────────────────────────────────────────────
|
static void updateChunks(int pcx,int pcz){
|
||||||
|
for(int dx=-RENDER_DIST;dx<=RENDER_DIST;dx++)
|
||||||
|
for(int dz=-RENDER_DIST;dz<=RENDER_DIST;dz++){
|
||||||
|
ChunkKey k{pcx+dx,pcz+dz};
|
||||||
|
if(CHUNKS.find(k)==CHUNKS.end()){
|
||||||
|
auto c=std::make_unique<Chunk>(k.x,k.z);
|
||||||
|
generateChunk(*c);
|
||||||
|
CHUNKS[k]=std::move(c);
|
||||||
|
for(auto& [nk,nc]:CHUNKS)
|
||||||
|
if(abs(nk.x-k.x)+abs(nk.z-k.z)==1) nc->meshDirty=true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
std::vector<ChunkKey> toRemove;
|
||||||
|
for(auto& [k,c]:CHUNKS)
|
||||||
|
if(abs(k.x-pcx)>RENDER_DIST+1||abs(k.z-pcz)>RENDER_DIST+1) toRemove.push_back(k);
|
||||||
|
for(auto& k:toRemove) CHUNKS.erase(k);
|
||||||
|
}
|
||||||
|
|
||||||
int main(){
|
int main(){
|
||||||
SDL_Init(SDL_INIT_VIDEO);
|
SDL_Init(SDL_INIT_VIDEO);
|
||||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION,3);
|
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION,3);
|
||||||
@@ -308,63 +313,47 @@ int main() {
|
|||||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,1);
|
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,1);
|
||||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,24);
|
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE,24);
|
||||||
|
|
||||||
SDL_Window* win = SDL_CreateWindow("MiniCraft",
|
SDL_Window* win=SDL_CreateWindow("MiniCraft - Infinite World",
|
||||||
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,SCREEN_W,SCREEN_H,SDL_WINDOW_OPENGL|SDL_WINDOW_SHOWN);
|
||||||
SCREEN_W, SCREEN_H,
|
|
||||||
SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN);
|
|
||||||
SDL_GLContext ctx=SDL_GL_CreateContext(win);
|
SDL_GLContext ctx=SDL_GL_CreateContext(win);
|
||||||
SDL_GL_SetSwapInterval(1);
|
SDL_GL_SetSwapInterval(1);
|
||||||
|
glewExperimental=GL_TRUE; glewInit();
|
||||||
glewExperimental = GL_TRUE;
|
glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); glCullFace(GL_BACK);
|
||||||
glewInit();
|
|
||||||
|
|
||||||
glEnable(GL_DEPTH_TEST);
|
|
||||||
glEnable(GL_CULL_FACE);
|
|
||||||
glCullFace(GL_BACK);
|
|
||||||
glClearColor(0.53f,0.81f,0.98f,1.0f);
|
glClearColor(0.53f,0.81f,0.98f,1.0f);
|
||||||
|
|
||||||
GLuint prog=buildProgram();
|
GLuint prog=buildProgram();
|
||||||
GLuint VAO, VBO, EBO;
|
|
||||||
glGenVertexArrays(1,&VAO);
|
|
||||||
glGenBuffers(1,&VBO);
|
|
||||||
glGenBuffers(1,&EBO);
|
|
||||||
|
|
||||||
generateWorld();
|
|
||||||
|
|
||||||
std::vector<Vertex> verts;
|
|
||||||
std::vector<GLuint> idx;
|
|
||||||
buildMesh(verts, idx);
|
|
||||||
|
|
||||||
glBindVertexArray(VAO);
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, VBO);
|
|
||||||
glBufferData(GL_ARRAY_BUFFER, verts.size()*sizeof(Vertex), verts.data(), GL_DYNAMIC_DRAW);
|
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
|
|
||||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, idx.size()*sizeof(GLuint), idx.data(), GL_DYNAMIC_DRAW);
|
|
||||||
glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,sizeof(Vertex),(void*)0);
|
|
||||||
glEnableVertexAttribArray(0);
|
|
||||||
glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,sizeof(Vertex),(void*)(3*sizeof(float)));
|
|
||||||
glEnableVertexAttribArray(1);
|
|
||||||
|
|
||||||
glm::mat4 proj = glm::perspective(glm::radians(70.0f),(float)SCREEN_W/SCREEN_H,0.05f,300.0f);
|
|
||||||
GLint mvpLoc=glGetUniformLocation(prog,"uMVP");
|
GLint mvpLoc=glGetUniformLocation(prog,"uMVP");
|
||||||
|
GLint camLoc=glGetUniformLocation(prog,"uCamPos");
|
||||||
|
|
||||||
SDL_SetRelativeMouseMode(SDL_TRUE);
|
initNoise(12345);
|
||||||
|
std::cout<<"Generating spawn chunks...\n";
|
||||||
|
updateChunks(0,0);
|
||||||
|
// build all initial meshes immediately
|
||||||
|
for(auto& [k,c]:CHUNKS) if(c->generated) buildChunkMesh(*c);
|
||||||
|
|
||||||
Camera cam;
|
Camera cam;
|
||||||
bool running = true;
|
for(int y=WORLD_H-1;y>=0;y--){
|
||||||
Uint64 last = SDL_GetPerformanceCounter();
|
uint8_t b=getBlock(0,y,0);
|
||||||
|
if(b!=AIR&&b!=WATER){cam.pos.y=(float)y+1+PH+0.1f;break;}
|
||||||
|
}
|
||||||
|
cam.pos.x=0.5f; cam.pos.z=0.5f;
|
||||||
|
|
||||||
|
glm::mat4 proj=glm::perspective(glm::radians(70.0f),(float)SCREEN_W/SCREEN_H,0.05f,500.0f);
|
||||||
|
SDL_SetRelativeMouseMode(SDL_TRUE);
|
||||||
|
|
||||||
uint8_t selectedBlock=GRASS;
|
uint8_t selectedBlock=GRASS;
|
||||||
bool meshDirty = false;
|
bool running=true;
|
||||||
|
Uint64 last=SDL_GetPerformanceCounter();
|
||||||
|
int lastPcx=INT_MAX,lastPcz=INT_MAX;
|
||||||
|
|
||||||
std::cout << "=== MiniCraft Controls ===\n"
|
std::cout<<"=== MiniCraft Infinite World ===\n"
|
||||||
<< "WASD - Move\n"
|
<<"WASD Move\n"
|
||||||
<< "Space - Jump\n"
|
<<"Space Jump\n"
|
||||||
<< "Mouse - Look\n"
|
<<"Mouse Look\n"
|
||||||
<< "LMB - Destroy block\n"
|
<<"LMB Destroy block\n"
|
||||||
<< "RMB - Place block\n"
|
<<"RMB Place block\n"
|
||||||
<< "1-7 - Select block type\n"
|
<<"1-8 Grass/Dirt/Stone/Wood/Leaves/Sand/Snow/Water\n"
|
||||||
<< "ESC - Quit\n";
|
<<"ESC Quit\n";
|
||||||
|
|
||||||
while(running){
|
while(running){
|
||||||
Uint64 now=SDL_GetPerformanceCounter();
|
Uint64 now=SDL_GetPerformanceCounter();
|
||||||
@@ -376,75 +365,65 @@ int main() {
|
|||||||
while(SDL_PollEvent(&e)){
|
while(SDL_PollEvent(&e)){
|
||||||
if(e.type==SDL_QUIT) running=false;
|
if(e.type==SDL_QUIT) running=false;
|
||||||
if(e.type==SDL_KEYDOWN){
|
if(e.type==SDL_KEYDOWN){
|
||||||
if (e.key.keysym.sym == SDLK_ESCAPE) running = false;
|
switch(e.key.keysym.sym){
|
||||||
if (e.key.keysym.sym == SDLK_1) selectedBlock = GRASS;
|
case SDLK_ESCAPE:running=false;break;
|
||||||
if (e.key.keysym.sym == SDLK_2) selectedBlock = DIRT;
|
case SDLK_1:selectedBlock=GRASS;break;
|
||||||
if (e.key.keysym.sym == SDLK_3) selectedBlock = STONE;
|
case SDLK_2:selectedBlock=DIRT;break;
|
||||||
if (e.key.keysym.sym == SDLK_4) selectedBlock = WOOD;
|
case SDLK_3:selectedBlock=STONE;break;
|
||||||
if (e.key.keysym.sym == SDLK_5) selectedBlock = LEAVES;
|
case SDLK_4:selectedBlock=WOOD;break;
|
||||||
if (e.key.keysym.sym == SDLK_6) selectedBlock = SAND;
|
case SDLK_5:selectedBlock=LEAVES;break;
|
||||||
if (e.key.keysym.sym == SDLK_7) selectedBlock = WATER;
|
case SDLK_6:selectedBlock=SAND;break;
|
||||||
|
case SDLK_7:selectedBlock=SNOW;break;
|
||||||
|
case SDLK_8:selectedBlock=WATER;break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if(e.type==SDL_MOUSEMOTION){
|
if(e.type==SDL_MOUSEMOTION){
|
||||||
cam.yaw+=e.motion.xrel*MOUSE_SENS;
|
cam.yaw+=e.motion.xrel*MOUSE_SENS;
|
||||||
cam.pitch-=e.motion.yrel*MOUSE_SENS;
|
cam.pitch-=e.motion.yrel*MOUSE_SENS;
|
||||||
if (cam.pitch > 89.0f) cam.pitch = 89.0f;
|
if(cam.pitch>89)cam.pitch=89; if(cam.pitch<-89)cam.pitch=-89;
|
||||||
if (cam.pitch < -89.0f) cam.pitch = -89.0f;
|
|
||||||
}
|
}
|
||||||
if(e.type==SDL_MOUSEBUTTONDOWN){
|
if(e.type==SDL_MOUSEBUTTONDOWN){
|
||||||
glm::ivec3 hit,prev;
|
glm::ivec3 hit,prev;
|
||||||
if(raycast(cam,hit,prev)){
|
if(raycast(cam,hit,prev)){
|
||||||
if (e.button.button == SDL_BUTTON_LEFT) {
|
if(e.button.button==SDL_BUTTON_LEFT) setBlock(hit.x,hit.y,hit.z,AIR);
|
||||||
WORLD[hit.x][hit.y][hit.z] = AIR;
|
else if(e.button.button==SDL_BUTTON_RIGHT) setBlock(prev.x,prev.y,prev.z,selectedBlock);
|
||||||
meshDirty = true;
|
|
||||||
} else if (e.button.button == SDL_BUTTON_RIGHT) {
|
|
||||||
if (prev.x>=0&&prev.x<CHUNK_SIZE&&prev.y>=0&&prev.y<WORLD_H&&prev.z>=0&&prev.z<CHUNK_SIZE)
|
|
||||||
WORLD[prev.x][prev.y][prev.z] = selectedBlock;
|
|
||||||
meshDirty = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (meshDirty) {
|
|
||||||
buildMesh(verts, idx);
|
|
||||||
glBindVertexArray(VAO);
|
|
||||||
glBindBuffer(GL_ARRAY_BUFFER, VBO);
|
|
||||||
glBufferData(GL_ARRAY_BUFFER, verts.size()*sizeof(Vertex), verts.data(), GL_DYNAMIC_DRAW);
|
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO);
|
|
||||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER, idx.size()*sizeof(GLuint), idx.data(), GL_DYNAMIC_DRAW);
|
|
||||||
meshDirty = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Movement
|
|
||||||
const Uint8* keys=SDL_GetKeyboardState(nullptr);
|
const Uint8* keys=SDL_GetKeyboardState(nullptr);
|
||||||
glm::vec3 fwd=cam.forward(); fwd.y=0; if(glm::length(fwd)>0) fwd=glm::normalize(fwd);
|
glm::vec3 fwd=cam.forward(); fwd.y=0; if(glm::length(fwd)>0) fwd=glm::normalize(fwd);
|
||||||
glm::vec3 rgt=cam.right(); rgt.y=0; if(glm::length(rgt)>0) rgt=glm::normalize(rgt);
|
glm::vec3 rgt=cam.right(); rgt.y=0; if(glm::length(rgt)>0) rgt=glm::normalize(rgt);
|
||||||
glm::vec3 move{0,0,0};
|
glm::vec3 move{0,0,0};
|
||||||
|
|
||||||
if(keys[SDL_SCANCODE_W]) move+=fwd*MOVE_SPEED;
|
if(keys[SDL_SCANCODE_W]) move+=fwd*MOVE_SPEED;
|
||||||
if(keys[SDL_SCANCODE_S]) move-=fwd*MOVE_SPEED;
|
if(keys[SDL_SCANCODE_S]) move-=fwd*MOVE_SPEED;
|
||||||
if(keys[SDL_SCANCODE_D]) move+=rgt*MOVE_SPEED;
|
if(keys[SDL_SCANCODE_D]) move+=rgt*MOVE_SPEED;
|
||||||
if(keys[SDL_SCANCODE_A]) move-=rgt*MOVE_SPEED;
|
if(keys[SDL_SCANCODE_A]) move-=rgt*MOVE_SPEED;
|
||||||
if(keys[SDL_SCANCODE_SPACE]&&cam.onGround) cam.vel.y=JUMP_VEL;
|
if(keys[SDL_SCANCODE_SPACE]&&cam.onGround) cam.vel.y=JUMP_VEL;
|
||||||
|
|
||||||
moveCamera(cam,move,dt);
|
moveCamera(cam,move,dt);
|
||||||
|
|
||||||
// Render
|
int pcx=(int)floorf(cam.pos.x/CHUNK_SIZE);
|
||||||
|
int pcz=(int)floorf(cam.pos.z/CHUNK_SIZE);
|
||||||
|
if(pcx!=lastPcx||pcz!=lastPcz){
|
||||||
|
updateChunks(pcx,pcz); lastPcx=pcx; lastPcz=pcz;
|
||||||
|
}
|
||||||
|
|
||||||
|
int rebuilt=0;
|
||||||
|
for(auto& [k,c]:CHUNKS)
|
||||||
|
if(c->meshDirty&&c->generated){buildChunkMesh(*c);if(++rebuilt>=4)break;}
|
||||||
|
|
||||||
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
|
||||||
glUseProgram(prog);
|
glUseProgram(prog);
|
||||||
glm::mat4 mvp=proj*cam.view();
|
glm::mat4 mvp=proj*cam.view();
|
||||||
glUniformMatrix4fv(mvpLoc,1,GL_FALSE,glm::value_ptr(mvp));
|
glUniformMatrix4fv(mvpLoc,1,GL_FALSE,glm::value_ptr(mvp));
|
||||||
|
glUniform3fv(camLoc,1,glm::value_ptr(cam.pos));
|
||||||
glBindVertexArray(VAO);
|
for(auto& [k,c]:CHUNKS)
|
||||||
glDrawElements(GL_TRIANGLES,(GLsizei)idx.size(),GL_UNSIGNED_INT,0);
|
if(c->indexCount>0){glBindVertexArray(c->VAO);glDrawElements(GL_TRIANGLES,c->indexCount,GL_UNSIGNED_INT,0);}
|
||||||
|
|
||||||
SDL_GL_SwapWindow(win);
|
SDL_GL_SwapWindow(win);
|
||||||
}
|
}
|
||||||
|
|
||||||
glDeleteVertexArrays(1,&VAO);
|
CHUNKS.clear();
|
||||||
glDeleteBuffers(1,&VBO);
|
|
||||||
glDeleteBuffers(1,&EBO);
|
|
||||||
glDeleteProgram(prog);
|
glDeleteProgram(prog);
|
||||||
SDL_GL_DeleteContext(ctx);
|
SDL_GL_DeleteContext(ctx);
|
||||||
SDL_DestroyWindow(win);
|
SDL_DestroyWindow(win);
|
||||||
|
|||||||
Reference in New Issue
Block a user