First working prototype
This commit is contained in:
453
main.cpp
Normal file
453
main.cpp
Normal file
@@ -0,0 +1,453 @@
|
||||
#include <SDL2/SDL.h>
|
||||
#include <GL/glew.h>
|
||||
#include <SDL2/SDL_opengl.h>
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstring>
|
||||
#include <unordered_map>
|
||||
|
||||
// Instalation
|
||||
// On root:
|
||||
// mkdir build
|
||||
// cmake .
|
||||
// make -j$(nproc)
|
||||
// ./minicraft
|
||||
|
||||
// ─── Constants ───────────────────────────────────────────────────────────────
|
||||
static const int SCREEN_W = 1280;
|
||||
static const int SCREEN_H = 720;
|
||||
static const int CHUNK_SIZE = 16;
|
||||
static const int WORLD_H = 16;
|
||||
static const float MOVE_SPEED = 5.0f;
|
||||
static const float MOUSE_SENS = 0.12f;
|
||||
static const float GRAVITY = -20.0f;
|
||||
static const float JUMP_VEL = 8.0f;
|
||||
|
||||
// ─── Block types ─────────────────────────────────────────────────────────────
|
||||
enum BlockType : uint8_t { AIR=0, GRASS, DIRT, STONE, WOOD, LEAVES, SAND, WATER };
|
||||
|
||||
struct BlockColor { float r,g,b; };
|
||||
static const BlockColor BLOCK_COLORS[] = {
|
||||
{0,0,0}, // AIR
|
||||
{0.40f,0.72f,0.24f}, // GRASS
|
||||
{0.55f,0.40f,0.22f}, // DIRT
|
||||
{0.55f,0.55f,0.55f}, // STONE
|
||||
{0.45f,0.30f,0.15f}, // WOOD
|
||||
{0.20f,0.60f,0.15f}, // LEAVES
|
||||
{0.90f,0.85f,0.55f}, // SAND
|
||||
{0.20f,0.45f,0.90f}, // WATER
|
||||
};
|
||||
|
||||
// face shade multipliers (top, bottom, front, back, left, right)
|
||||
static const float FACE_SHADE[] = {1.0f, 0.5f, 0.8f, 0.8f, 0.65f, 0.65f};
|
||||
|
||||
// ─── World ───────────────────────────────────────────────────────────────────
|
||||
static uint8_t WORLD[CHUNK_SIZE][WORLD_H][CHUNK_SIZE];
|
||||
|
||||
static int terrainHeight(int x, int z) {
|
||||
float h = 5.0f
|
||||
+ 3.0f * sinf(x * 0.25f) * cosf(z * 0.20f)
|
||||
+ 2.0f * sinf(x * 0.10f + z * 0.13f)
|
||||
+ 1.0f * cosf(x * 0.40f - z * 0.35f);
|
||||
return (int)h;
|
||||
}
|
||||
|
||||
static void generateWorld() {
|
||||
memset(WORLD, AIR, sizeof(WORLD));
|
||||
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;
|
||||
for (int t = trunk; t < trunk + 3 && t < WORLD_H; ++t)
|
||||
WORLD[x][t][z] = WOOD;
|
||||
for (int dx = -1; dx <= 1; ++dx)
|
||||
for (int dz = -1; dz <= 1; ++dz)
|
||||
for (int dy = trunk + 2; dy <= trunk + 4; ++dy) {
|
||||
int nx = x+dx, nz = z+dz;
|
||||
if (nx>=0&&nx<CHUNK_SIZE&&nz>=0&&nz<CHUNK_SIZE&&dy<WORLD_H)
|
||||
if (WORLD[nx][dy][nz] == AIR)
|
||||
WORLD[nx][dy][nz] = LEAVES;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Shaders ─────────────────────────────────────────────────────────────────
|
||||
static const char* VS_SRC = R"(
|
||||
#version 330 core
|
||||
layout(location=0) in vec3 aPos;
|
||||
layout(location=1) in vec3 aColor;
|
||||
out vec3 vColor;
|
||||
uniform mat4 uMVP;
|
||||
void main(){
|
||||
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() {
|
||||
GLuint vs = compileShader(GL_VERTEX_SHADER, VS_SRC);
|
||||
GLuint fs = compileShader(GL_FRAGMENT_SHADER, FS_SRC);
|
||||
GLuint p = glCreateProgram();
|
||||
glAttachShader(p, vs); glAttachShader(p, fs);
|
||||
glLinkProgram(p);
|
||||
glDeleteShader(vs); glDeleteShader(fs);
|
||||
return p;
|
||||
}
|
||||
|
||||
// ─── Mesh builder ────────────────────────────────────────────────────────────
|
||||
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 currentBlock, int nx, int ny, int nz) {
|
||||
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;
|
||||
// Transparent blocks like water/leaves hide internal faces of the *same* type,
|
||||
// but allow drawing faces of different adjacent blocks.
|
||||
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,
|
||||
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();
|
||||
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});
|
||||
}
|
||||
|
||||
static void buildMesh(std::vector<Vertex>& verts, std::vector<GLuint>& idx) {
|
||||
verts.clear(); idx.clear();
|
||||
for (int x=0;x<CHUNK_SIZE;++x)
|
||||
for (int y=0;y<WORLD_H;++y)
|
||||
for (int z=0;z<CHUNK_SIZE;++z) {
|
||||
uint8_t b = WORLD[x][y][z];
|
||||
if (b == AIR) continue;
|
||||
float r=BLOCK_COLORS[b].r, g=BLOCK_COLORS[b].g, bl=BLOCK_COLORS[b].b;
|
||||
float bx=x,by=y,bz=z;
|
||||
|
||||
// FIXED Winding Orders:
|
||||
// Top (+Y)
|
||||
if (shouldDrawFace(b, x,y+1,z))
|
||||
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]);
|
||||
// Bottom (-Y)
|
||||
if (shouldDrawFace(b, x,y-1,z))
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Camera / Player ─────────────────────────────────────────────────────────
|
||||
struct Camera {
|
||||
glm::vec3 pos{8,12,8};
|
||||
float yaw=-135.0f, pitch=0.0f;
|
||||
glm::vec3 vel{0,0,0};
|
||||
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::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) {
|
||||
int x=(int)floorf(fx), y=(int)floorf(fy), z=(int)floorf(fz);
|
||||
if (y < 0) return true; // solid floor under world
|
||||
if (x < 0 || x >= CHUNK_SIZE || z < 0 || z >= CHUNK_SIZE) return true; // invisible walls
|
||||
if (y >= WORLD_H) return false;
|
||||
uint8_t b = WORLD[x][y][z];
|
||||
return b != AIR && b != WATER;
|
||||
}
|
||||
|
||||
// FIXED: Now correctly checks all blocks intersecting the player's bounding volume
|
||||
static bool aabbSolid(float px, float py, float pz) {
|
||||
int minX = (int)floorf(px - HW);
|
||||
int maxX = (int)floorf(px + HW);
|
||||
int minY = (int)floorf(py - PH);
|
||||
int maxY = (int)floorf(py + 0.1f);
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
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; // 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) {
|
||||
// Moving downward
|
||||
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.vel.y = 0.0f;
|
||||
cam.onGround = true;
|
||||
} else {
|
||||
cam.onGround = false;
|
||||
}
|
||||
} else {
|
||||
// Moving upward
|
||||
if (aabbSolid(cam.pos.x, cam.pos.y, cam.pos.z)) {
|
||||
// Hit ceiling — snap head downwards
|
||||
cam.pos.y = floorf(cam.pos.y + 0.1f) - 0.1f - 0.001f;
|
||||
cam.vel.y = 0.0f;
|
||||
}
|
||||
cam.onGround = false;
|
||||
}
|
||||
}
|
||||
|
||||
// ─── Ray-cast for block placement/removal ────────────────────────────────────
|
||||
static bool raycast(const Camera& cam, glm::ivec3& hit, glm::ivec3& prev) {
|
||||
glm::vec3 dir = cam.forward();
|
||||
glm::vec3 p = cam.pos;
|
||||
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) {
|
||||
glm::vec3 rp = p + dir*t;
|
||||
glm::ivec3 bp{(int)floorf(rp.x),(int)floorf(rp.y),(int)floorf(rp.z)};
|
||||
|
||||
// Skip out of bounds, but don't break the ray
|
||||
if (bp.x<0||bp.x>=CHUNK_SIZE||bp.y<0||bp.y>=WORLD_H||bp.z<0||bp.z>=CHUNK_SIZE) {
|
||||
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;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// ─── Main ────────────────────────────────────────────────────────────────────
|
||||
int main() {
|
||||
SDL_Init(SDL_INIT_VIDEO);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
|
||||
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
|
||||
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
|
||||
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
|
||||
|
||||
SDL_Window* win = SDL_CreateWindow("MiniCraft",
|
||||
SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED,
|
||||
SCREEN_W, SCREEN_H,
|
||||
SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN);
|
||||
SDL_GLContext ctx = SDL_GL_CreateContext(win);
|
||||
SDL_GL_SetSwapInterval(1);
|
||||
|
||||
glewExperimental = GL_TRUE;
|
||||
glewInit();
|
||||
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glEnable(GL_CULL_FACE);
|
||||
glCullFace(GL_BACK);
|
||||
glClearColor(0.53f, 0.81f, 0.98f, 1.0f);
|
||||
|
||||
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");
|
||||
|
||||
SDL_SetRelativeMouseMode(SDL_TRUE);
|
||||
|
||||
Camera cam;
|
||||
bool running = true;
|
||||
Uint64 last = SDL_GetPerformanceCounter();
|
||||
|
||||
uint8_t selectedBlock = GRASS;
|
||||
bool meshDirty = false;
|
||||
|
||||
std::cout << "=== MiniCraft Controls ===\n"
|
||||
<< "WASD - Move\n"
|
||||
<< "Space - Jump\n"
|
||||
<< "Mouse - Look\n"
|
||||
<< "LMB - Destroy block\n"
|
||||
<< "RMB - Place block\n"
|
||||
<< "1-7 - Select block type\n"
|
||||
<< "ESC - Quit\n";
|
||||
|
||||
while (running) {
|
||||
Uint64 now = SDL_GetPerformanceCounter();
|
||||
float dt = (float)(now - last) / SDL_GetPerformanceFrequency();
|
||||
if (dt > 0.1f) dt = 0.1f;
|
||||
last = now;
|
||||
|
||||
SDL_Event e;
|
||||
while (SDL_PollEvent(&e)) {
|
||||
if (e.type == SDL_QUIT) running = false;
|
||||
if (e.type == SDL_KEYDOWN) {
|
||||
if (e.key.keysym.sym == SDLK_ESCAPE) running = false;
|
||||
if (e.key.keysym.sym == SDLK_1) selectedBlock = GRASS;
|
||||
if (e.key.keysym.sym == SDLK_2) selectedBlock = DIRT;
|
||||
if (e.key.keysym.sym == SDLK_3) selectedBlock = STONE;
|
||||
if (e.key.keysym.sym == SDLK_4) selectedBlock = WOOD;
|
||||
if (e.key.keysym.sym == SDLK_5) selectedBlock = LEAVES;
|
||||
if (e.key.keysym.sym == SDLK_6) selectedBlock = SAND;
|
||||
if (e.key.keysym.sym == SDLK_7) selectedBlock = WATER;
|
||||
}
|
||||
if (e.type == SDL_MOUSEMOTION) {
|
||||
cam.yaw += e.motion.xrel * MOUSE_SENS;
|
||||
cam.pitch -= e.motion.yrel * MOUSE_SENS;
|
||||
if (cam.pitch > 89.0f) cam.pitch = 89.0f;
|
||||
if (cam.pitch < -89.0f) cam.pitch = -89.0f;
|
||||
}
|
||||
if (e.type == SDL_MOUSEBUTTONDOWN) {
|
||||
glm::ivec3 hit, prev;
|
||||
if (raycast(cam, hit, prev)) {
|
||||
if (e.button.button == SDL_BUTTON_LEFT) {
|
||||
WORLD[hit.x][hit.y][hit.z] = AIR;
|
||||
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);
|
||||
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 move{0,0,0};
|
||||
|
||||
if (keys[SDL_SCANCODE_W]) 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_A]) move -= rgt * MOVE_SPEED;
|
||||
if (keys[SDL_SCANCODE_SPACE] && cam.onGround) cam.vel.y = JUMP_VEL;
|
||||
|
||||
moveCamera(cam, move, dt);
|
||||
|
||||
// Render
|
||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
glUseProgram(prog);
|
||||
glm::mat4 mvp = proj * cam.view();
|
||||
glUniformMatrix4fv(mvpLoc,1,GL_FALSE,glm::value_ptr(mvp));
|
||||
|
||||
glBindVertexArray(VAO);
|
||||
glDrawElements(GL_TRIANGLES,(GLsizei)idx.size(),GL_UNSIGNED_INT,0);
|
||||
|
||||
SDL_GL_SwapWindow(win);
|
||||
}
|
||||
|
||||
glDeleteVertexArrays(1,&VAO);
|
||||
glDeleteBuffers(1,&VBO);
|
||||
glDeleteBuffers(1,&EBO);
|
||||
glDeleteProgram(prog);
|
||||
SDL_GL_DeleteContext(ctx);
|
||||
SDL_DestroyWindow(win);
|
||||
SDL_Quit();
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user