SBgl 0.1.0
A graphics framework in C99
Loading...
Searching...
No Matches
voxel_main.c File Reference
#include <core/sbl_arena.h>
#include <math.h>
#include <sbgl.h>
#include <sbgl_camera.h>
#include <sbgl_math.h>
#include <sbgl_types.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include "voxel_vert.h"
#include "voxel_frag.h"
#include "stb_perlin.h"
Include dependency graph for voxel_main.c:

Go to the source code of this file.

Data Structures

struct  VoxelPushConstants
 

Macros

#define STB_PERLIN_IMPLEMENTATION
 

Functions

static int calculate_instance_count (int radius)
 
static int update_radius (int current, int delta)
 
static float calculate_far_plane (int radius)
 
static void apply_radius_change (sbgl_Camera *camera, int *radius, int *instance_count, int delta)
 
int main (void)
 

Variables

static const int VOXEL_CHUNK_DIM = 32
 
static const float VOXEL_CHUNK_SIZE = 32.0f
 
static const int VOXEL_WORLD_SIZE = 2048
 
static const float VOXEL_FAR_PLANE_MARGIN = 1.5f
 

Macro Definition Documentation

◆ STB_PERLIN_IMPLEMENTATION

#define STB_PERLIN_IMPLEMENTATION

Definition at line 14 of file voxel_main.c.

Function Documentation

◆ apply_radius_change()

static void apply_radius_change ( sbgl_Camera * camera,
int * radius,
int * instance_count,
int delta )
static

Definition at line 50 of file voxel_main.c.

50 {
51 *radius = update_radius(*radius, delta);
52 *instance_count = calculate_instance_count(*radius);
53 camera->far_plane = calculate_far_plane(*radius);
54 printf(
55 "Render Radius: %d (%d chunks), Far Plane: %.1f\n",
56 *radius,
57 *instance_count,
58 camera->far_plane
59 );
60}
float far_plane
Definition sbgl_camera.h:37
static int update_radius(int current, int delta)
Definition voxel_main.c:37
static float calculate_far_plane(int radius)
Definition voxel_main.c:46
static int calculate_instance_count(int radius)
Definition voxel_main.c:32

◆ calculate_far_plane()

static float calculate_far_plane ( int radius)
static

Definition at line 46 of file voxel_main.c.

46 {
47 return (float)(radius + 1) * VOXEL_CHUNK_SIZE * VOXEL_FAR_PLANE_MARGIN;
48}
static const float VOXEL_FAR_PLANE_MARGIN
Definition voxel_main.c:30
static const float VOXEL_CHUNK_SIZE
Definition voxel_main.c:28

◆ calculate_instance_count()

static int calculate_instance_count ( int radius)
static

Definition at line 32 of file voxel_main.c.

32 {
33 int width = (radius * 2 + 1);
34 return width * width;
35}

◆ main()

int main ( void )

Definition at line 62 of file voxel_main.c.

62 {
63 /* Initialize the graphics context and window. */
64 sbgl_InitResult res = sbgl_Init(1280, 720, "SBgl Procedural Voxels");
65 if (res.error != SBGL_SUCCESS)
66 return 1;
67 sbgl_Context* ctx = res.ctx;
68
69 /* Generating world things */
70 float* height_data = malloc(sizeof(float) * VOXEL_WORLD_SIZE * VOXEL_WORLD_SIZE);
71
72 for (int z = 0; z < VOXEL_WORLD_SIZE; z++) {
73 for (int x = 0; x < VOXEL_WORLD_SIZE; x++) {
74 float noise = 0;
75 float amp = 1.0f;
76 for (int oct = 0; oct < 6; oct++) {
77 // To make the noise periodic at VOXEL_WORLD_SIZE=2048,
78 // the period in the noise coordinate space must be a power of two.
79 // A base period of 16 units is selected for the first octave.
80 // 16.0 / 2048.0 = 0.0078125
81 float base_freq = 16.0f / (float)VOXEL_WORLD_SIZE;
82 float oct_freq = base_freq * (float)(1 << oct);
83
84 int wrap = 16 << oct;
85 if (wrap > 256) {
86 wrap = 256;
87 }
88 // Use consistent wrapping across octaves to match the 2048x2048 world size.
89 // stb_perlin_noise3 wraps internally at 256. For a 2048 world,
90 // we ensure the noise period divides 2048 evenly.
91
92 // int wrap = 256;
93
94 noise += stb_perlin_noise3(
95 (float)x * oct_freq,
96 (float)z * oct_freq,
97 0,
98 wrap,
99 wrap,
100 wrap
101 ) *
102 amp;
103 amp *= 0.5f;
104 }
105 height_data[z * VOXEL_WORLD_SIZE + x] = floorf((noise + 1.0f) * 0.5f * 64.0f);
106 }
107 }
108
109 sbgl_Buffer height_ssbo = sbgl_CreateBuffer(
110 ctx,
112 sizeof(float) * VOXEL_WORLD_SIZE * VOXEL_WORLD_SIZE,
113 height_data
114 );
115 free(height_data);
116
117 /* Initialize the memory arena. */
118 SblArena arena;
119 sbl_arena_init(&arena, 32 * 1024 * 1024);
120
121 /* Load the procedural voxel shaders. */
122 sbgl_Shader vert =
123 sbgl_LoadShader(ctx, SBGL_SHADER_STAGE_VERTEX, (const uint32_t*)voxel_vert_spv, voxel_vert_spv_len);
124 sbgl_Shader frag =
125 sbgl_LoadShader(ctx, SBGL_SHADER_STAGE_FRAGMENT, (const uint32_t*)voxel_frag_spv, voxel_frag_spv_len);
126
127 /* Create a dummy VBO. Pure procedural mode does not require one, but binding ensures safety. */
128 sbgl_Vertex dummy_vert = { { 0, 0, 0, 0 }, 0, 0 };
129 sbgl_Buffer vbo =
130 sbgl_CreateBuffer(ctx, SBGL_BUFFER_USAGE_VERTEX, sizeof(dummy_vert), &dummy_vert);
131
132 /* Create a linear index buffer (0, 1, 2...) for procedural generation. */
133 const uint32_t PRO_INDEX_COUNT = VOXEL_CHUNK_DIM * VOXEL_CHUNK_DIM * 36;
134 uint32_t* pro_indices = malloc(sizeof(uint32_t) * PRO_INDEX_COUNT);
135 for (uint32_t i = 0; i < PRO_INDEX_COUNT; i++)
136 pro_indices[i] = i;
138 ctx,
140 sizeof(uint32_t) * PRO_INDEX_COUNT,
141 pro_indices
142 );
143 free(pro_indices);
144
145 /* Empty vertex layout for procedural rendering. */
146 sbgl_PipelineConfig pipe_cfg = { .vertexShader = vert,
147 .fragmentShader = frag,
148 .vertexLayout = { sizeof(sbgl_Vertex), 0, NULL } };
149 sbgl_Pipeline pipeline = sbgl_CreatePipeline(ctx, &pipe_cfg);
150 sbgl_RenderQueue* queue = sbgl_CreateRenderQueue(ctx, &arena);
151
152 /* Render Distance Configuration. */
153 int radius = 5;
154 int instance_count = calculate_instance_count(radius);
155 float initial_far = calculate_far_plane(radius);
156
157 sbgl_Camera camera =
158 sbgl_CameraPerspective(SBGL_PI / 4.0f, 1280.0f / 720.0f, 0.1f, initial_far);
159 camera.position = sbgl_Vec3Set(0.0f, 40.0f, 100.0f);
160
161 float pitch = -0.4f, yaw = -SBGL_PI / 2.0f;
162 bool mouse_locked = false;
163 float move_speed = 100.0f, mouse_sensitivity = 0.005f;
164 double start_time = sbgl_GetTime(ctx), last_time = start_time;
165 float fps_timer = 0.0f;
166 int frame_count = 0;
167
168 printf("--- Voxel Controls ---\n");
169 printf("W/A/S/D: Move\n");
170 printf("Q/E: Vertical Move\n");
171 printf("TAB: Lock/Unlock Mouse\n");
172 printf("+/-: Change Render Distance\n");
173 printf("ESC: Exit\n");
174 printf("----------------------\n");
175
176 while (!sbgl_WindowShouldClose(ctx)) {
177 sbgl_Clear(ctx, 0.5f, 0.7f, 1.0f, 1.0f);
179
180 const sbgl_InputState* input = sbgl_GetInputState(ctx);
181 if (input->keysDown[SBGL_KEY_ESCAPE])
182 break;
183
184 double current_time = sbgl_GetTime(ctx);
185 float dt = (float)(current_time - last_time);
186 last_time = current_time;
187
188 frame_count++;
189 fps_timer += dt;
190 if (fps_timer >= 1.0f) {
192 printf(
193 "FPS: %d | CPU: %.2fms (Sort: %.2fms) | GPU: %.2fms\n",
194 frame_count,
195 tel.cpu_frame_time,
196 tel.cpu_sort_time,
198 );
199 fps_timer = 0.0f;
200 frame_count = 0;
201 }
202
203 if (input->keysPressed[SBGL_KEY_TAB]) {
204 mouse_locked = !mouse_locked;
206 ctx,
208 );
209 }
210
211 // Radius control using +/- keys
212 if (input->keysPressed[SBGL_KEY_EQUAL]) {
213 apply_radius_change(&camera, &radius, &instance_count, 1);
214 }
215 if (input->keysPressed[SBGL_KEY_MINUS]) {
216 apply_radius_change(&camera, &radius, &instance_count, -1);
217 }
218
219 if (mouse_locked) {
220 yaw += (float)input->mouseDeltaX * mouse_sensitivity;
221 pitch -= (float)input->mouseDeltaY * mouse_sensitivity;
222 if (pitch > 1.5f)
223 pitch = 1.5f;
224 if (pitch < -1.5f)
225 pitch = -1.5f;
226 }
227
229 sbgl_Vec3Set(cosf(yaw) * cosf(pitch), sinf(pitch), sinf(yaw) * cosf(pitch))
230 );
232 float velocity = move_speed * dt;
233
234 if (input->keysDown[SBGL_KEY_W])
235 camera.position = sbgl_Vec3Add(camera.position, sbgl_Vec3Mul(front, velocity));
236 if (input->keysDown[SBGL_KEY_S])
237 camera.position = sbgl_Vec3Sub(camera.position, sbgl_Vec3Mul(front, velocity));
238 if (input->keysDown[SBGL_KEY_A])
239 camera.position = sbgl_Vec3Sub(camera.position, sbgl_Vec3Mul(right, velocity));
240 if (input->keysDown[SBGL_KEY_D])
241 camera.position = sbgl_Vec3Add(camera.position, sbgl_Vec3Mul(right, velocity));
242 if (input->keysDown[SBGL_KEY_Q])
243 camera.position =
244 sbgl_Vec3Sub(camera.position, sbgl_Vec3Mul(sbgl_Vec3Set(0, 1, 0), velocity));
245 if (input->keysDown[SBGL_KEY_E])
246 camera.position =
247 sbgl_Vec3Add(camera.position, sbgl_Vec3Mul(sbgl_Vec3Set(0, 1, 0), velocity));
248
249 camera.target = sbgl_Vec3Add(camera.position, front);
250 camera.up = sbgl_Vec3Normalize(sbgl_Vec3Cross(right, front));
251
252 /* Submit chunk instances.
253 Every instance contains camera chunk metadata and radius in its transform matrix. */
254 int camChunkX = (int)floorf(camera.position.x / VOXEL_CHUNK_SIZE);
255 int camChunkZ = (int)floorf(camera.position.z / VOXEL_CHUNK_SIZE);
256
257 for (int i = 0; i < instance_count; i++) {
258 sbgl_InstanceData data = { 0 };
259 // Pass camera chunk metadata and radius in every instance.
260 // This ensures the shader can retrieve it using gl_BaseInstanceARB.
261 data.transform.m[0][0] = (float)camChunkX;
262 data.transform.m[0][1] = (float)camChunkZ;
263 data.transform.m[0][2] = (float)radius;
264
265 sbgl_SubmitDraw(queue, 3, 0, 0, 0, 0, 0, &data);
266 }
267
268 VoxelPushConstants pc = {
269 .viewProj =
271 .heightAddress = sbgl_GetBufferDeviceAddress(ctx, height_ssbo)
272 };
273
274 sbgl_BindPipeline(ctx, pipeline);
277 sbgl_RenderQueuesEx(ctx, &queue, 1, &pc.viewProj, pc.heightAddress);
278
279 sbgl_EndDrawing(ctx);
280 }
281
283 sbgl_DestroyBuffer(ctx, height_ssbo);
284 sbgl_Shutdown(ctx);
285 return 0;
286}
@ SBGL_MOUSE_MODE_NORMAL
Definition sbgl_input.h:154
@ SBGL_MOUSE_MODE_CAPTURED
Definition sbgl_input.h:156
#define SBGL_KEY_A
Definition sbgl.h:42
void sbgl_RenderQueuesEx(sbgl_Context *ctx, sbgl_RenderQueue **queues, uint32_t queueCount, const sbgl_Mat4 *viewProj, uint64_t userAddress)
Extended version of sbgl_RenderQueues with user metadata.
Definition sbgl_core.c:817
sbgl_InitResult sbgl_Init(int w, int h, const char *title)
Initializes the engine and opens a window.
Definition sbgl_core.c:193
#define SBGL_KEY_E
Definition sbgl.h:46
void sbgl_SubmitDraw(sbgl_RenderQueue *queue, uint32_t mesh, uint32_t material, uint32_t blendMode, uint32_t sidedness, uint32_t tags, sbgl_SortKey key, const sbgl_InstanceData *data)
Appends a draw command to the render queue.
Definition sbgl_core.c:772
sbgl_Telemetry sbgl_GetTelemetry(sbgl_Context *ctx)
Retrieves the performance telemetry data for the previous frame.
Definition sbgl_core.c:436
bool sbgl_WindowShouldClose(sbgl_Context *ctx)
Checks if the user or OS has requested to close the window.
Definition sbgl_core.c:238
const sbgl_InputState * sbgl_GetInputState(sbgl_Context *ctx)
Retrieves the input state for the current frame.
Definition sbgl_core.c:413
sbgl_Pipeline sbgl_CreatePipeline(sbgl_Context *ctx, const sbgl_PipelineConfig *config)
Creates a graphics pipeline.
Definition sbgl_core.c:587
#define SBGL_KEY_EQUAL
Definition sbgl.h:81
#define SBGL_KEY_S
Definition sbgl.h:60
sbgl_Shader sbgl_LoadShader(sbgl_Context *ctx, sbgl_ShaderStage stage, const uint32_t *bytecode, size_t size)
Loads a shader from SPIR-V bytecode.
Definition sbgl_core.c:523
void sbgl_SetMouseMode(sbgl_Context *ctx, sbgl_MouseMode mode)
Sets the cursor behavior and visibility.
Definition sbgl_core.c:422
void sbgl_EndDrawing(sbgl_Context *ctx)
Finalizes the current frame and presents it to the screen.
Definition sbgl_core.c:315
#define SBGL_KEY_ESCAPE
Definition sbgl.h:84
#define SBGL_KEY_D
Definition sbgl.h:45
void sbgl_BindPipeline(sbgl_Context *ctx, sbgl_Pipeline pipeline)
Binds a graphics pipeline for subsequent draw calls.
Definition sbgl_core.c:673
#define SBGL_KEY_Q
Definition sbgl.h:58
#define sbgl_Clear
Backward compatibility alias for sbgl_SetClearColor.
Definition sbgl.h:229
void sbgl_DestroyBuffer(sbgl_Context *ctx, sbgl_Buffer buffer)
Destroys a GPU buffer.
Definition sbgl_core.c:457
#define SBGL_KEY_MINUS
Definition sbgl.h:80
#define SBGL_KEY_TAB
Definition sbgl.h:86
sbgl_RenderQueue * sbgl_CreateRenderQueue(sbgl_Context *ctx, struct SblArena *arena)
Creates a thread-local render queue for collecting draw commands.
Definition sbgl_core.c:743
void sbgl_DeviceWaitIdle(sbgl_Context *ctx)
Synchronizes the CPU with the GPU, waiting for all commands to complete.
Definition sbgl_core.c:391
double sbgl_GetTime(sbgl_Context *ctx)
Retrieves the current monotonic system time in seconds.
Definition sbgl_core.c:245
uint64_t sbgl_GetBufferDeviceAddress(sbgl_Context *ctx, sbgl_Buffer buffer)
Retrieves the 64-bit GPU virtual address for a buffer.
Definition sbgl_core.c:689
void sbgl_Shutdown(sbgl_Context *ctx)
Gracefully shuts down the engine and releases all resources.
Definition sbgl_core.c:204
void sbgl_BeginDrawing(sbgl_Context *ctx)
Prepares the engine for a new frame of drawing.
Definition sbgl_core.c:262
void sbgl_BindBuffer(sbgl_Context *ctx, sbgl_Buffer buffer, sbgl_BufferUsage usage)
Binds a buffer to the pipeline.
Definition sbgl_core.c:681
#define SBGL_KEY_W
Definition sbgl.h:64
sbgl_Buffer sbgl_CreateBuffer(sbgl_Context *ctx, sbgl_BufferUsage usage, size_t size, const void *data)
Creates a GPU buffer.
Definition sbgl_core.c:445
static sbgl_Camera sbgl_CameraPerspective(float fov_y_rad, float aspect, float near_p, float far_p)
Initializes a camera with perspective projection.
Definition sbgl_camera.h:86
static sbgl_Mat4 sbgl_CameraGetProjection(const sbgl_Camera *cam)
Computes the projection matrix for the given camera.
static sbgl_Mat4 sbgl_CameraGetView(const sbgl_Camera *cam)
Computes the view matrix for the given camera.
static sbgl_Vec3 sbgl_Vec3Set(float x, float y, float z)
Creates a Vec3, correctly padded.
Definition sbgl_math.h:93
static sbgl_Vec3 sbgl_Vec3Add(sbgl_Vec3 a, sbgl_Vec3 b)
Adds two Vec3 vectors.
Definition sbgl_math.h:132
static sbgl_Vec3 sbgl_Vec3Mul(sbgl_Vec3 a, float s)
Multiplies a Vec3 by a scalar.
Definition sbgl_math.h:142
#define SBGL_PI
Definition sbgl_math.h:22
static sbgl_Mat4 sbgl_Mat4Mul(sbgl_Mat4 a, sbgl_Mat4 b)
Multiplies two matrices.
Definition sbgl_math.h:240
static sbgl_Vec3 sbgl_Vec3Cross(sbgl_Vec3 a, sbgl_Vec3 b)
Computes the cross product of two Vec3 vectors.
Definition sbgl_math.h:152
static sbgl_Vec3 sbgl_Vec3Normalize(sbgl_Vec3 v)
Normalizes a Vec3.
Definition sbgl_math.h:162
static sbgl_Vec3 sbgl_Vec3Sub(sbgl_Vec3 a, sbgl_Vec3 b)
Subtracts b from a.
Definition sbgl_math.h:137
@ SBGL_BUFFER_USAGE_INDEX
Definition sbgl_types.h:124
@ SBGL_BUFFER_USAGE_STORAGE
Definition sbgl_types.h:125
@ SBGL_BUFFER_USAGE_VERTEX
Definition sbgl_types.h:123
@ SBGL_SUCCESS
Definition sbgl_types.h:215
uint32_t sbgl_Buffer
Handle for a GPU-side buffer.
Definition sbgl_types.h:37
uint32_t sbgl_Shader
Handle for a shader module.
Definition sbgl_types.h:42
@ SBGL_SHADER_STAGE_FRAGMENT
Definition sbgl_types.h:135
@ SBGL_SHADER_STAGE_VERTEX
Definition sbgl_types.h:134
uint32_t sbgl_Pipeline
Handle for a graphics pipeline.
Definition sbgl_types.h:47
SBL_ARENA_DEF bool sbl_arena_init(SblArena *arena, uint64_t initial_size)
float stb_perlin_noise3(float x, float y, float z, int x_wrap, int y_wrap, int z_wrap)
Arena allocator.
Definition sbl_arena.h:47
sbgl_Mat4 viewProj
Definition voxel_main.c:21
uint64_t heightAddress
Definition voxel_main.c:23
Stateful camera representation.
Definition sbgl_camera.h:27
sbgl_Vec3 target
Definition sbgl_camera.h:31
sbgl_Vec3 up
Definition sbgl_camera.h:32
sbgl_Vec3 position
Definition sbgl_camera.h:30
Engine context.
Definition sbgl_types.h:268
Result structure for initialization.
Definition sbgl_types.h:339
sbgl_Context * ctx
Definition sbgl_types.h:340
sbgl_Result error
Definition sbgl_types.h:341
Represents the real-time state of physical inputs.
Definition sbgl_input.h:165
bool keysPressed[SBGL_SCANCODE_MAX]
Definition sbgl_input.h:167
bool keysDown[SBGL_SCANCODE_MAX]
Definition sbgl_input.h:166
Per-instance data for automated batching.
Definition sbgl_types.h:19
sbgl_Mat4 transform
Definition sbgl_types.h:20
float m[4][4]
Definition sbgl_math.h:84
Configuration for creating a graphics pipeline.
Definition sbgl_types.h:204
Internal storage for draw packets awaiting submission.
Definition sbgl_core.c:25
Performance telemetry data for a single frame.
Definition sbgl_types.h:175
float gpu_render_time
Definition sbgl_types.h:178
float cpu_sort_time
Definition sbgl_types.h:177
float cpu_frame_time
Definition sbgl_types.h:176
Standard vertex structure for basic geometry rendering. Optimized for cache density (16 bytes).
Definition sbgl_types.h:28
3D Vector, 16-byte aligned and padded for SIMD safety.
Definition sbgl_math.h:49
float z
Definition sbgl_math.h:52
float x
Definition sbgl_math.h:52
static const int VOXEL_WORLD_SIZE
Definition voxel_main.c:29
static const int VOXEL_CHUNK_DIM
Definition voxel_main.c:27
static void apply_radius_change(sbgl_Camera *camera, int *radius, int *instance_count, int delta)
Definition voxel_main.c:50

◆ update_radius()

static int update_radius ( int current,
int delta )
static

Definition at line 37 of file voxel_main.c.

37 {
38 int next = current + delta;
39 if (next < 1)
40 return 1;
41 if (next > 50)
42 return 50;
43 return next;
44}

Variable Documentation

◆ VOXEL_CHUNK_DIM

const int VOXEL_CHUNK_DIM = 32
static

Definition at line 27 of file voxel_main.c.

◆ VOXEL_CHUNK_SIZE

const float VOXEL_CHUNK_SIZE = 32.0f
static

Definition at line 28 of file voxel_main.c.

◆ VOXEL_FAR_PLANE_MARGIN

const float VOXEL_FAR_PLANE_MARGIN = 1.5f
static

Definition at line 30 of file voxel_main.c.

◆ VOXEL_WORLD_SIZE

const int VOXEL_WORLD_SIZE = 2048
static

Definition at line 29 of file voxel_main.c.