/* #include */
#include
#include
#include
#include
#include
#include
#include
// i686-w64-mingw32-gcc main.c -O0 -lopengl32 -lglut && ./a.exe
// extensions
PFNGLLOCKARRAYSEXTPROC glLockArraysEXT;
PFNGLUNLOCKARRAYSEXTPROC glUnlockArraysEXT;
float randFloat() { return ((float)(rand() % 1000000)/1000000.0f); }
int last_time = 0;
#define CHUNK_H 16
#define CHUNK_V 128
#define RENDER_DISTANCE 16
// Camera state
float camX = 1000, camY = CHUNK_V/2, camZ = 1000; // position
float camYaw = 0.0f, camPitch = 0.0f; // angles in degrees
float moveSpeed = 0.1f;
float mouseSensitivity = 0.2f;
// Mouse previous position
int lastMouseX = -1, lastMouseY = -1;
// Key state
int keys[256] = {0};
void error(char* s) {
printf(s);
exit(1);
}
GLuint load_ppm_texture(const char *path)
{
#define PPM_HEADER_LIMIT 128
FILE *f = fopen(path, "rb");
if (!f) {
error("Error: failed to load texture\n");
}
char header[PPM_HEADER_LIMIT];
int hlen = 0;
int spaces = 0;
while (hlen < PPM_HEADER_LIMIT-1 && spaces < 4) {
int c = fgetc(f);
if (c == EOF) { fclose(f); return 0; }
header[hlen++] = c;
if (c == ' ' || c == '\n' || c == '\t') spaces++;
}
if (hlen >= PPM_HEADER_LIMIT-1) {
fclose(f);
return 0;
}
header[hlen] = 0;
char magic[3];
int width, height, maxval;
if (sscanf(header, "%2s %d %d %d", magic, &width, &height, &maxval) != 4) {
fclose(f);
return 0;
}
if (strcmp(magic, "P6") != 0 || maxval != 255) {
fclose(f);
return 0;
}
size_t size = width * height * 3;
unsigned char *rgb = malloc(size);
if (!rgb) {
fclose(f);
return 0;
}
if (fread(rgb, 1, size, f) != size) {
free(rgb);
fclose(f);
return 0;
}
fclose(f);
/* expand to RGBA with white colorkey */
size_t pixels = width * height;
unsigned char *rgba = malloc(pixels * 4);
for (size_t i = 0; i < pixels; i++) {
unsigned char r = rgb[i*3+0];
unsigned char g = rgb[i*3+1];
unsigned char b = rgb[i*3+2];
rgba[i*4+0] = r;
rgba[i*4+1] = g;
rgba[i*4+2] = b;
if (r==0 && g==0 && b==0)
rgba[i*4+3] = 0; /* transparent */
else
rgba[i*4+3] = 255; /* opaque */
}
free(rgb);
GLuint tex;
glGenTextures(1, &tex);
glBindTexture(GL_TEXTURE_2D, tex);
glPixelStorei(GL_UNPACK_ALIGNMENT,1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexImage2D(
GL_TEXTURE_2D,
0,
GL_RGBA,
width,
height,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
rgba
);
free(rgba);
return tex;
}
typedef struct { int x, y; } ivec2;
typedef unsigned char block;
typedef struct {
block b[CHUNK_H][CHUNK_V][CHUNK_H]; /* block ids */
char is_vector_buffer_valid;
int vector_buffer_len; /* last element in vector buffer */
/* some extra positions in the buffer are dedicated to drawing lights */
int vector_buffer_light_len;
int vector_buffer_allocated; /* size of vector buffer's backing array */
GLfloat* vector_buffer;
unsigned random; /* used for graphic effects */
char visible;
} chunk;
typedef struct {
chunk* chunk[RENDER_DISTANCE][RENDER_DISTANCE];
ivec2 pivot;
} world;
world* world_new() {
world* new_world = calloc(1, sizeof(world));
return new_world;
}
void world_move_xp(world* w) { }
void world_move_xn(world* w) { }
void world_move_zp(world* w) { }
void world_move_zn(world* w) { }
#define static_range(A, arr) for (int A = 0; A < sizeof(arr)/sizeof((arr)[0]); A++)
enum block_id {
block_id_null,
block_id_air,
block_id_stone,
block_id_grass,
block_id_water,
block_id_cloud,
block_id_light,
block_ids,
};
chunk* chunk_new() {
chunk* newchunk = malloc(sizeof(chunk));
newchunk->vector_buffer = 0;
newchunk->is_vector_buffer_valid = 0;
/* properly initialized chunks are guaranteed to be full of air */
memset(newchunk->b, block_id_air, sizeof(newchunk->b));
return newchunk;
}
void chunk_free(chunk* c) {
if (!c) return;
if (c->vector_buffer) free(c->vector_buffer);
free(c);
}
static int floorf_to_int(float f) {
int i = (int)f;
if (f < 0.0f && (i != f)) {
return i - 1;
}
return i;
}
unsigned seed = 0;
static uint32_t hash(int x, int y) {
uint32_t h = seed + (uint32_t)x * 374761393 + (uint32_t)y * 668265263;
h = (h ^ (h >> 13)) * 1274126177;
return h ^ (h >> 16);
}
float noise2d(float x, float y) {
int xi = floorf_to_int(x);
int yi = floorf_to_int(y);
float xf = x - (float)xi;
float yf = y - (float)yi;
float v00 = (float)hash(xi, yi) / 4294967296.0f;
float v10 = (float)hash(xi + 1, yi) / 4294967296.0f;
float v01 = (float)hash(xi, yi + 1) / 4294967296.0f;
float v11 = (float)hash(xi + 1, yi + 1) / 4294967296.0f;
// smoothstep
float u = xf * xf * (3.0f - 2.0f * xf);
float v = yf * yf * (3.0f - 2.0f * yf);
float x1 = v00 + u * (v10 - v00);
float x2 = v01 + u * (v11 - v01);
return x1 + v * (x2 - x1);
}
void chunk_terrain(chunk* c, int x, int y) {
c->random = noise2d(x, y);
static_range(i, c->b) {
static_range(j, c->b[i]) {
static_range(k, c->b[i][j]) {
if (j < (CHUNK_V/3)) {
c->b[i][j][k] = block_id_stone;
continue;
}
float n = (noise2d((x + i)/50.0f, (y + k)/50.0f)) * (CHUNK_V/2) + (CHUNK_V/3);
if ( n > j ) {
c->b[i][j][k] = block_id_stone;
} else if ((n + 1.0f) > j) {
c->b[i][j][k] = block_id_grass;
if ((rand() % 1000) == 0)
c->b[i][j+1][k] = block_id_light;
}
if ( (j < (CHUNK_V/2)) && (n < j) ) {
c->b[i][j][k] = block_id_water;
}
}
}
}
}
block get_block(world* w, int x, int y, int z) {
if (x < w->pivot.x) error("requested out of bounds block in negative x\n");
if (x > ((w->pivot.x)+(CHUNK_H*RENDER_DISTANCE))) error("requested out of bounds block in positive x\n");
if (z < w->pivot.y) error("requested out of bounds block in z\n");
if (z > ((w->pivot.y)+(CHUNK_H*RENDER_DISTANCE))) error("requested out of bounds block in positive z\n");
if ((y < 0) || (y >= CHUNK_V)) error("requested out of bounds block in y\n");
/* todo look at replacing the divisions and modulus with & and % */
ivec2 world_index = (ivec2){
(x - w->pivot.x)/CHUNK_H,
(z - w->pivot.y)/CHUNK_H
};
block b = w->chunk[world_index.x][world_index.y]->b[x%CHUNK_H][y][z%CHUNK_H];
if (b == block_id_null) error("attempted to read null block\n");
return b;
}
#define LIGHT_LEVELS 16
float world_daylight = 0.5f;
void daylight_update(int tick) {
}
/* p represents the absolute position of the chunk */
void chunk_update_vector_array(world* w, chunk* c, int x, int y) {
const float TILE = 16.0f/256.0f;
float texU0(unsigned face) { return face * TILE; }
float texU1(unsigned face) { return face * TILE + TILE; }
float texV0(block id) { return id * TILE; }
float texV1(block id) { return id * TILE + TILE; }
#define VECTOR_BUCKET 4096
void vector_buffer_push(chunk *c, float v)
{
if (c->vector_buffer_allocated == 0)
{
c->vector_buffer_allocated = VECTOR_BUCKET;
c->vector_buffer_len = 0;
c->vector_buffer_light_len = 0;
c->vector_buffer = malloc(sizeof(GLfloat) * VECTOR_BUCKET);
}
if (c->vector_buffer_len >= c->vector_buffer_allocated)
{
size_t new_cap = c->vector_buffer_allocated + VECTOR_BUCKET;
GLfloat *tmp = realloc(c->vector_buffer, sizeof(GLfloat) * new_cap);
if (!tmp) return; /* or abort */
c->vector_buffer = tmp;
c->vector_buffer_allocated = new_cap;
}
c->vector_buffer[c->vector_buffer_len++] = v;
}
void vector_buffer_light_push(chunk *c, float v)
{
if (c->vector_buffer_allocated == 0)
{
c->vector_buffer_allocated = VECTOR_BUCKET;
c->vector_buffer_len = 0;
c->vector_buffer_light_len = 0;
c->vector_buffer = malloc(sizeof(GLfloat) * VECTOR_BUCKET);
}
int index = c->vector_buffer_len + c->vector_buffer_light_len;
if (index >= c->vector_buffer_allocated)
{
size_t new_cap = c->vector_buffer_allocated + VECTOR_BUCKET;
GLfloat *tmp = realloc(c->vector_buffer, sizeof(GLfloat) * new_cap);
if (!tmp) return;
c->vector_buffer = tmp;
c->vector_buffer_allocated = new_cap;
}
c->vector_buffer[index] = v;
c->vector_buffer_light_len++;
}
int light_sources_count = 0;
static int light_sources[CHUNK_H*CHUNK_H*CHUNK_V][3];
for(int i = -CHUNK_H/2; i < (3*CHUNK_H/2); i++) {
for(int j = 0; j < CHUNK_V; j++) {
for(int k = -CHUNK_H/2; k < (3*CHUNK_H/2); k++) {
block b = get_block(w, x + i, j, y + k);
if (b == block_id_light) {
light_sources[light_sources_count][0] = x + i;
light_sources[light_sources_count][1] = j;
light_sources[light_sources_count][2] = y + k;
light_sources_count++;
}
}
}
}
float lighting(int x, int y, int z) {
float artificial_lighting = 0.0f;
for(int i = 0; i < light_sources_count; i++) {
int d = abs(x - light_sources[i][0]) + abs(y - light_sources[i][1]) + abs(z - light_sources[i][2]);
if (d < LIGHT_LEVELS) {
if (d == 0) {
artificial_lighting = 1.0f;
break;
}
if ((1.0f / (float)d) > artificial_lighting)
artificial_lighting = (1.0f / (float)d);
}
}
if (y > (CHUNK_V/2)) {
return (world_daylight + artificial_lighting);
}
float f = (float)world_daylight / (float)(CHUNK_V/2);
return f*f*f + artificial_lighting;
}
/* emit a quad with interleaved xyzuv */
void emit_face_run(
chunk* c,
const GLfloat *cube,
int i, int j, int k, int run,
float r, float g, float b,
const float uv[8],
int *acc
){
for(int v=0; v<4; v++) {
float z = cube[v*3+2];
if (z > 0) z += (run - 1);
vector_buffer_push(c, cube[v*3+0] + i + x);
vector_buffer_push(c, cube[v*3+1] + j);
vector_buffer_push(c, z + k + y);
vector_buffer_push(c, uv[v*2+0]);
vector_buffer_push(c, uv[v*2+1]);
vector_buffer_push(c, r);
vector_buffer_push(c, g);
vector_buffer_push(c, b);
}
}
void emit_face(
chunk* c,
const GLfloat *cube,
int i, int j, int k,
float r, float g, float b,
unsigned face,
block id,
int *acc
){
float u0 = texU0(face);
float u1 = texU1(face);
float v0 = texV0(id);
float v1 = texV1(id);
const float uv[8] = {
u0,v0,
u1,v0,
u1,v1,
u0,v1
};
for(int v=0; v<4; v++) {
vector_buffer_push(c, cube[v*3+0] + i + x);
vector_buffer_push(c, cube[v*3+1] + j);
vector_buffer_push(c, cube[v*3+2] + k + y);
vector_buffer_push(c, uv[v*2+0]);
vector_buffer_push(c, uv[v*2+1]);
vector_buffer_push(c, r);
vector_buffer_push(c, g);
vector_buffer_push(c, b);
}
}
const static GLfloat CubeVertices_ZP[] = {
-0.5f,-0.5f, 0.5f,
0.5f,-0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
-0.5f, 0.5f, 0.5f
};
const static GLfloat CubeVertices_ZN[] = {
0.5f,-0.5f,-0.5f,
-0.5f,-0.5f,-0.5f,
-0.5f, 0.5f,-0.5f,
0.5f, 0.5f,-0.5f
};
const static GLfloat CubeVertices_XP[] = {
0.5f,-0.5f, 0.5f,
0.5f,-0.5f,-0.5f,
0.5f, 0.5f,-0.5f,
0.5f, 0.5f, 0.5f
};
const static GLfloat CubeVertices_XN[] = {
-0.5f,-0.5f,-0.5f,
-0.5f,-0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
-0.5f, 0.5f,-0.5f
};
const static GLfloat CubeVertices_YP[] = {
-0.5f, 0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
0.5f, 0.5f,-0.5f,
-0.5f, 0.5f,-0.5f
};
const static GLfloat CubeVertices_YN[] = {
-0.5f,-0.5f,-0.5f,
0.5f,-0.5f,-0.5f,
0.5f,-0.5f, 0.5f,
-0.5f,-0.5f, 0.5f
};
int acc = 0;
const static char is_transparent[block_ids] = {
[block_id_air] = 1,
[block_id_water] = 1,
};
static_range(i, c->b) {
static_range(j, c->b[i]) {
if (j <= 1) continue;
if (j >= (CHUNK_V-2)) continue;
for (int k = 0; k < CHUNK_H; )
{
block id = get_block(w,i+x,j,k+y);
if (id == block_id_air) {
k++;
continue;
}
block n0 = get_block(w,i+x+1,j,k+y);
if (!((n0 == block_id_air) || (!is_transparent[id] && is_transparent[n0]))) {
k++;
continue;
}
int run_len = 1;
while ((k + run_len) < CHUNK_H)
{
block id2 = get_block(w,i+x,j,k+y+run_len);
block n02 = get_block(w,i+x+1,j,k+y+run_len);
if (id2 != id)
break;
if (!((n02 == block_id_air) || (!is_transparent[id2] && is_transparent[n02])))
break;
run_len++;
}
float l = lighting(i+x,j,k+y);
float u0 = texU0(0);
float u1 = texU1(0);
float v0 = texV0(id);
float v1 = texV1(id);
const float UV_XP[8] = {
u1*run_len, v0,
u0 , v0,
u0 , v1,
u1*run_len, v1
};
emit_face_run(c, CubeVertices_XP,
i,j,k,run_len,
l,l,l,
UV_XP,&acc);
k += run_len;
}
for (int k = 0; k < CHUNK_H; )
{
block id = get_block(w,i+x,j,k+y);
if (id == block_id_air) {
k++;
continue;
}
block n1 = get_block(w,i+x-1,j,k+y);
if (!((n1 == block_id_air) || (!is_transparent[id] && is_transparent[n1]))) {
k++;
continue;
}
int run_len = 1;
while ((k + run_len) < CHUNK_H)
{
block id2 = get_block(w,i+x,j,k+y+run_len);
block n12 = get_block(w,i+x-1,j,k+y+run_len);
if (id2 != id)
break;
if (!((n12 == block_id_air) || (!is_transparent[id2] && is_transparent[n12])))
break;
run_len++;
}
float l = lighting(i+x,j,k+y);
float u0 = texU0(0);
float u1 = texU1(0);
float v0 = texV0(id);
float v1 = texV1(id);
const float UV_XN[8] = {
u0, v0,
u1*run_len, v0,
u1*run_len, v1,
u0, v1
};
emit_face_run(c, CubeVertices_XN,
i,j,k,run_len,
l,l,l,
UV_XN,&acc);
k += run_len;
}
for (int k = 0; k < CHUNK_H; )
{
block id = get_block(w,i+x,j,k+y);
if (id == block_id_air) {
k++;
continue;
}
block n4 = get_block(w,i+x,j+1,k+y);
if (!((n4 == block_id_air) || (!is_transparent[id] && is_transparent[n4]))) {
k++;
continue;
}
int run_len = 1;
while ((k + run_len) < CHUNK_H)
{
block id2 = get_block(w,i+x,j,k+y+run_len);
block n42 = get_block(w,i+x,j+1,k+y+run_len);
if (id2 != id)
break;
if (!((n42 == block_id_air) || (!is_transparent[id2] && is_transparent[n42])))
break;
run_len++;
}
float l = lighting(i+x,j,k+y);
float u0 = texU0(0);
float u1 = texU1(0);
float v0 = texV0(id);
float v1 = texV1(id);
const float UV_YP[8] = {
u0, v0,
u0, v1,
u1*run_len, v1,
u1*run_len, v0,
};
emit_face_run(c, CubeVertices_YP,
i,j,k,run_len,
l,l,l,
UV_YP,&acc);
k += run_len;
}
for (int k = 0; k < CHUNK_H; )
{
block id = get_block(w,i+x,j,k+y);
if (id == block_id_air) {
k++;
continue;
}
block n5 = get_block(w,i+x,j-1,k+y);
if (!((n5 == block_id_air) || (!is_transparent[id] && is_transparent[n5]))) {
k++;
continue;
}
int run_len = 1;
while ((k + run_len) < CHUNK_H)
{
block id2 = get_block(w,i+x,j,k+y+run_len);
block n52 = get_block(w,i+x,j-1,k+y+run_len);
if (id2 != id)
break;
if (!((n52 == block_id_air) || (!is_transparent[id2] && is_transparent[n52])))
break;
run_len++;
}
float l = lighting(i+x,j,k+y);
float u0 = texU0(0);
float u1 = texU1(0);
float v0 = texV0(id);
float v1 = texV1(id);
const float UV_YN[8] = {
u1*run_len, v0,
u1*run_len, v1,
u0, v1,
u0, v0
};
emit_face_run(c, CubeVertices_YN,
i,j,k,run_len,
l,l,l,
UV_YN,&acc);
k += run_len;
}
for (int k = 0; k < CHUNK_H; k++)
{
block id = get_block(w,i+x,j,k+y);
if (id == block_id_air)
continue;
float l = lighting(i+x,j,k+y);
block n2 = get_block(w,i+x,j,k+y+1);
if ((n2 == block_id_air) || (!is_transparent[id] && is_transparent[n2]))
emit_face(c, CubeVertices_ZP,
i,j,k,
l,l,l,
0,id,&acc);
block n3 = get_block(w,i+x,j,k+y-1);
if ((n3 == block_id_air) || (!is_transparent[id] && is_transparent[n3]))
emit_face(c, CubeVertices_ZN,
i,j,k,
l,l,l,
0,id,&acc);
}
}
}
printf("after occlusion: %d %d\n", c->vector_buffer_len, c->vector_buffer_allocated);
}
world* world_current;
void world_logic() {
if (!world_current) {
world_current = world_new();
world_current->pivot.x = (int)camX - (int)camX % CHUNK_H;
world_current->pivot.y = (int)camZ - (int)camZ % CHUNK_H;
}
void world_shift(world *w, int shift_i, int shift_j)
{
if (shift_i == 0 && shift_j == 0) return;
chunk* newgrid[RENDER_DISTANCE][RENDER_DISTANCE] = {0};
static_range(i, world_current->chunk)
static_range(j, world_current->chunk[i]) {
int ni = i - shift_i;
int nj = j - shift_j;
if (ni >= 0 && ni < RENDER_DISTANCE &&
nj >= 0 && nj < RENDER_DISTANCE) {
newgrid[ni][nj] = w->chunk[i][j];
} else {
chunk_free(w->chunk[i][j]);
w->chunk[i][j] = NULL;
}
}
memcpy(w->chunk, newgrid, sizeof(newgrid));
// mark all existing chunks as needing vector buffer regen
/*
static_range(i, world_current->chunk)
static_range(j, world_current->chunk[i])
if (w->chunk[i][j]) w->chunk[i][j]->is_vector_buffer_valid = 0;
*/
}
if (world_current) {
int center = (RENDER_DISTANCE * CHUNK_H) / 2;
int dx = camX - (world_current->pivot.x + center);
int dz = camZ - (world_current->pivot.y + center);
int hysteresis = 24;
int shift_i = 0;
int shift_j = 0;
if (dx > hysteresis)
shift_i = (dx - hysteresis) / CHUNK_H + 1;
else if (dx < -hysteresis)
shift_i = (dx + hysteresis) / CHUNK_H - 1;
if (dz > hysteresis)
shift_j = (dz - hysteresis) / CHUNK_H + 1;
else if (dz < -hysteresis)
shift_j = (dz + hysteresis) / CHUNK_H - 1;
if (shift_i > RENDER_DISTANCE) shift_i = RENDER_DISTANCE;
if (shift_i < -RENDER_DISTANCE) shift_i = -RENDER_DISTANCE;
if (shift_j > RENDER_DISTANCE) shift_j = RENDER_DISTANCE;
if (shift_j < -RENDER_DISTANCE) shift_j = -RENDER_DISTANCE;
if (shift_i || shift_j) {
world_shift(world_current, shift_i, shift_j);
world_current->pivot.x += shift_i * CHUNK_H;
world_current->pivot.y += shift_j * CHUNK_H;
}
}
if (world_current) {
static_range(i, world_current->chunk) {
static_range(j, world_current->chunk[i]) {
if (!world_current->chunk[i][j]) {
chunk* newchunk = chunk_new();
chunk_terrain(
newchunk,
world_current->pivot.x + i*CHUNK_H,
world_current->pivot.y + j*CHUNK_H
);
world_current->chunk[i][j] = newchunk;
}
}
}
printf("%d %d\n",world_current->pivot.x, world_current->pivot.y);
}
if (world_current) {
static_range(i, world_current->chunk) {
static_range(j, world_current->chunk[i]) {
if (!world_current->chunk[i][j]->is_vector_buffer_valid) {
/* guarantee every chunk in the world is generated before updating vector arrays */
/* avoid updating vertex arrays on chunks with uninitialized neighbors */
if ((i > 0) && (i < (RENDER_DISTANCE-1)))
if ((j > 0) && (j < (RENDER_DISTANCE-1))) {
chunk_update_vector_array(
world_current,
world_current->chunk[i][j],
i*CHUNK_H + world_current->pivot.x,
j*CHUNK_H + world_current->pivot.y
);
world_current->chunk[i][j]->is_vector_buffer_valid = 1;
}
}
}
}
}
}
/*
GLuint atlas[LIGHT_LEVELS];
*/
GLuint atlas;
typedef struct {
chunk* c;
int p;
int i, j;
} chunk_sorting;
int chunk_dist(const void* a, const void* b) {
const chunk_sorting* A = a;
const chunk_sorting* B = b;
if (A->p < B->p) return -1;
if (A->p > B->p) return 1;
return 0;
}
unsigned tick = 0;
char do_occlusion = 1;
void display(void)
{
tick++;
int start = glutGet(GLUT_ELAPSED_TIME);
if(!atlas) {
/*
if (!atlas[0]) {
char name[64];
for(int i = 0; i < LIGHT_LEVELS; i++) {
sprintf(name, "atlas%d.ppm", i);
printf("loaded atlas %d %s\n", i, name);
atlas[i] = load_ppm_texture(name);
}
*/
atlas = load_ppm_texture("atlas.ppm");
}
world_logic();
float cosPitch = cosf(camPitch * M_PI / 180.0f);
float sinPitch = sinf(camPitch * M_PI / 180.0f);
float cosYaw = cosf(camYaw * M_PI / 180.0f);
float sinYaw = sinf(camYaw * M_PI / 180.0f);
float dirX = cosPitch * sinYaw;
float dirY = sinPitch;
float dirZ = -cosPitch * cosYaw;
glClearColor(
0.0f/255.0f,
0.0f/255.0f,
0.0f/255.0f,
0.0f/255.0f
);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(70.0,640.0/480.0,0.1,1000.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(
camX, camY, camZ,
camX + dirX, camY + dirY, camZ + dirZ,
0.0f, 1.0f, 0.0f
);
/*
glEnable(GL_FOG);
glFogi(GL_FOG_MODE, GL_LINEAR);
GLfloat fogColor[4] = {
50.0f/255.0f,
209.0f/255.0f,
231.0f/255.0f,
255.0f/255.0f
};
glFogi(GL_FOG_END, (RENDER_DISTANCE-2)*CHUNK_H);
glFogfv(GL_FOG_COLOR, fogColor);
glHint(GL_FOG_HINT, GL_NICEST);
*/
glEnableClientState(GL_VERTEX_ARRAY);
/*
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
*/
float dirLen = sqrtf(dirX*dirX + dirZ*dirZ);
int chunk_order_len = 0;
chunk_sorting chunk_order[RENDER_DISTANCE * RENDER_DISTANCE];
if (world_current) {
static_range(i, world_current->chunk) {
static_range(j, world_current->chunk[i]) {
chunk* c = world_current->chunk[i][j];
if (!c) continue;
if (!c->vector_buffer) continue;
if (!c->is_vector_buffer_valid) continue;
chunk_order[chunk_order_len].c = c;
int cx = (camX - (world_current->pivot.x + i*CHUNK_H + CHUNK_H/2));
int cy = (camZ - (world_current->pivot.y + j*CHUNK_H + CHUNK_H/2));
chunk_order[chunk_order_len].p = cx*cx + cy*cy;
chunk_order[chunk_order_len].i = i;
chunk_order[chunk_order_len].j = j;
chunk_order_len++;
}
}
qsort(chunk_order, chunk_order_len, sizeof(chunk_sorting), chunk_dist);
}
int occluded_counter = 0;
if ((tick % 4) == 0) {
glDisable(GL_TEXTURE_2D);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
#define OCCLUSION_RES 128
#define OCCLUSION_SIZE (OCCLUSION_RES * OCCLUSION_RES * 3)
GLint old_viewport[4];
glGetIntegerv(GL_VIEWPORT, old_viewport);
glViewport(0, 0, OCCLUSION_RES, OCCLUSION_RES);
for(int k = 0; k < chunk_order_len; k++) {
int i = chunk_order[k].i;
int j = chunk_order[k].j;
chunk* c = world_current->chunk[i][j];
c->visible = 0; /* make all chunks invisible */
if (!c) continue;
if (!c->vector_buffer) continue;
if (!c->is_vector_buffer_valid) continue;
int cx = (camX - (world_current->pivot.x + i*CHUNK_H + CHUNK_H/2));
int cy = (camZ - (world_current->pivot.y + j*CHUNK_H + CHUNK_H/2));
float c_len = sqrtf((cx * cx) + (cy * cy));
float dot_dir_chunk = (cx / c_len) * (dirX / dirLen) + (cy / c_len) * (dirZ / dirLen);
if (dot_dir_chunk > 0.5) continue;
int num_vertices = c->vector_buffer_len / 8;
GLfloat *buf = c->vector_buffer;
const int stride = 8*sizeof(GLfloat);
glVertexPointer(3, GL_FLOAT, stride, buf);
glTexCoordPointer(2, GL_FLOAT, stride, buf+3);
glColorPointer(3, GL_FLOAT, stride, buf+5);
/* never set render distance to >255 chunks */
/* color given an offset to not break in a black background */
unsigned char g = i;
unsigned char b = j;
glColor3ub(0,g,b);
if (glLockArraysEXT && num_vertices) glLockArraysEXT(0, num_vertices);
glDrawArrays(GL_QUADS, 0, num_vertices);
if (glLockArraysEXT) glUnlockArraysEXT();
}
unsigned char buffer[OCCLUSION_SIZE];
glReadPixels(
0, 0, // x,y
OCCLUSION_RES, OCCLUSION_RES, // width,height
GL_RGB, // format
GL_UNSIGNED_BYTE, // type
buffer
);
GLfloat curr_clear_color[4];
glGetFloatv(GL_COLOR_CLEAR_VALUE, curr_clear_color);
for(int i = 0; i < OCCLUSION_SIZE; i += 3) {
unsigned char r = buffer[i+0];
unsigned char g = buffer[i+1];
unsigned char b = buffer[i+2];
if (g != 0 || b != 0) {
world_current->chunk[g][b]->visible = 1;
}
}
glViewport(
old_viewport[0],
old_viewport[1],
old_viewport[2],
old_viewport[3]
);
}
glDisableClientState(GL_VERTEX_ARRAY);
glClearColor(
50.0f/255.0f,
209.0f/255.0f,
231.0f/255.0f,
255.0f/255.0f
);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glEnableClientState(GL_COLOR_ARRAY);
glEnable(GL_TEXTURE_2D);
/*
glBindTexture(GL_TEXTURE_2D, atlas[1]);
*/
glBindTexture(GL_TEXTURE_2D, atlas);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glColor3f(0, 0, 0);
glEnable(GL_ALPHA_TEST);
glAlphaFunc(GL_GREATER, 0.5f);
if (world_current)
for(int k = 0; k < chunk_order_len; k++) {
int i = chunk_order[k].i;
int j = chunk_order[k].j;
chunk* c = chunk_order[k].c;
if (!c) continue;
if (!c->vector_buffer) continue;
if (!c->is_vector_buffer_valid) continue;
if (!chunk_order[k].c->visible) { occluded_counter++; continue; }
int cx = (camX - (world_current->pivot.x + i*CHUNK_H + CHUNK_H/2));
int cy = (camZ - (world_current->pivot.y + j*CHUNK_H + CHUNK_H/2));
float c_len = sqrtf((cx * cx) + (cy * cy));
float dot_dir_chunk = (cx / c_len) * (dirX / dirLen) + (cy / c_len) * (dirZ / dirLen);
if (dot_dir_chunk > 0.5) continue;
int num_vertices = c->vector_buffer_len / 8;
GLfloat *buf = c->vector_buffer;
const int stride = 8*sizeof(GLfloat);
glVertexPointer(3, GL_FLOAT, stride, buf);
glTexCoordPointer(2, GL_FLOAT, stride, buf+3);
glColorPointer(3, GL_FLOAT, stride, buf+5);
if (glLockArraysEXT && num_vertices) glLockArraysEXT(0, num_vertices);
glDrawArrays(GL_QUADS, 0, num_vertices);
if (glLockArraysEXT) glUnlockArraysEXT();
}
glDisableClientState(GL_COLOR_ARRAY);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
glDisableClientState(GL_VERTEX_ARRAY);
int end = glutGet(GLUT_ELAPSED_TIME);
printf("time: %d ms\n", end - start);
printf("occluded %d chunks\n", occluded_counter);
glutSwapBuffers();
float radYaw = camYaw * M_PI / 180.0f;
float radPitch = camPitch * M_PI / 180.0f;
float forwardX = sinf(radYaw);
float forwardZ = -cosf(radYaw);
if (keys['w']) { camX += forwardX * moveSpeed; camZ += forwardZ * moveSpeed; }
if (keys['s']) { camX -= forwardX * moveSpeed; camZ -= forwardZ * moveSpeed; }
if (keys['d']) { camX -= forwardZ * moveSpeed; camZ += forwardX * moveSpeed; }
if (keys['a']) { camX += forwardZ * moveSpeed; camZ -= forwardX * moveSpeed; }
if (keys[' ']) { camY += 0.1f; }
if (keys['z']) camY -= 0.1f;
}
int windowWidth, windowHeight;
void reshape(int w, int h) {
windowWidth = w;
windowHeight = h;
glViewport(0, 0, w, h);
glScissor(0,0,w,h);
glEnable(GL_SCISSOR_TEST);
glEnable(GL_CULL_FACE);
glCullFace(GL_BACK);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LESS);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-1,1,-1,1,-1,1);
glMatrixMode(GL_MODELVIEW);
}
void timer(int v) {
glutPostRedisplay();
glutTimerFunc(16,timer,0);
}
void keyDown(unsigned char key, int x, int y) {
keys[key] = 1;
if (key == 'q') {
exit(0);
}
if (do_occlusion == 'p') do_occlusion = !do_occlusion;
if (key == 'u') {
static_range(i, world_current->chunk) {
static_range(j, world_current->chunk[i]) {
free(world_current->chunk[i][j]);
world_current->chunk[i][j] = 0;
}
}
world_current->pivot.x+=16;
}
}
void keyUp(unsigned char key, int x, int y) { keys[key] = 0; }
void mouseMotion(int x, int y) {
int centerX = windowWidth / 2;
int centerY = windowHeight / 2;
int dx = x - centerX;
int dy = y - centerY;
if (dx == 0 && dy == 0) return; // ignore warp events
camYaw += dx * mouseSensitivity;
camPitch += -dy * mouseSensitivity;
if (camPitch > 89.0f) camPitch = 89.0f;
if (camPitch < -89.0f) camPitch = -89.0f;
glutWarpPointer(centerX, centerY);
}
int main(int argc, char** argv) {
srand(time(NULL)); // seed random generator
seed = rand(); // random number
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glutInitWindowSize(640,480);
glutInitWindowPosition(100,100);
glutCreateWindow("Opengl Window");
glutSetCursor(GLUT_CURSOR_NONE);
glLockArraysEXT = (void*)glutGetProcAddress("glLockArraysEXT");
glUnlockArraysEXT = (void*)glutGetProcAddress("glUnlockArraysEXT");
glEnable(GL_DEPTH_TEST);
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutKeyboardFunc(keyDown);
glutKeyboardUpFunc(keyUp);
glutPassiveMotionFunc(mouseMotion);
glutTimerFunc(16,timer,0);
glutMainLoop();
}