*.code-workspace
*.sublime-project
*.sublime-workspace
-.vscode/c_cpp_properties.json
\ No newline at end of file
+.vscode/c_cpp_properties.json
+*.wasm
-onyx run build -I ../src
\ No newline at end of file
+onyx build -I ../src -o game.wasm && onyxrun game.wasm
use package core
+use package opengles
Block :: #distinct u32;
#operator == macro (b1, b2: Block) => cast(u32) b1 == cast(u32) b2;
}
+// Provides x, y, z, and block in the body
chunk_foreach :: macro (chunk: ^Chunk, body: Code) {
for x: Chunk_Size do for y: Chunk_Size do for z: Chunk_Size {
block := chunk.blocks[x * Chunk_Size * Chunk_Size + y * Chunk_Size + z];
#insert body;
}
}
+
+block_highlight: ^Mesh(Chunk_Vertex);
+chunk_highlight_block :: (x, y, z: f32) {
+ data := 0xf000;
+
+ vertex_data := cast(^Chunk_Vertex) alloca(sizeof Chunk_Vertex * 8);
+ vertex_data[0] = .{.{x-0.001,y-0.001,z-0.001},data};
+ vertex_data[1] = .{.{x-0.001,y+1.001,z-0.001},data};
+ vertex_data[2] = .{.{x+1.001,y+1.001,z-0.001},data};
+ vertex_data[3] = .{.{x+1.001,y-0.001,z-0.001},data};
+ vertex_data[4] = .{.{x-0.001,y-0.001,z+1.001},data};
+ vertex_data[5] = .{.{x-0.001,y+1.001,z+1.001},data};
+ vertex_data[6] = .{.{x+1.001,y+1.001,z+1.001},data};
+ vertex_data[7] = .{.{x+1.001,y-0.001,z+1.001},data};
+
+ if block_highlight == null {
+ #persist index_data := u32.[
+ 2, 1, 1, 0, // back
+ 0, 3, 3, 2,
+
+ 6, 2, 2, 3, // right side
+ 3, 7, 7, 6,
+
+ 0, 1, 1, 5, // left side
+ 5, 4, 4, 0,
+
+ 4, 5, 5, 6, // front
+ 6, 7, 7, 4,
+
+ 5, 1, 1, 2, // top
+ 2, 6, 6, 5,
+
+ 0, 4, 4, 7, // bottom
+ 7, 3, 3, 0,
+ ];
+
+ mesh := mesh_make(vertex_data[0..8], index_data);
+ mesh.primitive = GL_LINES;
+ block_highlight = mesh;
+
+ } else {
+ mesh_update_verticies(block_highlight, vertex_data[0 .. 8]);
+ }
+
+ update_model_matrix(.{0,0,0});
+ mesh_draw(block_highlight);
+}
+
+
world: ^World;
world_shader: Shader;
font: Font;
+selected_block: Vector3i;
setup_opengl :: () {
glInit(glfwGetLoadProcAddress());
camera.position -= Vector3.norm(Vector3.cross(facing, .{0,1,0})) * speed;
}
+ dir: Vector3i;
+ if !world_ray_cast(.{ camera.position, facing }, 40, null, (_, p) => {
+ return world_get_block(world, p.x, p.y, p.z) != Block_Empty;
+ }, ^selected_block, ^dir) {
+ selected_block = .{0,0,0};
+ }
+ if glfwGetKey(window, GLFW_KEY_SPACE) == GLFW_PRESS {
+ world_set_block(world, selected_block.x, selected_block.y, selected_block.z, Block_Empty);
+ }
+
{
#persist last_debug_key := 0;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
shader_use(world_shader);
+ glLineWidth(2);
world_draw(world);
+ chunk_highlight_block(~~selected_block.x, ~~selected_block.y, ~~selected_block.z);
if debug_screen {
+ ww, wh: i32;
+ glfwGetWindowSize(window, ^ww, ^wh);
+ font_print(font, ~~(ww / 2), ~~(wh / 2), "X");
+
font_print(font, 0, 32, "FPS: {}", game_fps);
font_print(font, 0, 64, "Position: {}", camera.position);
font_print(font, 0, 96, "Facing: {}", camera.y_rot * 180 / math.PI);
+ font_print(font, 0, 128, "Looking at: {}", selected_block);
}
glfwSwapBuffers(window);
vertex_count: u32;
index_count: u32;
+ primitive: GLuint = GL_TRIANGLES;
}
mesh_make :: (verticies: [] $T, indicies: [] u32) -> ^Mesh(T) {
return mesh;
}
+mesh_update_verticies :: (use mesh: ^Mesh($T), verticies: [] T) {
+ glBindBuffer(GL_ARRAY_BUFFER, vertex_handle);
+ glBufferSubData(GL_ARRAY_BUFFER, 0, verticies.count * sizeof T, verticies.data);
+ glBindBuffer(GL_ARRAY_BUFFER, -1);
+}
+
mesh_draw :: (use mesh: ^Mesh($T)) {
glBindVertexArray(handle);
if index_count > 0 {
- glDrawElements(GL_TRIANGLES, index_count, GL_UNSIGNED_INT, ~~0);
+ glDrawElements(primitive, index_count, GL_UNSIGNED_INT, ~~0);
} else {
- glDrawArrays(GL_TRIANGLES, 0, vertex_count);
+ glDrawArrays(primitive, 0, vertex_count);
}
glBindVertexArray(-1);
}
-#if false {
-// Old code for making a single cube
-mesh_make_cube :: (x, y, z: f32) -> ^Mesh {
- data := random.between(0x0000, 0xffff);
-
- vertex_data := memory.make_slice(Vertex, 8);
- vertex_data[0] = .{x+0,y+0,z+0,data};
- vertex_data[1] = .{x+0,y+1,z+0,data};
- vertex_data[2] = .{x+1,y+1,z+0,data};
- vertex_data[3] = .{x+1,y+0,z+0,data};
- vertex_data[4] = .{x+0,y+0,z+1,data};
- vertex_data[5] = .{x+0,y+1,z+1,data};
- vertex_data[6] = .{x+1,y+1,z+1,data};
- vertex_data[7] = .{x+1,y+0,z+1,data};
-
- #persist index_data := u32.[
- 0, 2, 1, // back
- 0, 3, 2,
-
- 3, 6, 2, // right side
- 3, 7, 6,
-
- 5, 0, 1, // left side
- 5, 4, 0,
-
- 4, 5, 6, // front
- 4, 6, 7,
-
- 5, 1, 2, // top
- 5, 2, 6,
-
- 0, 4, 7, // bottom
- 0, 7, 3,
- ];
-
- return mesh_make(vertex_data, index_data);
-}
-}
\ No newline at end of file
Vector3 :: struct {
x, y, z: f32;
+ mag :: macro (v: Vector3) => math.sqrt(v.x * v.x + v.y * v.y + v.z * v.z);
+
neg :: macro (v: Vector3) => Vector3.{ -v.x, -v.y, -v.z };
dot :: macro (v1, v2: Vector3) -> f32 {
return world;
}
+world_get_block :: (world: ^World, x, y, z: i32) -> Block {
+ @WorldCenter
+ cx := x / Chunk_Size;
+ cy := y / Chunk_Size;
+ cz := z / Chunk_Size;
+ if cx < 0 || cx >= world.chunk_dist do return Block_Empty;
+ if cy < 0 || cy >= world.chunk_dist do return Block_Empty;
+ if cz < 0 || cz >= world.chunk_dist do return Block_Empty;
+ chunk := world.chunks[cx * world.chunk_dist * world.chunk_dist + cy * world.chunk_dist + cz];
+ return chunk_get(chunk, x % Chunk_Size, y % Chunk_Size, z % Chunk_Size);
+}
+
+world_set_block :: (world: ^World, x, y, z: i32, block: Block) {
+ @WorldCenter
+ cx := x / Chunk_Size;
+ cy := y / Chunk_Size;
+ cz := z / Chunk_Size;
+ if cx < 0 || cx >= world.chunk_dist do return;
+ if cy < 0 || cy >= world.chunk_dist do return;
+ if cz < 0 || cz >= world.chunk_dist do return;
+ chunk := world.chunks[cx * world.chunk_dist * world.chunk_dist + cy * world.chunk_dist + cz];
+ chunk_set(chunk, x % Chunk_Size, y % Chunk_Size, z % Chunk_Size, block);
+}
+
+Ray :: struct {
+ origin, direction: Vector3;
+}
+
+world_ray_cast :: (ray: Ray, max_distance: f32, ctx: $T, is_solid: (T, Vector3i) -> bool, out_block: ^Vector3i, out_dir: ^Vector3i) -> bool {
+ pos := Vector3i.{ ~~math.floor(ray.origin.x), ~~math.floor(ray.origin.y), ~~math.floor(ray.origin.z) };
+
+ dir := ray.direction;
+ step := Vector3i.{ ~~math.sign(dir.x), ~~math.sign(dir.y), ~~math.sign(dir.z) };
+
+ tmax: Vector3;
+ tmax.x = ((math.ceil(ray.origin.x) - ray.origin.x) if dir.x > 0 else (ray.origin.x - math.floor(ray.origin.x))) / math.abs(dir.x);
+ tmax.y = ((math.ceil(ray.origin.y) - ray.origin.y) if dir.y > 0 else (ray.origin.y - math.floor(ray.origin.y))) / math.abs(dir.y);
+ tmax.z = ((math.ceil(ray.origin.z) - ray.origin.z) if dir.z > 0 else (ray.origin.z - math.floor(ray.origin.z))) / math.abs(dir.z);
+
+ tdelta := Vector3.{ ~~step.x / dir.x, ~~step.y / dir.y, ~~step.z / dir.z };
+ radius := max_distance / Vector3.mag(dir);
+
+ while true {
+ if is_solid(ctx, pos) {
+ *out_block = pos;
+ return true;
+ }
+
+ if tmax.x < tmax.y {
+ if tmax.x < tmax.z {
+ if tmax.x > radius {
+ break;
+ }
+
+ pos.x += step.x;
+ tmax.x += tdelta.x;
+ *out_dir = Vector3i.{ -step.x, 0, 0 };
+
+ } else {
+ if tmax.z > radius {
+ break;
+ }
+
+ pos.z += step.z;
+ tmax.z += tdelta.z;
+ *out_dir = Vector3i.{ 0, 0, -step.z };
+ }
+
+ } else {
+ if tmax.y < tmax.z {
+ if tmax.y > radius {
+ break;
+ }
+
+ pos.y += step.y;
+ tmax.y += tdelta.y;
+ *out_dir = Vector3i.{ 0, -step.y, 0 };
+
+ } else {
+ if tmax.z > radius {
+ break;
+ }
+
+ pos.z += step.z;
+ tmax.z += tdelta.z;
+ *out_dir = Vector3i.{ 0, 0, -step.z };
+ }
+ }
+ }
+
+ return false;
+}
+
+
world_draw :: (use world: ^World) {
for chunks {
chunk_build_mesh(it);
chunk_draw(it);
}
-}
\ No newline at end of file
+}