basic voxel
There is no browser build of this example yet.
Is it the same as C?
- Desktop:verified The Zig version renders a final frame that is byte-identical to the C example.
- Browser:not checked yet The browser comparison does not cover this example yet.
//! raylib [models] example - basic voxel
//!
//! Zig-API translation of examples/models/models_basic_voxel.c (raylib 6.2).
//! Example licensed under an unmodified zlib/libpng license.
//! Copyright (c) 2025 Tim Little (@timlittle)
const rl = @import("raylib");
const world_size = 8; // Size of our voxel world (8x8x8 cubes)
pub fn main() !void {
// Initialization
const screen_width = 800;
const screen_height = 450;
rl.initWindow(screen_width, screen_height, "raylib [models] example - basic voxel");
defer rl.closeWindow(); // Close window and OpenGL context
rl.disableCursor(); // Lock mouse to window center
// Define the camera to look into our 3d world (first person)
var camera: rl.Camera3D = .{
.position = .{ .x = -2.0, .y = 0.0, .z = -2.0 }, // Camera position at ground level
.target = .{ .x = 0.0, .y = 0.0, .z = 0.0 }, // Camera looking at point
.up = .{ .x = 0.0, .y = 1.0, .z = 0.0 }, // Camera up vector
.fovy = 45.0, // Camera field-of-view Y
.projection = .perspective, // Camera projection type
};
// Create a cube model
const cube_mesh = rl.genMeshCube(1.0, 1.0, 1.0); // Create a unit cube mesh
const cube_model = try rl.loadModelFromMesh(cube_mesh); // Convert mesh to a model
defer rl.unloadModel(cube_model);
cube_model.materials.?[0].map(.diffuse).color = .beige;
// Initialize voxel world - fill with voxels
var voxels: [world_size][world_size][world_size]bool = @splat(@splat(@splat(true)));
rl.setTargetFPS(60);
// Main game loop
while (!rl.windowShouldClose()) { // Detect window close button or ESC key
// Update
rl.updateCamera(&camera, .first_person);
// Handle voxel removal with mouse click
// This method is quite inefficient. Ray marching through the voxel grid using DDA would be faster, but more complex.
if (rl.isMouseButtonPressed(.left)) {
// Cast a ray from the screen center (where crosshair would be)
const screen_center: rl.Vector2 = .{
.x = @as(f32, @floatFromInt(rl.getScreenWidth())) / 2.0,
.y = @as(f32, @floatFromInt(rl.getScreenHeight())) / 2.0,
};
const ray = rl.getScreenToWorldRay(screen_center, camera);
// Check ray collision with all voxels
var closest_distance: f32 = 99999.0;
var closest_voxel: ?*bool = null;
for (&voxels, 0..) |*plane, x| {
for (plane, 0..) |*row, y| {
for (row, 0..) |*voxel, z| {
if (!voxel.*) continue; // Skip empty voxels
// Build a bounding box for this voxel
const position = voxelPosition(x, y, z);
const box: rl.BoundingBox = .{
.min = .{ .x = position.x - 0.5, .y = position.y - 0.5, .z = position.z - 0.5 },
.max = .{ .x = position.x + 0.5, .y = position.y + 0.5, .z = position.z + 0.5 },
};
// Check ray-box collision
const collision = rl.getRayCollisionBox(ray, box);
if (collision.hit and collision.distance < closest_distance) {
closest_distance = collision.distance;
closest_voxel = voxel;
}
}
}
}
// Remove the closest voxel if one was hit
if (closest_voxel) |voxel| voxel.* = false;
}
// Draw
rl.beginDrawing();
defer rl.endDrawing();
rl.clearBackground(.ray_white);
{
rl.beginMode3D(camera);
defer rl.endMode3D();
rl.drawGrid(10, 1.0);
// Draw all voxels
for (voxels, 0..) |plane, x| {
for (plane, 0..) |row, y| {
for (row, 0..) |voxel, z| {
if (!voxel) continue;
const position = voxelPosition(x, y, z);
rl.drawModel(cube_model, position, 1.0, .beige);
rl.drawCubeWires(position, 1.0, 1.0, 1.0, .black);
}
}
}
}
// Draw reference point for raycasting to delete blocks
rl.drawCircle(@divTrunc(rl.getScreenWidth(), 2), @divTrunc(rl.getScreenHeight(), 2), 4, .red);
rl.drawText("Left-click a voxel to remove it!", 10, 10, 20, .dark_gray);
rl.drawText("WASD to move, mouse to look around", 10, 35, 10, .gray);
}
}
fn voxelPosition(x: usize, y: usize, z: usize) rl.Vector3 {
return .{ .x = @floatFromInt(x), .y = @floatFromInt(y), .z = @floatFromInt(z) };
}/*******************************************************************************************
*
* raylib [models] example - basic voxel
*
* Example complexity rating: [★★☆☆] 2/4
*
* Example originally created with raylib 5.5, last time updated with raylib 5.5
*
* Example contributed by Tim Little (@timlittle) and reviewed by Ramon Santamaria (@raysan5)
*
* Example licensed under an unmodified zlib/libpng license, which is an OSI-certified,
* BSD-like license that allows static linking with closed source software
*
* Copyright (c) 2025 Tim Little (@timlittle)
*
********************************************************************************************/
#include "raylib.h"
#include "raymath.h"
#define WORLD_SIZE 8 // Size of our voxel world (8x8x8 cubes)
//------------------------------------------------------------------------------------
// Program main entry point
//------------------------------------------------------------------------------------
int main(void)
{
// Initialization
//--------------------------------------------------------------------------------------
const int screenWidth = 800;
const int screenHeight = 450;
InitWindow(screenWidth, screenHeight, "raylib [models] example - basic voxel");
DisableCursor(); // Lock mouse to window center
// Define the camera to look into our 3d world (first person)
Camera3D camera = { 0 };
camera.position = (Vector3){ -2.0f, 0.0f, -2.0f }; // Camera position at ground level
camera.target = (Vector3){ 0.0f, 0.0f, 0.0f }; // Camera looking at point
camera.up = (Vector3){ 0.0f, 1.0f, 0.0f }; // Camera up vector
camera.fovy = 45.0f; // Camera field-of-view Y
camera.projection = CAMERA_PERSPECTIVE; // Camera projection type
// Create a cube model
Mesh cubeMesh = GenMeshCube(1.0f, 1.0f, 1.0f); // Create a unit cube mesh
Model cubeModel = LoadModelFromMesh(cubeMesh); // Convert mesh to a model
cubeModel.materials[0].maps[MATERIAL_MAP_DIFFUSE].color = BEIGE;
// Initialize voxel world - fill with voxels
bool voxels[WORLD_SIZE][WORLD_SIZE][WORLD_SIZE] = { false };
for (int x = 0; x < WORLD_SIZE; x++)
{
for (int y = 0; y < WORLD_SIZE; y++)
{
for (int z = 0; z < WORLD_SIZE; z++)
{
voxels[x][y][z] = true;
}
}
}
SetTargetFPS(60);
//--------------------------------------------------------------------------------------
// Main game loop
while (!WindowShouldClose()) // Detect window close button or ESC key
{
// Update
//----------------------------------------------------------------------------------
UpdateCamera(&camera, CAMERA_FIRST_PERSON);
// Handle voxel removal with mouse click
// This method is quite inefficient. Ray marching through the voxel grid using DDA would be faster, but more complex.
if (IsMouseButtonPressed(MOUSE_LEFT_BUTTON))
{
// Cast a ray from the screen center (where crosshair would be)
Vector2 screenCenter = { GetScreenWidth()/2.0f, GetScreenHeight()/2.0f };
Ray ray = GetScreenToWorldRay(screenCenter, camera);
// Check ray collision with all voxels
float closestDistance = 99999.0f;
Vector3 closestVoxelPosition = { -1, -1, -1 };
bool voxelFound = false;
for (int x = 0; x < WORLD_SIZE; x++)
{
for (int y = 0; y < WORLD_SIZE; y++)
{
for (int z = 0; z < WORLD_SIZE; z++)
{
if (!voxels[x][y][z]) continue; // Skip empty voxels
// Build a bounding box for this voxel
Vector3 position = { (float)x, (float)y, (float)z };
BoundingBox box = {
(Vector3){ position.x - 0.5f, position.y - 0.5f, position.z - 0.5f },
(Vector3){ position.x + 0.5f, position.y + 0.5f, position.z + 0.5f }
};
// Check ray-box collision
RayCollision collision = GetRayCollisionBox(ray, box);
if (collision.hit && (collision.distance < closestDistance))
{
closestDistance = collision.distance;
closestVoxelPosition = (Vector3){ (float)x, (float)y, (float)z };
voxelFound = true;
}
}
}
}
// Remove the closest voxel if one was hit
if (voxelFound)
{
voxels[(int)closestVoxelPosition.x]
[(int)closestVoxelPosition.y]
[(int)closestVoxelPosition.z] = false;
}
}
//----------------------------------------------------------------------------------
// Draw
//----------------------------------------------------------------------------------
BeginDrawing();
ClearBackground(RAYWHITE);
BeginMode3D(camera);
DrawGrid(10, 1.0f);
// Draw all voxels
for (int x = 0; x < WORLD_SIZE; x++)
{
for (int y = 0; y < WORLD_SIZE; y++)
{
for (int z = 0; z < WORLD_SIZE; z++)
{
if (!voxels[x][y][z]) continue;
Vector3 position = { (float)x, (float)y, (float)z };
DrawModel(cubeModel, position, 1.0f, BEIGE);
DrawCubeWires(position, 1.0f, 1.0f, 1.0f, BLACK);
}
}
}
EndMode3D();
// Draw reference point for raycasting to delete blocks
DrawCircle(GetScreenWidth()/2, GetScreenHeight()/2, 4, RED);
DrawText("Left-click a voxel to remove it!", 10, 10, 20, DARKGRAY);
DrawText("WASD to move, mouse to look around", 10, 35, 10, GRAY);
EndDrawing();
//----------------------------------------------------------------------------------
}
// De-Initialization
//--------------------------------------------------------------------------------------
UnloadModel(cubeModel);
CloseWindow();
//--------------------------------------------------------------------------------------
return 0;
}From raylib 6.2. The original on GitHub.