diff --git a/game/Makefile b/game/Makefile index 27640d1..5ac32d7 100644 --- a/game/Makefile +++ b/game/Makefile @@ -1,9 +1,9 @@ SRC=src/math_3d.c src/main.c src/mesh.c src/program.c src/resources.c src/camera.c \ - src/chunk/chunk.c src/chunk/noise.c src/chunk/chunkmesh.c src/flags.c + src/chunk/chunk.c src/chunk/noise.c src/chunk/chunkmesh.c src/flags.c src/texture.c OBJ=$(SRC:.c=.o) -CFLAGS=-g -Werror -Wextra -Wall `pkg-config --cflags sdl3 gl` -LDFLAGS=`pkg-config --libs sdl3 gl` -lm +CFLAGS=-Werror -Wextra -Wall `pkg-config --cflags sdl3 sdl3-image gl` +LDFLAGS=`pkg-config --libs sdl3 sdl3-image gl` -lm TARGET=wschod CC=gcc LD=gcc diff --git a/game/res/chunk.frag b/game/res/chunk.frag deleted file mode 100644 index 3818fbe..0000000 --- a/game/res/chunk.frag +++ /dev/null @@ -1,11 +0,0 @@ -#version 100 - -precision lowp float; - -varying vec2 f_uv; -varying float f_height; - -void main () -{ - gl_FragColor = vec4 (f_uv.x, 1.0, f_uv.y, 1.0); -} diff --git a/game/res/shaders/chunk.frag b/game/res/shaders/chunk.frag new file mode 100644 index 0000000..3799d9f --- /dev/null +++ b/game/res/shaders/chunk.frag @@ -0,0 +1,21 @@ +#version 100 + +precision lowp float; + +varying vec2 f_uv; +varying vec3 f_material; +varying float f_height; + +uniform sampler2D materials[5]; + +void main () +{ + /* This means there can be four textures on a terrain. Could be + extended, but who cares? */ + vec4 result = texture2D (materials[0], f_uv); + result = mix (result, texture2D (materials[1], f_uv), f_material[0]); + result = mix (result, texture2D (materials[2], f_uv), f_material[1]); + result = mix (result, texture2D (materials[3], f_uv), f_material[2]); + + gl_FragColor = result * vec4(f_height, f_height, f_height, 1.0); +} diff --git a/game/res/chunk.vert b/game/res/shaders/chunk.vert similarity index 54% rename from game/res/chunk.vert rename to game/res/shaders/chunk.vert index 30886e7..50abf04 100644 --- a/game/res/chunk.vert +++ b/game/res/shaders/chunk.vert @@ -1,17 +1,20 @@ #version 100 -attribute vec3 position; +attribute vec4 position; +attribute vec3 material; uniform mat4 view; uniform mat4 projection; varying vec2 f_uv; +varying vec3 f_material; varying float f_height; void main () { f_uv = position.xy / 8.0; + f_material = material; f_height = position.y; - gl_Position = projection * view * vec4 (position, 1.0); + gl_Position = projection * view * position; } diff --git a/game/res/default.frag b/game/res/shaders/default.frag similarity index 100% rename from game/res/default.frag rename to game/res/shaders/default.frag diff --git a/game/res/default.vert b/game/res/shaders/default.vert similarity index 100% rename from game/res/default.vert rename to game/res/shaders/default.vert diff --git a/game/res/textures/asphalt_gravel.png b/game/res/textures/asphalt_gravel.png new file mode 100644 index 0000000..930b6b0 Binary files /dev/null and b/game/res/textures/asphalt_gravel.png differ diff --git a/game/res/textures/dirt.png b/game/res/textures/dirt.png new file mode 100644 index 0000000..2be0e39 Binary files /dev/null and b/game/res/textures/dirt.png differ diff --git a/game/res/textures/drygrass.png b/game/res/textures/drygrass.png new file mode 100644 index 0000000..d12625d Binary files /dev/null and b/game/res/textures/drygrass.png differ diff --git a/game/res/textures/grass.png b/game/res/textures/grass.png new file mode 100644 index 0000000..1f0c342 Binary files /dev/null and b/game/res/textures/grass.png differ diff --git a/game/shell.nix b/game/shell.nix index 3e2446b..ea8db3a 100644 --- a/game/shell.nix +++ b/game/shell.nix @@ -9,8 +9,10 @@ pkgs.mkShellNoCC { gcc pkg-config sdl3 + sdl3-image gdb cglm glew + valgrind ]; } diff --git a/game/src/chunk/chunk.c b/game/src/chunk/chunk.c index bc5edd6..080e170 100644 --- a/game/src/chunk/chunk.c +++ b/game/src/chunk/chunk.c @@ -1,5 +1,7 @@ #include "chunk.h" +#include + Chunk Chunk_new () { @@ -18,9 +20,11 @@ Chunk_noise (Chunk *chunk, int xoffset, int yoffset) { for (int x = 0; x < CHUNK_DIM; ++x) { - float val = noise2 ((x + xoffset * CHUNK_DIM), (y + yoffset * CHUNK_DIM)) * 8; - chunk->heightmap[INDEX (x, y)] = val; - chunk->materialmap[INDEX (x, y)] = 0; + (void) xoffset; (void) yoffset; + float val = sinf (x) + sinf (y); + (void) val; + chunk->heightmap[INDEX (x, y)] = (val + 0.5); + chunk->materialmap[INDEX (x, y)] = (int) val; } } } diff --git a/game/src/chunk/chunk.h b/game/src/chunk/chunk.h index 4e06635..bea42f6 100644 --- a/game/src/chunk/chunk.h +++ b/game/src/chunk/chunk.h @@ -6,7 +6,7 @@ #define CHUNK_DIM 16 #define CHUNK_SIZE (CHUNK_DIM*CHUNK_DIM) -#define INDEX(x, y) (CHUNK_DIM * y + x) +#define INDEX(x, y) (CHUNK_DIM * (y) + (x)) typedef struct Chunk { diff --git a/game/src/chunk/chunkmesh.c b/game/src/chunk/chunkmesh.c index 83bf1ee..291f9aa 100644 --- a/game/src/chunk/chunkmesh.c +++ b/game/src/chunk/chunkmesh.c @@ -7,31 +7,43 @@ ChunkMesh_from_Chunk_and_neighbors (Chunk *chunk, Chunk *nd, Chunk *nr, Chunk *n /* nd --- down neighbor; nr --- right neighbor; nc --- corner neighbor */ { vec3_t *positions = malloc (sizeof (vec3_t) * CHUNKMESH_SIZE); + vec3_t *materials = malloc (sizeof (vec3_t) * CHUNKMESH_SIZE); unsigned int *indices = malloc (sizeof (unsigned int) * CHUNKMESH_SIZE * 6); + INFO ("Temporarily allocated %zu bytes for ChunkMesh.", + (sizeof (vec3_t) * CHUNKMESH_SIZE * 2 + + sizeof (unsigned int) * CHUNKMESH_SIZE * 6)); + int i_i = 0; for (int y = 0; y < CHUNKMESH_DIM; ++y) { for (int x = 0; x < CHUNKMESH_DIM; ++x) { float height = chunk->heightmap[INDEX (x, y)]; - + float material_id = chunk->materialmap[INDEX (x, y)]; + if (x == CHUNK_DIM && y < CHUNKMESH_DIM && nr != NULL) { height = nr->heightmap[INDEX (0, y)]; + material_id = nr->materialmap[INDEX (0, y)]; } if (y == CHUNK_DIM && x < CHUNKMESH_DIM && nd != NULL) { height = nd->heightmap[INDEX (x, 0)]; + material_id = nr->materialmap[INDEX (x, 0)]; } if (x == CHUNK_DIM && y == CHUNK_DIM && nc != NULL) { height = nc->heightmap[INDEX (0, 0)]; + material_id = nr->materialmap[INDEX (0, 0)]; } positions[CMINDEX (x, y)] = vec3 (x, height, y); + materials[CMINDEX (x, y)] = vec3 + (material_id == 1 ? 1 : 0, material_id == 2 ? 1 : 0, + material_id == 3 ? 1 : 0); if (x < CHUNK_DIM && y < CHUNK_DIM) { @@ -41,11 +53,11 @@ ChunkMesh_from_Chunk_and_neighbors (Chunk *chunk, Chunk *nd, Chunk *nr, Chunk *n int i3 = CMINDEX (x + 1, y + 1); /* - i0----i1 - | | - | | - i2----i3 - */ + i0----i1 + | \ | + | \ | + i2----i3 + */ indices[i_i++] = i0; indices[i_i++] = i2; @@ -59,17 +71,27 @@ ChunkMesh_from_Chunk_and_neighbors (Chunk *chunk, Chunk *nd, Chunk *nr, Chunk *n ChunkMesh mesh; glGenBuffers (1, &mesh.pbo); + glGenBuffers (1, &mesh.mbo); glGenBuffers (1, &mesh.ebo); glBindBuffer (GL_ARRAY_BUFFER, mesh.pbo); glBufferData (GL_ARRAY_BUFFER, sizeof (vec3_t) * CHUNKMESH_SIZE, positions, GL_STATIC_DRAW); glEnableVertexAttribArray (0); glVertexAttribPointer (0, 3, GL_FLOAT, GL_FALSE, sizeof (vec3_t), 0); + + glBindBuffer (GL_ARRAY_BUFFER, mesh.mbo); + glBufferData (GL_ARRAY_BUFFER, sizeof (vec3_t) * CHUNKMESH_SIZE, materials, GL_STATIC_DRAW); + glEnableVertexAttribArray (1); + glVertexAttribPointer (1, 3, GL_FLOAT, GL_FALSE, sizeof (vec3_t), 0); glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, mesh.ebo); glBufferData (GL_ELEMENT_ARRAY_BUFFER, sizeof (unsigned int) * CHUNKMESH_SIZE * 6, indices, GL_STATIC_DRAW); + + glBindBuffer (GL_ARRAY_BUFFER, 0); + glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0); free (positions); + free (materials); free (indices); return mesh; @@ -79,7 +101,7 @@ void ChunkMesh_render (ChunkMesh *mesh) { glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, mesh->ebo); - + if (game_flags.wireframe_enabled) { glDrawElements (GL_LINES, CHUNKMESH_SIZE * 6, GL_UNSIGNED_INT, 0); @@ -88,4 +110,6 @@ ChunkMesh_render (ChunkMesh *mesh) { glDrawElements (GL_TRIANGLES, CHUNKMESH_SIZE * 6, GL_UNSIGNED_INT, 0); } + + glBindBuffer (GL_ELEMENT_ARRAY_BUFFER, 0); } diff --git a/game/src/chunk/chunkmesh.h b/game/src/chunk/chunkmesh.h index 05484c4..110ba02 100644 --- a/game/src/chunk/chunkmesh.h +++ b/game/src/chunk/chunkmesh.h @@ -6,14 +6,14 @@ #include "../math_3d.h" #include "chunk.h" -#define CHUNKMESH_DIM (CHUNK_SIZE+1) +#define CHUNKMESH_DIM (CHUNK_DIM+1) #define CHUNKMESH_SIZE (CHUNKMESH_DIM*CHUNKMESH_DIM) -#define CMINDEX(x, y) (CHUNKMESH_DIM * y + x) +#define CMINDEX(x, y) (CHUNKMESH_DIM * (y) + (x)) typedef struct ChunkMesh { unsigned int pbo; - /* Element Buffer Object, the indices. */ + unsigned int mbo; unsigned int ebo; } ChunkMesh; diff --git a/game/src/chunk/noise.c b/game/src/chunk/noise.c index 583ede3..ab13569 100644 --- a/game/src/chunk/noise.c +++ b/game/src/chunk/noise.c @@ -23,12 +23,12 @@ noise_seed (int seed) float noise2 (float x, float y) { - int X = (int) FASTFLOOR(x) & 0xff; - int Y = (int) FASTFLOOR(y) & 0xff; - x -= FASTFLOOR(x); - y -= FASTFLOOR(y); - float u = FADE(x); - float v = FADE(y); + int X = (int) FASTFLOOR (x) & 0xff; + int Y = (int) FASTFLOOR (y) & 0xff; + x -= FASTFLOOR (x); + y -= FASTFLOOR (y); + float u = FADE (x); + float v = FADE (y); int A = (perm[X] + Y) & 0xff; int B = (perm[X + 1] + Y) & 0xff; diff --git a/game/src/main.c b/game/src/main.c index a01f72a..5ed9db9 100644 --- a/game/src/main.c +++ b/game/src/main.c @@ -66,6 +66,9 @@ SDL_AppInit (void **appstate, int argc, char **argv) glViewport(0, 0, 640, 480); glClearColor (0, 0.3, 0.8, 1); + glEnable (GL_CULL_FACE); + glEnable (GL_DEPTH_TEST); + /* Initialize and load resources. */ Resources_init (); noise_seed (1337); @@ -75,14 +78,12 @@ SDL_AppInit (void **appstate, int argc, char **argv) Chunk_noise (&chunk, 0, 0); test_chunkmesh = ChunkMesh_from_Chunk_and_neighbors (&chunk, NULL, NULL, NULL); - program_use (resources.programs[1]); - program_print (resources.programs[1]); - /* Set up game state. */ game_state.last_time = SDL_GetTicks (); /* Set some basic game flags for testing. */ game_flags.can_free_look = 1; + game_flags.wireframe_enabled = 0; /* Initialize the default camera. */ camera = Camera_default (); @@ -193,12 +194,19 @@ SDL_AppIterate (void *appstate) /* Render stuff. */ - glClear (GL_COLOR_BUFFER_BIT); + glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); mat4_t view = Camera_get_view (&camera); mat4_t projection = Camera_get_projection (&camera, 640.0 / 480.0); program_use (resources.programs[1]); + + for (size_t i = 0; i < resources.textures_len; ++i) + { + glActiveTexture (GL_TEXTURE0 + i); + glBindTexture (GL_TEXTURE_2D, resources.textures[i]); + } + program_set_mat4 (resources.programs[1], "view", view); program_set_mat4 (resources.programs[1], "projection", projection); ChunkMesh_render (&test_chunkmesh); diff --git a/game/src/resources.c b/game/src/resources.c index 5697200..b5f1163 100644 --- a/game/src/resources.c +++ b/game/src/resources.c @@ -6,7 +6,7 @@ void Resources_add_program (const char *v_src, const char *f_src) { resources.programs_len++; - resources.programs = realloc (resources.programs, sizeof (int) * resources.programs_len); + resources.programs = realloc (resources.programs, sizeof (unsigned int) * resources.programs_len); resources.programs[resources.programs_len - 1] = program_create (v_src, f_src); } @@ -47,18 +47,48 @@ Resources_load_program (const char *v_path, const char *f_path) free (f_src); } +void +Resources_add_texture (int width, int height, unsigned char *data) +{ + resources.textures_len++; + resources.textures = realloc (resources.textures, sizeof (unsigned int) * resources.textures_len); + resources.textures[resources.textures_len - 1] = texture_create (width, height, data); +} + +void +Resources_load_texture (const char *path) +{ + SDL_Surface *surface = IMG_Load (path); + + if (surface == NULL) + { + WARN ("Failed to load texture from path %s: %s", path, SDL_GetError ()); + } + + Resources_add_texture (surface->w, surface->h, surface->pixels); + SDL_DestroySurface (surface); +} + void Resources_init () { resources.programs_len = 0; - resources.programs = malloc (sizeof (int)); - Resources_load_program ("res/default.vert", "res/default.frag"); - Resources_load_program ("res/chunk.vert", "res/chunk.frag"); + resources.programs = malloc (sizeof (unsigned int)); + Resources_load_program ("res/shaders/default.vert", "res/shaders/default.frag"); + Resources_load_program ("res/shaders/chunk.vert", "res/shaders/chunk.frag"); + + resources.textures_len = 0; + resources.textures = malloc (sizeof (unsigned int)); + Resources_load_texture ("res/textures/dirt.png"); + Resources_load_texture ("res/textures/drygrass.png"); + Resources_load_texture ("res/textures/grass.png"); + Resources_load_texture ("res/textures/asphalt_gravel.png"); } void Resources_free () { free (resources.programs); + free (resources.textures); } diff --git a/game/src/resources.h b/game/src/resources.h index af80666..a8ea326 100644 --- a/game/src/resources.h +++ b/game/src/resources.h @@ -1,13 +1,23 @@ #pragma once +#include + #include "program.h" +#include "texture.h" typedef struct Resources { size_t programs_len; - int *programs; + unsigned int *programs; + + size_t textures_len; + unsigned int *textures; } Resources; void Resources_add_program (const char *v_src, const char *f_src); void Resources_load_program (const char *v_path, const char *f_path); + +void Resources_add_texture (int width, int height, unsigned char *data); +void Resources_load_texture (const char *path); + void Resources_init (); void Resources_free (); diff --git a/game/src/texture.c b/game/src/texture.c new file mode 100644 index 0000000..09f91df --- /dev/null +++ b/game/src/texture.c @@ -0,0 +1,17 @@ +#include "texture.h" + +unsigned int +texture_create (int width, int height, unsigned char *data) +{ + unsigned int texture; + glGenTextures (1, &texture); + glBindTexture (GL_TEXTURE_2D, texture); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); + glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + + glTexImage2D (GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + glGenerateMipmap (GL_TEXTURE_2D); + glBindTexture (GL_TEXTURE_2D, 0); + + return texture; +} diff --git a/game/src/texture.h b/game/src/texture.h new file mode 100644 index 0000000..587abdd --- /dev/null +++ b/game/src/texture.h @@ -0,0 +1,5 @@ +#pragma once + +#include + +unsigned int texture_create (int width, int height, unsigned char *data);