Added inventroy, f5 and cross
This commit is contained in:
663
main.cpp
663
main.cpp
@@ -13,11 +13,13 @@
|
|||||||
#include <memory>
|
#include <memory>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <climits>
|
#include <climits>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
// Build:
|
// Build:
|
||||||
// mkdir build && cd build && cmake .. && make -j$(nproc)
|
// mkdir build && cd 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;
|
||||||
@@ -29,24 +31,30 @@ static const float GRAVITY = -22.0f;
|
|||||||
static const float JUMP_VEL = 9.0f;
|
static const float JUMP_VEL = 9.0f;
|
||||||
static const float HW = 0.3f;
|
static const float HW = 0.3f;
|
||||||
static const float PH = 1.8f;
|
static const float PH = 1.8f;
|
||||||
|
static const int HOTBAR_SIZE = 8;
|
||||||
|
|
||||||
|
// ─── 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, SNOW, WATER };
|
||||||
|
static const int NUM_BLOCK_TYPES = 9;
|
||||||
|
|
||||||
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},
|
{0,0,0},
|
||||||
{0.40f,0.72f,0.24f},
|
{0.40f,0.72f,0.24f}, // GRASS
|
||||||
{0.55f,0.40f,0.22f},
|
{0.55f,0.40f,0.22f}, // DIRT
|
||||||
{0.50f,0.50f,0.50f},
|
{0.50f,0.50f,0.50f}, // STONE
|
||||||
{0.45f,0.30f,0.15f},
|
{0.45f,0.30f,0.15f}, // WOOD
|
||||||
{0.22f,0.55f,0.18f},
|
{0.22f,0.55f,0.18f}, // LEAVES
|
||||||
{0.88f,0.83f,0.52f},
|
{0.88f,0.83f,0.52f}, // SAND
|
||||||
{0.92f,0.95f,0.98f},
|
{0.92f,0.95f,0.98f}, // SNOW
|
||||||
{0.18f,0.42f,0.88f},
|
{0.18f,0.42f,0.88f}, // WATER
|
||||||
|
};
|
||||||
|
static const char* BLOCK_NAMES[] = {
|
||||||
|
"Air","Grass","Dirt","Stone","Wood","Leaves","Sand","Snow","Water"
|
||||||
};
|
};
|
||||||
static const float FACE_SHADE[] = {1.0f, 0.5f, 0.8f, 0.7f, 0.65f, 0.65f};
|
static const float FACE_SHADE[] = {1.0f, 0.5f, 0.8f, 0.7f, 0.65f, 0.65f};
|
||||||
|
|
||||||
// Perlin noise
|
// ─── Perlin Noise ─────────────────────────────────────────────────────────────
|
||||||
static int P[512];
|
static int P[512];
|
||||||
static void initNoise(int seed){
|
static void initNoise(int seed){
|
||||||
int perm[256]; for(int i=0;i<256;i++) perm[i]=i;
|
int perm[256]; for(int i=0;i<256;i++) perm[i]=i;
|
||||||
@@ -69,6 +77,7 @@ static float fbm(float x,float y,int oct=6){
|
|||||||
return val/max;
|
return val/max;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── World chunk ─────────────────────────────────────────────────────────────
|
||||||
struct Chunk {
|
struct Chunk {
|
||||||
int cx,cz;
|
int cx,cz;
|
||||||
uint8_t blocks[CHUNK_SIZE][WORLD_H][CHUNK_SIZE];
|
uint8_t blocks[CHUNK_SIZE][WORLD_H][CHUNK_SIZE];
|
||||||
@@ -78,7 +87,6 @@ struct Chunk {
|
|||||||
Chunk(int cx,int cz):cx(cx),cz(cz){memset(blocks,AIR,sizeof(blocks));}
|
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);}}
|
~Chunk(){if(VAO){glDeleteVertexArrays(1,&VAO);glDeleteBuffers(1,&VBO);glDeleteBuffers(1,&EBO);}}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct ChunkKey{int x,z;bool operator==(const ChunkKey& o)const{return x==o.x&&z==o.z;}};
|
struct ChunkKey{int x,z;bool operator==(const ChunkKey& o)const{return x==o.x&&z==o.z;}};
|
||||||
struct ChunkKeyHash{size_t operator()(const ChunkKey& k)const{return std::hash<long long>()((long long)k.x<<32|(unsigned)k.z);}};
|
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>;
|
using ChunkMap=std::unordered_map<ChunkKey,std::unique_ptr<Chunk>,ChunkKeyHash>;
|
||||||
@@ -93,28 +101,22 @@ static void generateChunk(Chunk& c){
|
|||||||
float detail=fbm(wx*2.0f+5.3f,wz*2.0f+1.7f,5);
|
float detail=fbm(wx*2.0f+5.3f,wz*2.0f+1.7f,5);
|
||||||
float ridge=1.0f-fabsf(fbm(wx*0.8f+3.1f,wz*0.8f+8.9f,4));
|
float ridge=1.0f-fabsf(fbm(wx*0.8f+3.1f,wz*0.8f+8.9f,4));
|
||||||
float h=12.0f+cont*18.0f+detail*6.0f+ridge*14.0f*std::max(0.0f,cont);
|
float h=12.0f+cont*18.0f+detail*6.0f+ridge*14.0f*std::max(0.0f,cont);
|
||||||
int top=(int)h;
|
int top=(int)h; top=std::max(2,std::min(WORLD_H-2,top));
|
||||||
top=std::max(2,std::min(WORLD_H-2,top));
|
|
||||||
bool isSand=(top<16),isSnow=(top>38);
|
bool isSand=(top<16),isSnow=(top>38);
|
||||||
for(int y=0;y<WORLD_H;y++){
|
for(int y=0;y<WORLD_H;y++){
|
||||||
if(y==0) c.blocks[x][y][z]=STONE;
|
if(y==0) c.blocks[x][y][z]=STONE;
|
||||||
else if(y<top-4) 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) c.blocks[x][y][z]=isSand?SAND:DIRT;
|
||||||
else if(y==top){
|
else if(y==top){ c.blocks[x][y][z]=isSand?SAND:(isSnow?SNOW:GRASS); }
|
||||||
if(isSand) c.blocks[x][y][z]=SAND;
|
else if(y<=14) c.blocks[x][y][z]=WATER;
|
||||||
else if(isSnow) c.blocks[x][y][z]=SNOW;
|
else c.blocks[x][y][z]=AIR;
|
||||||
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){
|
if(!isSand&&!isSnow&&top>=16&&top<36&&top+6<WORLD_H){
|
||||||
unsigned col=(unsigned)((wx0+x)*73856093u^(unsigned)(wz0+z)*19349663u);
|
unsigned col=(unsigned)((wx0+x)*73856093u^(unsigned)(wz0+z)*19349663u);
|
||||||
if((col&0xFF)<18){
|
if((col&0xFF)<18){
|
||||||
int trunk=top+1;
|
int trunk=top+1;
|
||||||
for(int t=trunk;t<trunk+4&&t<WORLD_H;t++) c.blocks[x][t][z]=WOOD;
|
for(int t=trunk;t<trunk+4&&t<WORLD_H;t++) c.blocks[x][t][z]=WOOD;
|
||||||
for(int dx=-2;dx<=2;dx++)
|
for(int dx=-2;dx<=2;dx++) for(int dz=-2;dz<=2;dz++) for(int dy=trunk+2;dy<=trunk+5;dy++){
|
||||||
for(int dz=-2;dz<=2;dz++)
|
|
||||||
for(int dy=trunk+2;dy<=trunk+5;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(c.blocks[nx][dy][nz]==AIR) c.blocks[nx][dy][nz]=LEAVES;
|
if(c.blocks[nx][dy][nz]==AIR) c.blocks[nx][dy][nz]=LEAVES;
|
||||||
@@ -126,59 +128,45 @@ static void generateChunk(Chunk& c){
|
|||||||
}
|
}
|
||||||
|
|
||||||
static uint8_t getBlock(int wx,int wy,int wz){
|
static uint8_t getBlock(int wx,int wy,int wz){
|
||||||
if(wy<0) return STONE;
|
if(wy<0) return STONE; if(wy>=WORLD_H) return AIR;
|
||||||
if(wy>=WORLD_H) return AIR;
|
|
||||||
int cx=(int)floorf((float)wx/CHUNK_SIZE),cz=(int)floorf((float)wz/CHUNK_SIZE);
|
int cx=(int)floorf((float)wx/CHUNK_SIZE),cz=(int)floorf((float)wz/CHUNK_SIZE);
|
||||||
auto it=CHUNKS.find({cx,cz});
|
auto it=CHUNKS.find({cx,cz}); if(it==CHUNKS.end()) return STONE;
|
||||||
if(it==CHUNKS.end()) return STONE;
|
return it->second->blocks[wx-cx*CHUNK_SIZE][wy][wz-cz*CHUNK_SIZE];
|
||||||
int lx=wx-cx*CHUNK_SIZE,lz=wz-cz*CHUNK_SIZE;
|
|
||||||
return it->second->blocks[lx][wy][lz];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void setBlock(int wx,int wy,int wz,uint8_t val){
|
static void setBlock(int wx,int wy,int wz,uint8_t val){
|
||||||
if(wy<0||wy>=WORLD_H) return;
|
if(wy<0||wy>=WORLD_H) return;
|
||||||
int cx=(int)floorf((float)wx/CHUNK_SIZE),cz=(int)floorf((float)wz/CHUNK_SIZE);
|
int cx=(int)floorf((float)wx/CHUNK_SIZE),cz=(int)floorf((float)wz/CHUNK_SIZE);
|
||||||
auto it=CHUNKS.find({cx,cz});
|
auto it=CHUNKS.find({cx,cz}); if(it==CHUNKS.end()) return;
|
||||||
if(it==CHUNKS.end()) return;
|
|
||||||
int lx=wx-cx*CHUNK_SIZE,lz=wz-cz*CHUNK_SIZE;
|
int lx=wx-cx*CHUNK_SIZE,lz=wz-cz*CHUNK_SIZE;
|
||||||
it->second->blocks[lx][wy][lz]=val;
|
it->second->blocks[lx][wy][lz]=val; it->second->meshDirty=true;
|
||||||
it->second->meshDirty=true;
|
|
||||||
if(lx==0){auto n=CHUNKS.find({cx-1,cz});if(n!=CHUNKS.end())n->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(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==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;}
|
if(lz==CHUNK_SIZE-1){auto n=CHUNKS.find({cx,cz+1});if(n!=CHUNKS.end())n->second->meshDirty=true;}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── World mesh ──────────────────────────────────────────────────────────────
|
||||||
struct Vertex{float x,y,z,r,g,b;};
|
struct Vertex{float x,y,z,r,g,b;};
|
||||||
|
|
||||||
static bool shouldDrawFace(uint8_t cur,int wx,int wy,int wz){
|
static bool shouldDrawFace(uint8_t cur,int wx,int wy,int wz){
|
||||||
uint8_t nb=getBlock(wx,wy,wz);
|
uint8_t nb=getBlock(wx,wy,wz);
|
||||||
if(nb==AIR) return true;
|
if(nb==AIR) return true;
|
||||||
if(nb==WATER||nb==LEAVES) return cur!=nb;
|
if(nb==WATER||nb==LEAVES) return cur!=nb;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
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,float r,float g,float b,float shade){
|
glm::vec3 v0,glm::vec3 v1,glm::vec3 v2,glm::vec3 v3,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({v1.x,v1.y,v1.z,sr,sg,sb});
|
||||||
verts.push_back({v0.x,v0.y,v0.z,sr,sg,sb});
|
verts.push_back({v2.x,v2.y,v2.z,sr,sg,sb}); verts.push_back({v3.x,v3.y,v3.z,sr,sg,sb});
|
||||||
verts.push_back({v1.x,v1.y,v1.z,sr,sg,sb});
|
|
||||||
verts.push_back({v2.x,v2.y,v2.z,sr,sg,sb});
|
|
||||||
verts.push_back({v3.x,v3.y,v3.z,sr,sg,sb});
|
|
||||||
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 buildChunkMesh(Chunk& c){
|
static void buildChunkMesh(Chunk& c){
|
||||||
std::vector<Vertex> verts; std::vector<GLuint> idx;
|
std::vector<Vertex> verts; std::vector<GLuint> idx;
|
||||||
int wx0=c.cx*CHUNK_SIZE,wz0=c.cz*CHUNK_SIZE;
|
int wx0=c.cx*CHUNK_SIZE,wz0=c.cz*CHUNK_SIZE;
|
||||||
for(int x=0;x<CHUNK_SIZE;x++)
|
for(int x=0;x<CHUNK_SIZE;x++) for(int y=0;y<WORLD_H;y++) for(int z=0;z<CHUNK_SIZE;z++){
|
||||||
for(int y=0;y<WORLD_H;y++)
|
|
||||||
for(int z=0;z<CHUNK_SIZE;z++){
|
|
||||||
uint8_t b=c.blocks[x][y][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=wx0+x,by=y,bz=wz0+z;
|
float bx=wx0+x,by=y,bz=wz0+z; int wx=wx0+x,wz=wz0+z;
|
||||||
int wx=wx0+x,wz=wz0+z;
|
|
||||||
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]);
|
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]);
|
||||||
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,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,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]);
|
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]);
|
||||||
@@ -188,18 +176,14 @@ static void buildChunkMesh(Chunk& c){
|
|||||||
}
|
}
|
||||||
if(!c.VAO){glGenVertexArrays(1,&c.VAO);glGenBuffers(1,&c.VBO);glGenBuffers(1,&c.EBO);}
|
if(!c.VAO){glGenVertexArrays(1,&c.VAO);glGenBuffers(1,&c.VBO);glGenBuffers(1,&c.EBO);}
|
||||||
glBindVertexArray(c.VAO);
|
glBindVertexArray(c.VAO);
|
||||||
glBindBuffer(GL_ARRAY_BUFFER,c.VBO);
|
glBindBuffer(GL_ARRAY_BUFFER,c.VBO); glBufferData(GL_ARRAY_BUFFER,verts.size()*sizeof(Vertex),verts.data(),GL_DYNAMIC_DRAW);
|
||||||
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);
|
||||||
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,c.EBO);
|
glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,sizeof(Vertex),(void*)0); glEnableVertexAttribArray(0);
|
||||||
glBufferData(GL_ELEMENT_ARRAY_BUFFER,idx.size()*sizeof(GLuint),idx.data(),GL_DYNAMIC_DRAW);
|
glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,sizeof(Vertex),(void*)(3*sizeof(float))); glEnableVertexAttribArray(1);
|
||||||
glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,sizeof(Vertex),(void*)0);
|
c.indexCount=(int)idx.size(); c.meshDirty=false;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── Shaders ─────────────────────────────────────────────────────────────────
|
||||||
static const char* VS_SRC=R"(
|
static const char* VS_SRC=R"(
|
||||||
#version 330 core
|
#version 330 core
|
||||||
layout(location=0) in vec3 aPos;
|
layout(location=0) in vec3 aPos;
|
||||||
@@ -207,46 +191,344 @@ layout(location=1) in vec3 aColor;
|
|||||||
out vec3 vColor; out float vFog;
|
out vec3 vColor; out float vFog;
|
||||||
uniform mat4 uMVP; uniform vec3 uCamPos;
|
uniform mat4 uMVP; uniform vec3 uCamPos;
|
||||||
void main(){
|
void main(){
|
||||||
gl_Position=uMVP*vec4(aPos,1.0);
|
gl_Position=uMVP*vec4(aPos,1.0); vColor=aColor;
|
||||||
vColor=aColor;
|
|
||||||
float dist=length(aPos-uCamPos);
|
float dist=length(aPos-uCamPos);
|
||||||
vFog=clamp((dist-60.0)/40.0,0.0,1.0);
|
vFog=clamp((dist-60.0)/40.0,0.0,1.0);
|
||||||
})";
|
})";
|
||||||
static const char* FS_SRC=R"(
|
static const char* FS_SRC=R"(
|
||||||
#version 330 core
|
#version 330 core
|
||||||
in vec3 vColor; in float vFog;
|
in vec3 vColor; in float vFog; out vec4 fragColor;
|
||||||
out vec4 fragColor;
|
|
||||||
void main(){
|
void main(){
|
||||||
vec3 fogColor=vec3(0.53,0.81,0.98);
|
vec3 fogColor=vec3(0.53,0.81,0.98);
|
||||||
fragColor=vec4(mix(vColor,fogColor,vFog),1.0);
|
fragColor=vec4(mix(vColor,fogColor,vFog),1.0);
|
||||||
})";
|
})";
|
||||||
|
|
||||||
|
static const char* VS_UI=R"(
|
||||||
|
#version 330 core
|
||||||
|
layout(location=0) in vec2 aPos;
|
||||||
|
layout(location=1) in vec3 aColor;
|
||||||
|
out vec3 vColor;
|
||||||
|
uniform mat4 uProj;
|
||||||
|
void main(){ gl_Position=uProj*vec4(aPos,0.0,1.0); vColor=aColor; })";
|
||||||
|
static const char* FS_UI=R"(
|
||||||
|
#version 330 core
|
||||||
|
in vec3 vColor; out vec4 fragColor;
|
||||||
|
uniform float uAlpha;
|
||||||
|
void main(){ fragColor=vec4(vColor,uAlpha); })";
|
||||||
|
|
||||||
static GLuint compileShader(GLenum type,const char* src){
|
static GLuint compileShader(GLenum type,const char* src){
|
||||||
GLuint s=glCreateShader(type); glShaderSource(s,1,&src,nullptr); glCompileShader(s);
|
GLuint s=glCreateShader(type); glShaderSource(s,1,&src,nullptr); glCompileShader(s);
|
||||||
GLint ok; glGetShaderiv(s,GL_COMPILE_STATUS,&ok);
|
GLint ok; glGetShaderiv(s,GL_COMPILE_STATUS,&ok);
|
||||||
if(!ok){char buf[512];glGetShaderInfoLog(s,512,nullptr,buf);std::cerr<<"Shader: "<<buf<<"\n";}
|
if(!ok){char buf[512];glGetShaderInfoLog(s,512,nullptr,buf);std::cerr<<"Shader err: "<<buf<<"\n";}
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
static GLuint buildProgram(){
|
static GLuint buildProg(const char* vs,const char* fs){
|
||||||
GLuint vs=compileShader(GL_VERTEX_SHADER,VS_SRC),fs=compileShader(GL_FRAGMENT_SHADER,FS_SRC);
|
GLuint v=compileShader(GL_VERTEX_SHADER,vs),f=compileShader(GL_FRAGMENT_SHADER,fs);
|
||||||
GLuint p=glCreateProgram(); glAttachShader(p,vs); glAttachShader(p,fs); glLinkProgram(p);
|
GLuint p=glCreateProgram(); glAttachShader(p,v); glAttachShader(p,f); glLinkProgram(p);
|
||||||
glDeleteShader(vs); glDeleteShader(fs); return p;
|
glDeleteShader(v); glDeleteShader(f); return p;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Camera{
|
// ─── 2D UI Renderer ──────────────────────────────────────────────────────────
|
||||||
glm::vec3 pos{0,40,0}; float yaw=0,pitch=0;
|
struct UIRenderer {
|
||||||
glm::vec3 vel{0,0,0}; bool onGround=false;
|
GLuint prog=0,VAO=0,VBO=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)));}
|
GLint projLoc=-1,alphaLoc=-1;
|
||||||
|
glm::mat4 proj{1.0f};
|
||||||
|
|
||||||
|
void init(){
|
||||||
|
prog=buildProg(VS_UI,FS_UI);
|
||||||
|
projLoc=glGetUniformLocation(prog,"uProj");
|
||||||
|
alphaLoc=glGetUniformLocation(prog,"uAlpha");
|
||||||
|
proj=glm::ortho(0.0f,(float)SCREEN_W,(float)SCREEN_H,0.0f);
|
||||||
|
|
||||||
|
glGenVertexArrays(1,&VAO);
|
||||||
|
glGenBuffers(1,&VBO);
|
||||||
|
glBindVertexArray(VAO);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER,VBO);
|
||||||
|
glBufferData(GL_ARRAY_BUFFER,1024*sizeof(float),nullptr,GL_DYNAMIC_DRAW);
|
||||||
|
glVertexAttribPointer(0,2,GL_FLOAT,GL_FALSE,5*sizeof(float),(void*)0);
|
||||||
|
glEnableVertexAttribArray(0);
|
||||||
|
glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,5*sizeof(float),(void*)(2*sizeof(float)));
|
||||||
|
glEnableVertexAttribArray(1);
|
||||||
|
glBindVertexArray(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void begin(){
|
||||||
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
glDisable(GL_CULL_FACE);
|
||||||
|
glEnable(GL_BLEND);
|
||||||
|
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
|
||||||
|
glUseProgram(prog);
|
||||||
|
glUniformMatrix4fv(projLoc,1,GL_FALSE,glm::value_ptr(proj));
|
||||||
|
glBindVertexArray(VAO);
|
||||||
|
}
|
||||||
|
|
||||||
|
void end(){
|
||||||
|
glDisable(GL_BLEND);
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
glEnable(GL_CULL_FACE);
|
||||||
|
glBindVertexArray(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void rect(float x,float y,float w,float h,float r,float g,float b,float a=1.0f){
|
||||||
|
float verts[]={
|
||||||
|
x, y, r,g,b,
|
||||||
|
x+w, y, r,g,b,
|
||||||
|
x+w, y+h, r,g,b,
|
||||||
|
x, y, r,g,b,
|
||||||
|
x+w, y+h, r,g,b,
|
||||||
|
x, y+h, r,g,b
|
||||||
|
};
|
||||||
|
glUniform1f(alphaLoc,a);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER,VBO);
|
||||||
|
glBufferSubData(GL_ARRAY_BUFFER,0,sizeof(verts),verts);
|
||||||
|
glDrawArrays(GL_TRIANGLES,0,6);
|
||||||
|
}
|
||||||
|
|
||||||
|
void outline(float x,float y,float w,float h,float r,float g,float b,float t=2.0f){
|
||||||
|
rect(x,y,w,t,r,g,b);
|
||||||
|
rect(x,y+h-t,w,t,r,g,b);
|
||||||
|
rect(x,y,t,h,r,g,b);
|
||||||
|
rect(x+w-t,y,t,h,r,g,b);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// ─── Global UI + PlayerModel forward declarations ────────────────────────────
|
||||||
|
// Declared here so drawText / drawHUD / drawInventory can use them.
|
||||||
|
static UIRenderer UI;
|
||||||
|
|
||||||
|
// ─── Tiny bitmap font (5×7 per glyph, ASCII 32–126) ─────────────────────────
|
||||||
|
static const uint8_t FONT5x7[][5] = {
|
||||||
|
{0x00,0x00,0x00,0x00,0x00}, // space
|
||||||
|
{0x00,0x00,0x5F,0x00,0x00}, // !
|
||||||
|
{0x00,0x07,0x00,0x07,0x00}, // "
|
||||||
|
{0x14,0x7F,0x14,0x7F,0x14}, // #
|
||||||
|
{0x24,0x2A,0x7F,0x2A,0x12}, // $
|
||||||
|
{0x23,0x13,0x08,0x64,0x62}, // %
|
||||||
|
{0x36,0x49,0x55,0x22,0x50}, // &
|
||||||
|
{0x00,0x05,0x03,0x00,0x00}, // '
|
||||||
|
{0x00,0x1C,0x22,0x41,0x00}, // (
|
||||||
|
{0x00,0x41,0x22,0x1C,0x00}, // )
|
||||||
|
{0x14,0x08,0x3E,0x08,0x14}, // *
|
||||||
|
{0x08,0x08,0x3E,0x08,0x08}, // +
|
||||||
|
{0x00,0x50,0x30,0x00,0x00}, // ,
|
||||||
|
{0x08,0x08,0x08,0x08,0x08}, // -
|
||||||
|
{0x00,0x60,0x60,0x00,0x00}, // .
|
||||||
|
{0x20,0x10,0x08,0x04,0x02}, // /
|
||||||
|
{0x3E,0x51,0x49,0x45,0x3E}, // 0
|
||||||
|
{0x00,0x42,0x7F,0x40,0x00}, // 1
|
||||||
|
{0x42,0x61,0x51,0x49,0x46}, // 2
|
||||||
|
{0x21,0x41,0x45,0x4B,0x31}, // 3
|
||||||
|
{0x18,0x14,0x12,0x7F,0x10}, // 4
|
||||||
|
{0x27,0x45,0x45,0x45,0x39}, // 5
|
||||||
|
{0x3C,0x4A,0x49,0x49,0x30}, // 6
|
||||||
|
{0x01,0x71,0x09,0x05,0x03}, // 7
|
||||||
|
{0x36,0x49,0x49,0x49,0x36}, // 8
|
||||||
|
{0x06,0x49,0x49,0x29,0x1E}, // 9
|
||||||
|
{0x00,0x36,0x36,0x00,0x00}, // :
|
||||||
|
{0x00,0x56,0x36,0x00,0x00}, // ;
|
||||||
|
{0x08,0x14,0x22,0x41,0x00}, // <
|
||||||
|
{0x14,0x14,0x14,0x14,0x14}, // =
|
||||||
|
{0x00,0x41,0x22,0x14,0x08}, // >
|
||||||
|
{0x02,0x01,0x51,0x09,0x06}, // ?
|
||||||
|
{0x32,0x49,0x79,0x41,0x3E}, // @
|
||||||
|
{0x7E,0x11,0x11,0x11,0x7E}, // A
|
||||||
|
{0x7F,0x49,0x49,0x49,0x36}, // B
|
||||||
|
{0x3E,0x41,0x41,0x41,0x22}, // C
|
||||||
|
{0x7F,0x41,0x41,0x22,0x1C}, // D
|
||||||
|
{0x7F,0x49,0x49,0x49,0x41}, // E
|
||||||
|
{0x7F,0x09,0x09,0x09,0x01}, // F
|
||||||
|
{0x3E,0x41,0x49,0x49,0x7A}, // G
|
||||||
|
{0x7F,0x08,0x08,0x08,0x7F}, // H
|
||||||
|
{0x00,0x41,0x7F,0x41,0x00}, // I
|
||||||
|
{0x20,0x40,0x41,0x3F,0x01}, // J
|
||||||
|
{0x7F,0x08,0x14,0x22,0x41}, // K
|
||||||
|
{0x7F,0x40,0x40,0x40,0x40}, // L
|
||||||
|
{0x7F,0x02,0x04,0x02,0x7F}, // M
|
||||||
|
{0x7F,0x04,0x08,0x10,0x7F}, // N
|
||||||
|
{0x3E,0x41,0x41,0x41,0x3E}, // O
|
||||||
|
{0x7F,0x09,0x09,0x09,0x06}, // P
|
||||||
|
{0x3E,0x41,0x51,0x21,0x5E}, // Q
|
||||||
|
{0x7F,0x09,0x19,0x29,0x46}, // R
|
||||||
|
{0x46,0x49,0x49,0x49,0x31}, // S
|
||||||
|
{0x01,0x01,0x7F,0x01,0x01}, // T
|
||||||
|
{0x3F,0x40,0x40,0x40,0x3F}, // U
|
||||||
|
{0x1F,0x20,0x40,0x20,0x1F}, // V
|
||||||
|
{0x3F,0x40,0x38,0x40,0x3F}, // W
|
||||||
|
{0x63,0x14,0x08,0x14,0x63}, // X
|
||||||
|
{0x07,0x08,0x70,0x08,0x07}, // Y
|
||||||
|
{0x61,0x51,0x49,0x45,0x43}, // Z
|
||||||
|
{0x00,0x7F,0x41,0x41,0x00}, // [
|
||||||
|
{0x02,0x04,0x08,0x10,0x20}, // backslash
|
||||||
|
{0x00,0x41,0x41,0x7F,0x00}, // ]
|
||||||
|
{0x04,0x02,0x01,0x02,0x04}, // ^
|
||||||
|
{0x40,0x40,0x40,0x40,0x40}, // _
|
||||||
|
{0x00,0x01,0x02,0x04,0x00}, // `
|
||||||
|
{0x20,0x54,0x54,0x54,0x78}, // a
|
||||||
|
{0x7F,0x48,0x44,0x44,0x38}, // b
|
||||||
|
{0x38,0x44,0x44,0x44,0x20}, // c
|
||||||
|
{0x38,0x44,0x44,0x48,0x7F}, // d
|
||||||
|
{0x38,0x54,0x54,0x54,0x18}, // e
|
||||||
|
{0x08,0x7E,0x09,0x01,0x02}, // f
|
||||||
|
{0x0C,0x52,0x52,0x52,0x3E}, // g
|
||||||
|
{0x7F,0x08,0x04,0x04,0x78}, // h
|
||||||
|
{0x00,0x44,0x7D,0x40,0x00}, // i
|
||||||
|
{0x20,0x40,0x44,0x3D,0x00}, // j
|
||||||
|
{0x7F,0x10,0x28,0x44,0x00}, // k
|
||||||
|
{0x00,0x41,0x7F,0x40,0x00}, // l
|
||||||
|
{0x7C,0x04,0x18,0x04,0x78}, // m
|
||||||
|
{0x7C,0x08,0x04,0x04,0x78}, // n
|
||||||
|
{0x38,0x44,0x44,0x44,0x38}, // o
|
||||||
|
{0x7C,0x14,0x14,0x14,0x08}, // p
|
||||||
|
{0x08,0x14,0x14,0x18,0x7C}, // q
|
||||||
|
{0x7C,0x08,0x04,0x04,0x08}, // r
|
||||||
|
{0x48,0x54,0x54,0x54,0x20}, // s
|
||||||
|
{0x04,0x3F,0x44,0x40,0x20}, // t
|
||||||
|
{0x3C,0x40,0x40,0x20,0x7C}, // u
|
||||||
|
{0x1C,0x20,0x40,0x20,0x1C}, // v
|
||||||
|
{0x3C,0x40,0x30,0x40,0x3C}, // w
|
||||||
|
{0x44,0x28,0x10,0x28,0x44}, // x
|
||||||
|
{0x0C,0x50,0x50,0x50,0x3C}, // y
|
||||||
|
{0x44,0x64,0x54,0x4C,0x44}, // z
|
||||||
|
{0x00,0x08,0x36,0x41,0x00}, // {
|
||||||
|
{0x00,0x00,0x7F,0x00,0x00}, // |
|
||||||
|
{0x00,0x41,0x36,0x08,0x00}, // }
|
||||||
|
{0x10,0x08,0x08,0x10,0x08}, // ~
|
||||||
|
};
|
||||||
|
|
||||||
|
static void drawText(const char* text,float x,float y,float scale,
|
||||||
|
float r,float g,float b,float a=1.0f){
|
||||||
|
float cx=x;
|
||||||
|
for(int i=0;text[i];i++){
|
||||||
|
int c=text[i]-32;
|
||||||
|
if(c<0||c>=(int)(sizeof(FONT5x7)/sizeof(FONT5x7[0]))){cx+=(6*scale);continue;}
|
||||||
|
for(int col=0;col<5;col++){
|
||||||
|
uint8_t bits=FONT5x7[c][col];
|
||||||
|
for(int row=0;row<7;row++){
|
||||||
|
if(bits&(1<<row))
|
||||||
|
UI.rect(cx+col*scale,y+row*scale,scale,scale,r,g,b,a);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cx+=6*scale;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Inventory ───────────────────────────────────────────────────────────────
|
||||||
|
struct Inventory {
|
||||||
|
uint8_t hotbar[HOTBAR_SIZE]={GRASS,DIRT,STONE,WOOD,LEAVES,SAND,SNOW,WATER};
|
||||||
|
int selected=0;
|
||||||
|
bool open=false;
|
||||||
|
uint8_t selectedBlock()const{return hotbar[selected];}
|
||||||
|
};
|
||||||
|
|
||||||
|
static void drawInventory(Inventory& inv){
|
||||||
|
const float SLOT=52.0f;
|
||||||
|
const float GAP=6.0f;
|
||||||
|
const float BORDER=4.0f;
|
||||||
|
int n=HOTBAR_SIZE;
|
||||||
|
float totalW=n*(SLOT+GAP)-GAP;
|
||||||
|
float hx=(SCREEN_W-totalW)*0.5f;
|
||||||
|
float hy=SCREEN_H-SLOT-16.0f;
|
||||||
|
|
||||||
|
// Background bar
|
||||||
|
UI.rect(hx-8,hy-8,totalW+16,SLOT+16,0.15f,0.15f,0.15f,0.75f);
|
||||||
|
UI.outline(hx-8,hy-8,totalW+16,SLOT+16,0.0f,0.0f,0.0f);
|
||||||
|
|
||||||
|
for(int i=0;i<n;i++){
|
||||||
|
float sx=hx+i*(SLOT+GAP),sy=hy;
|
||||||
|
bool sel=(i==inv.selected);
|
||||||
|
UI.rect(sx,sy,SLOT,SLOT,sel?0.30f:0.20f,sel?0.30f:0.20f,sel?0.30f:0.20f,0.9f);
|
||||||
|
uint8_t bt=inv.hotbar[i];
|
||||||
|
if(bt!=AIR){
|
||||||
|
auto& bc=BLOCK_COLORS[bt];
|
||||||
|
UI.rect(sx+BORDER,sy+BORDER,SLOT-2*BORDER,SLOT-2*BORDER-10,bc.r,bc.g,bc.b,1.0f);
|
||||||
|
UI.rect(sx+BORDER,sy+SLOT-BORDER-10-4,SLOT-2*BORDER,4,bc.r*0.65f,bc.g*0.65f,bc.b*0.65f,1.0f);
|
||||||
|
}
|
||||||
|
if(sel) UI.outline(sx,sy,SLOT,SLOT,1.0f,1.0f,1.0f,BORDER*0.5f);
|
||||||
|
else UI.outline(sx,sy,SLOT,SLOT,0.0f,0.0f,0.0f,2.0f);
|
||||||
|
char num[3]; snprintf(num,3,"%d",i+1);
|
||||||
|
drawText(num,sx+4,sy+SLOT-12,1.5f,0.8f,0.8f,0.8f);
|
||||||
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
uint8_t bt=inv.selectedBlock();
|
||||||
|
const char* name=BLOCK_NAMES[bt];
|
||||||
|
float tw=strlen(name)*6*2.0f;
|
||||||
|
drawText(name,(SCREEN_W-tw)*0.5f,hy-26,2.0f,1.0f,1.0f,1.0f,0.9f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!inv.open) return;
|
||||||
|
|
||||||
|
const int COLS=4;
|
||||||
|
const int ROWS=(NUM_BLOCK_TYPES-1+COLS-1)/COLS;
|
||||||
|
float iw=COLS*(SLOT+GAP)-GAP+32.0f;
|
||||||
|
float ih=ROWS*(SLOT+GAP)-GAP+60.0f;
|
||||||
|
float ix=(SCREEN_W-iw)*0.5f;
|
||||||
|
float iy=(SCREEN_H-ih)*0.5f;
|
||||||
|
|
||||||
|
UI.rect(0,0,(float)SCREEN_W,(float)SCREEN_H,0,0,0,0.45f);
|
||||||
|
UI.rect(ix,iy,iw,ih,0.18f,0.18f,0.18f,0.95f);
|
||||||
|
UI.outline(ix,iy,iw,ih,0.6f,0.6f,0.6f,2.0f);
|
||||||
|
drawText("INVENTORY",ix+10,iy+10,2.0f,1.0f,1.0f,1.0f);
|
||||||
|
|
||||||
|
for(int bi=1;bi<NUM_BLOCK_TYPES;bi++){
|
||||||
|
int row=(bi-1)/COLS,col=(bi-1)%COLS;
|
||||||
|
float sx=ix+16+col*(SLOT+GAP);
|
||||||
|
float sy=iy+40+row*(SLOT+GAP);
|
||||||
|
bool inHotbar=false;
|
||||||
|
for(int h=0;h<HOTBAR_SIZE;h++) if(inv.hotbar[h]==bi) inHotbar=true;
|
||||||
|
auto& bc=BLOCK_COLORS[bi];
|
||||||
|
UI.rect(sx,sy,SLOT,SLOT,0.25f,0.25f,0.25f,0.9f);
|
||||||
|
UI.rect(sx+BORDER,sy+BORDER,SLOT-2*BORDER,SLOT-2*BORDER-8,bc.r,bc.g,bc.b,1.0f);
|
||||||
|
UI.rect(sx+BORDER,sy+SLOT-BORDER-8-4,SLOT-2*BORDER,4,bc.r*0.65f,bc.g*0.65f,bc.b*0.65f,1.0f);
|
||||||
|
UI.outline(sx,sy,SLOT,SLOT,inHotbar?1.0f:0.4f,inHotbar?1.0f:0.4f,inHotbar?0.0f:0.4f,2.0f);
|
||||||
|
float scale=1.5f;
|
||||||
|
float tw=strlen(BLOCK_NAMES[bi])*6*scale;
|
||||||
|
float tx=sx+(SLOT-tw)*0.5f;
|
||||||
|
drawText(BLOCK_NAMES[bi],tx,sy+SLOT-10,scale,0.9f,0.9f,0.9f);
|
||||||
|
}
|
||||||
|
drawText("Click a slot (1-8) to select. E to close.",ix+10,iy+ih-18,1.5f,0.7f,0.7f,0.7f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── HUD ─────────────────────────────────────────────────────────────────────
|
||||||
|
static void drawHUD(const glm::vec3& pos,uint8_t /*selBlock*/,bool thirdPerson){
|
||||||
|
float cx=(float)SCREEN_W/2,cy=(float)SCREEN_H/2;
|
||||||
|
UI.rect(cx-1,cy-10,2,20,1,1,1,0.85f);
|
||||||
|
UI.rect(cx-10,cy-1,20,2,1,1,1,0.85f);
|
||||||
|
UI.rect(cx-1,cy-1,2,2,0,0,0,0.85f);
|
||||||
|
|
||||||
|
char buf[64];
|
||||||
|
snprintf(buf,64,"X:%.0f Y:%.0f Z:%.0f",pos.x,pos.y-PH,pos.z);
|
||||||
|
UI.rect(6,6,strlen(buf)*6*1.5f+6,18,0,0,0,0.5f);
|
||||||
|
drawText(buf,9,9,1.5f,1.0f,1.0f,1.0f);
|
||||||
|
|
||||||
|
if(thirdPerson)
|
||||||
|
drawText("F5: 3rd Person",9,28,1.5f,1.0f,1.0f,0.6f);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ─── Camera / Player ─────────────────────────────────────────────────────────
|
||||||
|
struct Camera {
|
||||||
|
glm::vec3 pos{0,40,0};
|
||||||
|
float yaw=0,pitch=0;
|
||||||
|
glm::vec3 vel{0,0,0};
|
||||||
|
bool onGround=false;
|
||||||
|
|
||||||
|
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)));
|
||||||
|
}
|
||||||
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 firstPersonView()const{return glm::lookAt(pos,pos+forward(),{0,1,0});}
|
||||||
|
glm::mat4 thirdPersonView()const{
|
||||||
|
glm::vec3 fwd=forward();
|
||||||
|
glm::vec3 eye=pos-fwd*6.0f+glm::vec3(0,2,0);
|
||||||
|
return glm::lookAt(eye,pos,{0,1,0});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
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;
|
if(y<0) return true; if(y>=WORLD_H) return false;
|
||||||
if(y>=WORLD_H) return false;
|
uint8_t b=getBlock(x,y,z); return b!=AIR&&b!=WATER&&b!=LEAVES;
|
||||||
uint8_t b=getBlock(x,y,z);
|
|
||||||
return b!=AIR&&b!=WATER&&b!=LEAVES;
|
|
||||||
}
|
}
|
||||||
static bool aabbSolid(float px,float py,float pz){
|
static bool aabbSolid(float px,float py,float pz){
|
||||||
int x0=(int)floorf(px-HW),x1=(int)floorf(px+HW);
|
int x0=(int)floorf(px-HW),x1=(int)floorf(px+HW);
|
||||||
@@ -273,6 +555,77 @@ static void moveCamera(Camera& cam,const glm::vec3& move,float dt){
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── Player model (3rd person) ───────────────────────────────────────────────
|
||||||
|
struct PlayerModel {
|
||||||
|
GLuint VAO=0,VBO=0,EBO=0;
|
||||||
|
std::vector<Vertex> verts;
|
||||||
|
std::vector<GLuint> idx;
|
||||||
|
int indexCount=0;
|
||||||
|
|
||||||
|
void addBox(float x0,float y0,float z0,float x1,float y1,float z1,
|
||||||
|
float r,float g,float b){
|
||||||
|
auto face=[&](glm::vec3 v0,glm::vec3 v1,glm::vec3 v2,glm::vec3 v3,float shade){
|
||||||
|
GLuint base=(GLuint)verts.size();
|
||||||
|
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({v1.x,v1.y,v1.z,sr,sg,sb});
|
||||||
|
verts.push_back({v2.x,v2.y,v2.z,sr,sg,sb}); verts.push_back({v3.x,v3.y,v3.z,sr,sg,sb});
|
||||||
|
idx.insert(idx.end(),{base,base+1,base+2,base,base+2,base+3});
|
||||||
|
};
|
||||||
|
face({x0,y1,z0},{x1,y1,z0},{x1,y1,z1},{x0,y1,z1},1.0f); // Top
|
||||||
|
face({x0,y0,z1},{x1,y0,z1},{x1,y0,z0},{x0,y0,z0},0.5f); // Bottom
|
||||||
|
face({x0,y0,z1},{x1,y0,z1},{x1,y1,z1},{x0,y1,z1},0.8f); // Front
|
||||||
|
face({x1,y0,z0},{x0,y0,z0},{x0,y1,z0},{x1,y1,z0},0.7f); // Back
|
||||||
|
face({x0,y0,z0},{x0,y0,z1},{x0,y1,z1},{x0,y1,z0},0.65f); // Left
|
||||||
|
face({x1,y0,z1},{x1,y0,z0},{x1,y1,z0},{x1,y1,z1},0.65f); // Right
|
||||||
|
}
|
||||||
|
|
||||||
|
void build(){
|
||||||
|
verts.clear(); idx.clear();
|
||||||
|
float sk=0.90f,sg_=0.72f,sb_=0.55f;
|
||||||
|
float tr=0.25f,tg=0.45f,tb=0.75f;
|
||||||
|
float lr=0.20f,lg=0.20f,lb=0.60f;
|
||||||
|
float hr=0.30f,hg=0.18f,hb=0.08f;
|
||||||
|
|
||||||
|
addBox(-0.3f,1.55f,-0.3f, 0.3f,2.15f,0.3f, sk,sg_,sb_); // Head
|
||||||
|
addBox(-0.32f,1.9f,-0.32f, 0.32f,2.17f,0.32f, hr,hg,hb); // Hair
|
||||||
|
addBox(-0.3f,0.7f,-0.2f, 0.3f,1.5f,0.2f, tr,tg,tb); // Body
|
||||||
|
addBox(-0.55f,0.7f,-0.15f, -0.32f,1.45f,0.15f, sk,sg_,sb_);// Left arm
|
||||||
|
addBox( 0.32f,0.7f,-0.15f, 0.55f,1.45f,0.15f, sk,sg_,sb_);// Right arm
|
||||||
|
addBox(-0.28f,0.0f,-0.15f, -0.05f,0.7f,0.15f, lr,lg,lb); // Left leg
|
||||||
|
addBox( 0.05f,0.0f,-0.15f, 0.28f,0.7f,0.15f, lr,lg,lb); // Right leg
|
||||||
|
|
||||||
|
indexCount=(int)idx.size();
|
||||||
|
|
||||||
|
if(!VAO){glGenVertexArrays(1,&VAO);glGenBuffers(1,&VBO);glGenBuffers(1,&EBO);}
|
||||||
|
glBindVertexArray(VAO);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER,VBO);
|
||||||
|
glBufferData(GL_ARRAY_BUFFER,verts.size()*sizeof(Vertex),verts.data(),GL_STATIC_DRAW);
|
||||||
|
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,EBO);
|
||||||
|
glBufferData(GL_ELEMENT_ARRAY_BUFFER,idx.size()*sizeof(GLuint),idx.data(),GL_STATIC_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);
|
||||||
|
glBindVertexArray(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw(GLuint prog,GLint mvpLoc,GLint camLoc,
|
||||||
|
const glm::mat4& proj,const glm::mat4& view,
|
||||||
|
const Camera& cam) const {
|
||||||
|
glm::vec3 feet=cam.pos-glm::vec3(0,PH,0);
|
||||||
|
glm::mat4 model=glm::translate(glm::mat4(1),feet);
|
||||||
|
model=glm::rotate(model,glm::radians(cam.yaw+180.0f),glm::vec3(0,1,0));
|
||||||
|
glm::mat4 mvp=proj*view*model;
|
||||||
|
glUseProgram(prog);
|
||||||
|
glUniformMatrix4fv(mvpLoc,1,GL_FALSE,glm::value_ptr(mvp));
|
||||||
|
glUniform3fv(camLoc,1,glm::value_ptr(cam.pos));
|
||||||
|
glBindVertexArray(VAO);
|
||||||
|
glDrawElements(GL_TRIANGLES,(GLsizei)indexCount,GL_UNSIGNED_INT,0);
|
||||||
|
glBindVertexArray(0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static PlayerModel PLAYER_MODEL;
|
||||||
|
|
||||||
|
// ─── Raycast ─────────────────────────────────────────────────────────────────
|
||||||
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(),p=cam.pos;
|
glm::vec3 dir=cam.forward(),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)};
|
||||||
@@ -287,24 +640,23 @@ static bool raycast(const Camera& cam,glm::ivec3& hit,glm::ivec3& prev){
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── Chunk streaming ─────────────────────────────────────────────────────────
|
||||||
static void updateChunks(int pcx,int pcz){
|
static void updateChunks(int pcx,int pcz){
|
||||||
for(int dx=-RENDER_DIST;dx<=RENDER_DIST;dx++)
|
for(int dx=-RENDER_DIST;dx<=RENDER_DIST;dx++)
|
||||||
for(int dz=-RENDER_DIST;dz<=RENDER_DIST;dz++){
|
for(int dz=-RENDER_DIST;dz<=RENDER_DIST;dz++){
|
||||||
ChunkKey k{pcx+dx,pcz+dz};
|
ChunkKey k{pcx+dx,pcz+dz};
|
||||||
if(CHUNKS.find(k)==CHUNKS.end()){
|
if(CHUNKS.find(k)==CHUNKS.end()){
|
||||||
auto c=std::make_unique<Chunk>(k.x,k.z);
|
auto c=std::make_unique<Chunk>(k.x,k.z); generateChunk(*c);
|
||||||
generateChunk(*c);
|
|
||||||
CHUNKS[k]=std::move(c);
|
CHUNKS[k]=std::move(c);
|
||||||
for(auto& [nk,nc]:CHUNKS)
|
for(auto& [nk,nc]:CHUNKS) if(abs(nk.x-k.x)+abs(nk.z-k.z)==1) nc->meshDirty=true;
|
||||||
if(abs(nk.x-k.x)+abs(nk.z-k.z)==1) nc->meshDirty=true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
std::vector<ChunkKey> toRemove;
|
std::vector<ChunkKey> toRemove;
|
||||||
for(auto& [k,c]:CHUNKS)
|
for(auto& [k,c]:CHUNKS) if(abs(k.x-pcx)>RENDER_DIST+1||abs(k.z-pcz)>RENDER_DIST+1) toRemove.push_back(k);
|
||||||
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);
|
for(auto& k:toRemove) CHUNKS.erase(k);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ─── Main ────────────────────────────────────────────────────────────────────
|
||||||
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);
|
||||||
@@ -313,22 +665,25 @@ 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 - Infinite World",
|
SDL_Window* win=SDL_CreateWindow("MiniCraft",
|
||||||
SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,SCREEN_W,SCREEN_H,SDL_WINDOW_OPENGL|SDL_WINDOW_SHOWN);
|
SDL_WINDOWPOS_CENTERED,SDL_WINDOWPOS_CENTERED,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; glewInit();
|
||||||
|
|
||||||
glEnable(GL_DEPTH_TEST); glEnable(GL_CULL_FACE); glCullFace(GL_BACK);
|
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 prog3d=buildProg(VS_SRC,FS_SRC);
|
||||||
GLint mvpLoc=glGetUniformLocation(prog,"uMVP");
|
GLint mvpLoc=glGetUniformLocation(prog3d,"uMVP");
|
||||||
GLint camLoc=glGetUniformLocation(prog,"uCamPos");
|
GLint camLoc=glGetUniformLocation(prog3d,"uCamPos");
|
||||||
|
|
||||||
|
UI.init();
|
||||||
initNoise(12345);
|
initNoise(12345);
|
||||||
std::cout<<"Generating spawn chunks...\n";
|
|
||||||
|
std::cout<<"Generating world...\n";
|
||||||
updateChunks(0,0);
|
updateChunks(0,0);
|
||||||
// build all initial meshes immediately
|
|
||||||
for(auto& [k,c]:CHUNKS) if(c->generated) buildChunkMesh(*c);
|
for(auto& [k,c]:CHUNKS) if(c->generated) buildChunkMesh(*c);
|
||||||
|
|
||||||
Camera cam;
|
Camera cam;
|
||||||
@@ -338,21 +693,26 @@ int main(){
|
|||||||
}
|
}
|
||||||
cam.pos.x=0.5f; cam.pos.z=0.5f;
|
cam.pos.x=0.5f; cam.pos.z=0.5f;
|
||||||
|
|
||||||
|
PLAYER_MODEL.build();
|
||||||
|
|
||||||
glm::mat4 proj=glm::perspective(glm::radians(70.0f),(float)SCREEN_W/SCREEN_H,0.05f,500.0f);
|
glm::mat4 proj=glm::perspective(glm::radians(70.0f),(float)SCREEN_W/SCREEN_H,0.05f,500.0f);
|
||||||
SDL_SetRelativeMouseMode(SDL_TRUE);
|
SDL_SetRelativeMouseMode(SDL_TRUE);
|
||||||
|
|
||||||
uint8_t selectedBlock=GRASS;
|
Inventory inv;
|
||||||
|
bool thirdPerson=false;
|
||||||
bool running=true;
|
bool running=true;
|
||||||
Uint64 last=SDL_GetPerformanceCounter();
|
Uint64 last=SDL_GetPerformanceCounter();
|
||||||
int lastPcx=INT_MAX,lastPcz=INT_MAX;
|
int lastPcx=INT_MAX,lastPcz=INT_MAX;
|
||||||
|
|
||||||
std::cout<<"=== MiniCraft Infinite World ===\n"
|
std::cout<<"=== MiniCraft ===\n"
|
||||||
<<"WASD Move\n"
|
<<"WASD Move\n"
|
||||||
<<"Space Jump\n"
|
<<"Space Jump\n"
|
||||||
<<"Mouse Look\n"
|
<<"Mouse Look\n"
|
||||||
<<"LMB Destroy block\n"
|
<<"LMB Break block\n"
|
||||||
<<"RMB Place block\n"
|
<<"RMB Place block\n"
|
||||||
<<"1-8 Grass/Dirt/Stone/Wood/Leaves/Sand/Snow/Water\n"
|
<<"1-8 Hotbar slot\n"
|
||||||
|
<<"E Open/close inventory\n"
|
||||||
|
<<"F5 Toggle 3rd person\n"
|
||||||
<<"ESC Quit\n";
|
<<"ESC Quit\n";
|
||||||
|
|
||||||
while(running){
|
while(running){
|
||||||
@@ -366,67 +726,110 @@ int main(){
|
|||||||
if(e.type==SDL_QUIT) running=false;
|
if(e.type==SDL_QUIT) running=false;
|
||||||
if(e.type==SDL_KEYDOWN){
|
if(e.type==SDL_KEYDOWN){
|
||||||
switch(e.key.keysym.sym){
|
switch(e.key.keysym.sym){
|
||||||
case SDLK_ESCAPE:running=false;break;
|
case SDLK_ESCAPE: running=false; break;
|
||||||
case SDLK_1:selectedBlock=GRASS;break;
|
case SDLK_e:
|
||||||
case SDLK_2:selectedBlock=DIRT;break;
|
inv.open=!inv.open;
|
||||||
case SDLK_3:selectedBlock=STONE;break;
|
SDL_SetRelativeMouseMode(inv.open?SDL_FALSE:SDL_TRUE);
|
||||||
case SDLK_4:selectedBlock=WOOD;break;
|
break;
|
||||||
case SDLK_5:selectedBlock=LEAVES;break;
|
case SDLK_F5: thirdPerson=!thirdPerson; break;
|
||||||
case SDLK_6:selectedBlock=SAND;break;
|
case SDLK_1: inv.selected=0; break;
|
||||||
case SDLK_7:selectedBlock=SNOW;break;
|
case SDLK_2: inv.selected=1; break;
|
||||||
case SDLK_8:selectedBlock=WATER;break;
|
case SDLK_3: inv.selected=2; break;
|
||||||
|
case SDLK_4: inv.selected=3; break;
|
||||||
|
case SDLK_5: inv.selected=4; break;
|
||||||
|
case SDLK_6: inv.selected=5; break;
|
||||||
|
case SDLK_7: inv.selected=6; break;
|
||||||
|
case SDLK_8: inv.selected=7; break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(e.type==SDL_MOUSEMOTION){
|
if(e.type==SDL_MOUSEWHEEL){
|
||||||
cam.yaw+=e.motion.xrel*MOUSE_SENS;
|
inv.selected=(inv.selected-e.wheel.y+HOTBAR_SIZE)%HOTBAR_SIZE;
|
||||||
cam.pitch-=e.motion.yrel*MOUSE_SENS;
|
|
||||||
if(cam.pitch>89)cam.pitch=89; if(cam.pitch<-89)cam.pitch=-89;
|
|
||||||
}
|
}
|
||||||
if(e.type==SDL_MOUSEBUTTONDOWN){
|
if(!inv.open){
|
||||||
glm::ivec3 hit,prev;
|
if(e.type==SDL_MOUSEMOTION){
|
||||||
if(raycast(cam,hit,prev)){
|
cam.yaw+=e.motion.xrel*MOUSE_SENS;
|
||||||
if(e.button.button==SDL_BUTTON_LEFT) setBlock(hit.x,hit.y,hit.z,AIR);
|
cam.pitch-=e.motion.yrel*MOUSE_SENS;
|
||||||
else if(e.button.button==SDL_BUTTON_RIGHT) setBlock(prev.x,prev.y,prev.z,selectedBlock);
|
if(cam.pitch>89)cam.pitch=89; if(cam.pitch<-89)cam.pitch=-89;
|
||||||
|
}
|
||||||
|
if(e.type==SDL_MOUSEBUTTONDOWN){
|
||||||
|
glm::ivec3 hit,prev;
|
||||||
|
if(raycast(cam,hit,prev)){
|
||||||
|
if(e.button.button==SDL_BUTTON_LEFT)
|
||||||
|
setBlock(hit.x,hit.y,hit.z,AIR);
|
||||||
|
else if(e.button.button==SDL_BUTTON_RIGHT)
|
||||||
|
setBlock(prev.x,prev.y,prev.z,inv.selectedBlock());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if(e.type==SDL_MOUSEBUTTONDOWN&&e.button.button==SDL_BUTTON_LEFT){
|
||||||
|
const int COLS=4; const float SLOT=52.0f,GAP=6.0f;
|
||||||
|
float iw=COLS*(SLOT+GAP)-GAP+32.0f;
|
||||||
|
float ih=((NUM_BLOCK_TYPES-1+COLS-1)/COLS)*(SLOT+GAP)-GAP+60.0f;
|
||||||
|
float ix=(SCREEN_W-iw)*0.5f,iy=(SCREEN_H-ih)*0.5f;
|
||||||
|
int mx=e.button.x,my=e.button.y;
|
||||||
|
for(int bi=1;bi<NUM_BLOCK_TYPES;bi++){
|
||||||
|
int row=(bi-1)/COLS,col=(bi-1)%COLS;
|
||||||
|
float sx=ix+16+col*(SLOT+GAP),sy=iy+40+row*(SLOT+GAP);
|
||||||
|
if(mx>=sx&&mx<=sx+SLOT&&my>=sy&&my<=sy+SLOT){
|
||||||
|
inv.hotbar[inv.selected]=(uint8_t)bi;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const Uint8* keys=SDL_GetKeyboardState(nullptr);
|
if(!inv.open){
|
||||||
glm::vec3 fwd=cam.forward(); fwd.y=0; if(glm::length(fwd)>0) fwd=glm::normalize(fwd);
|
const Uint8* keys=SDL_GetKeyboardState(nullptr);
|
||||||
glm::vec3 rgt=cam.right(); rgt.y=0; if(glm::length(rgt)>0) rgt=glm::normalize(rgt);
|
glm::vec3 fwd=cam.forward(); fwd.y=0; if(glm::length(fwd)>0) fwd=glm::normalize(fwd);
|
||||||
glm::vec3 move{0,0,0};
|
glm::vec3 rgt=cam.right(); rgt.y=0; if(glm::length(rgt)>0) rgt=glm::normalize(rgt);
|
||||||
if(keys[SDL_SCANCODE_W]) move+=fwd*MOVE_SPEED;
|
glm::vec3 move{0,0,0};
|
||||||
if(keys[SDL_SCANCODE_S]) move-=fwd*MOVE_SPEED;
|
if(keys[SDL_SCANCODE_W]) move+=fwd*MOVE_SPEED;
|
||||||
if(keys[SDL_SCANCODE_D]) move+=rgt*MOVE_SPEED;
|
if(keys[SDL_SCANCODE_S]) move-=fwd*MOVE_SPEED;
|
||||||
if(keys[SDL_SCANCODE_A]) move-=rgt*MOVE_SPEED;
|
if(keys[SDL_SCANCODE_D]) move+=rgt*MOVE_SPEED;
|
||||||
if(keys[SDL_SCANCODE_SPACE]&&cam.onGround) cam.vel.y=JUMP_VEL;
|
if(keys[SDL_SCANCODE_A]) move-=rgt*MOVE_SPEED;
|
||||||
moveCamera(cam,move,dt);
|
if(keys[SDL_SCANCODE_SPACE]&&cam.onGround) cam.vel.y=JUMP_VEL;
|
||||||
|
moveCamera(cam,move,dt);
|
||||||
|
}
|
||||||
|
|
||||||
int pcx=(int)floorf(cam.pos.x/CHUNK_SIZE);
|
int pcx=(int)floorf(cam.pos.x/CHUNK_SIZE);
|
||||||
int pcz=(int)floorf(cam.pos.z/CHUNK_SIZE);
|
int pcz=(int)floorf(cam.pos.z/CHUNK_SIZE);
|
||||||
if(pcx!=lastPcx||pcz!=lastPcz){
|
if(pcx!=lastPcx||pcz!=lastPcz){ updateChunks(pcx,pcz); lastPcx=pcx; lastPcz=pcz; }
|
||||||
updateChunks(pcx,pcz); lastPcx=pcx; lastPcz=pcz;
|
|
||||||
}
|
|
||||||
|
|
||||||
int rebuilt=0;
|
int rebuilt=0;
|
||||||
for(auto& [k,c]:CHUNKS)
|
for(auto& [k,c]:CHUNKS)
|
||||||
if(c->meshDirty&&c->generated){buildChunkMesh(*c);if(++rebuilt>=4)break;}
|
if(c->meshDirty&&c->generated){buildChunkMesh(*c);if(++rebuilt>=4)break;}
|
||||||
|
|
||||||
|
// ── Render 3D world ──
|
||||||
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
|
||||||
glUseProgram(prog);
|
glm::mat4 view=thirdPerson?cam.thirdPersonView():cam.firstPersonView();
|
||||||
glm::mat4 mvp=proj*cam.view();
|
glm::mat4 mvp=proj*view;
|
||||||
|
|
||||||
|
glUseProgram(prog3d);
|
||||||
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));
|
glUniform3fv(camLoc,1,glm::value_ptr(cam.pos));
|
||||||
for(auto& [k,c]:CHUNKS)
|
for(auto& [k,c]:CHUNKS)
|
||||||
if(c->indexCount>0){glBindVertexArray(c->VAO);glDrawElements(GL_TRIANGLES,c->indexCount,GL_UNSIGNED_INT,0);}
|
if(c->indexCount>0){glBindVertexArray(c->VAO);glDrawElements(GL_TRIANGLES,c->indexCount,GL_UNSIGNED_INT,0);}
|
||||||
|
|
||||||
|
// ── Player model (3rd person only) ──
|
||||||
|
if(thirdPerson)
|
||||||
|
PLAYER_MODEL.draw(prog3d,mvpLoc,camLoc,proj,view,cam);
|
||||||
|
|
||||||
|
// ── 2D UI ──
|
||||||
|
UI.begin();
|
||||||
|
drawHUD(cam.pos,inv.selectedBlock(),thirdPerson);
|
||||||
|
drawInventory(inv);
|
||||||
|
UI.end();
|
||||||
|
|
||||||
SDL_GL_SwapWindow(win);
|
SDL_GL_SwapWindow(win);
|
||||||
}
|
}
|
||||||
|
|
||||||
CHUNKS.clear();
|
CHUNKS.clear();
|
||||||
glDeleteProgram(prog);
|
glDeleteProgram(prog3d);
|
||||||
|
glDeleteProgram(UI.prog);
|
||||||
|
glDeleteVertexArrays(1,&UI.VAO); glDeleteBuffers(1,&UI.VBO);
|
||||||
|
glDeleteVertexArrays(1,&PLAYER_MODEL.VAO);
|
||||||
|
glDeleteBuffers(1,&PLAYER_MODEL.VBO); glDeleteBuffers(1,&PLAYER_MODEL.EBO);
|
||||||
SDL_GL_DeleteContext(ctx);
|
SDL_GL_DeleteContext(ctx);
|
||||||
SDL_DestroyWindow(win);
|
SDL_DestroyWindow(win);
|
||||||
SDL_Quit();
|
SDL_Quit();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user