From 8b30fa4e7ccfe3c9df73b148f19e2c596e54444c Mon Sep 17 00:00:00 2001 From: Riyyi Date: Mon, 12 Oct 2026 18:59:50 +0200 Subject: [PATCH] Basic vertex and index buffer --- src/platform/glfw.odin | 4 +- src/platform/wgpu.odin | 132 +++++++++++++++++++++++++++++++++++++---- 2 files changed, 120 insertions(+), 16 deletions(-) diff --git a/src/platform/glfw.odin b/src/platform/glfw.odin index 0b1947b..40bd73c 100644 --- a/src/platform/glfw.odin +++ b/src/platform/glfw.odin @@ -21,9 +21,7 @@ OS :: struct { // Constructor / destructor os_init :: proc(settings: base.Settings) { - if !glfw.Init() { - panic("[glfw] init failure") - } + assert(cast(bool)glfw.Init(), "[glfw] init failure") // Set window properties glfw.WindowHint(glfw.CLIENT_API, glfw.NO_API) // do not create OpenGL context diff --git a/src/platform/wgpu.odin b/src/platform/wgpu.odin index 2834d30..ee3df9d 100644 --- a/src/platform/wgpu.odin +++ b/src/platform/wgpu.odin @@ -8,6 +8,16 @@ import "wgpu:wgpu" import "sindri:base" +// ----------------------------------------- +// Types + +Vec3 :: [3]f32 + +Vertex :: struct #align (4) { + pos: Vec3, + col: Vec3, +} + // ----------------------------------------- // Variables @@ -28,6 +38,10 @@ state: struct { // ---------------------------------------- capabilities: wgpu.SurfaceCapabilities, vsync: bool, + // ---------------------------------------- + vertex_buffer: wgpu.Buffer, + index_buffer: wgpu.Buffer, + index_count: u32, } // ----------------------------------------- @@ -64,6 +78,9 @@ instance_init :: proc(settings: base.Settings) { } instance_destroy :: proc() { + wgpu.BufferRelease(state.vertex_buffer) + wgpu.BufferRelease(state.index_buffer) + wgpu.RenderPipelineRelease(state.pipeline) wgpu.PipelineLayoutRelease(state.pipeline_layout) wgpu.ShaderModuleRelease(state.module) @@ -137,18 +154,36 @@ frame :: proc "c" () { loadOp = .Clear, storeOp = .Store, depthSlice = wgpu.DEPTH_SLICE_UNDEFINED, - clearValue = {0, 1, 0, 1}, + clearValue = {55 / 255.0, 73 / 255.0, 73 / 255.0, 1}, }, }, ) wgpu.RenderPassEncoderSetPipeline(render_pass_encoder, state.pipeline) - wgpu.RenderPassEncoderDraw( + + wgpu.RenderPassEncoderSetVertexBuffer( render_pass_encoder, - vertexCount = 3, - instanceCount = 1, - firstVertex = 0, - firstInstance = 0, + slot = 0, + buffer = state.vertex_buffer, + offset = 0, // offset into buffer to start from + size = wgpu.WHOLE_SIZE, // x bytes of the buffer to use + ) + + wgpu.RenderPassEncoderSetIndexBuffer( + render_pass_encoder, + buffer = state.index_buffer, + format = .Uint16, // type of each element in the buffer + offset = 0, // offset into buffer to start from + size = wgpu.WHOLE_SIZE, // x bytes of the buffer to use + ) + + wgpu.RenderPassEncoderDrawIndexed( + render_pass_encoder, + state.index_count, + instanceCount = 1, // draw x instance(s) of the mesh + firstIndex = 0, // offset into buffer to start from, useful for drawing sub-mesh + baseVertex = 0, // value added to index before vertex fetch, useful for multi-mesh vertex buffers + firstInstance = 0, // number to start instance index from ) wgpu.RenderPassEncoderEnd(render_pass_encoder) @@ -208,17 +243,31 @@ on_device :: proc "c" ( } wgpu.SurfaceConfigure(state.surface, &state.config) + // ---------------------------------------- + // Compile shader + shader :: ` + struct VertexInput { + @location(0) pos: vec3, + @location(1) col: vec3, + }; + + struct VertexOutput { + @builtin(position) position: vec4, + @location(0) col: vec3, + }; + @vertex - fn vs_main(@builtin(vertex_index) in_vertex_index: u32) -> @builtin(position) vec4 { - let x = f32(i32(in_vertex_index) - 1); - let y = f32(i32(in_vertex_index & 1u) * 2 - 1); - return vec4(x, y, 0.0, 1.0); + fn vs_main(in: VertexInput) -> VertexOutput { + var out: VertexOutput; + out.position = vec4(in.pos, 1.0); + out.col = in.col; + return out; } @fragment - fn fs_main() -> @location(0) vec4 { - return vec4(1.0, 0.0, 0.0, 1.0); + fn fs_main(in: VertexOutput) -> @location(0) vec4 { + return vec4(in.col, 1.0); }` state.module = wgpu.DeviceCreateShaderModule( @@ -231,12 +280,46 @@ on_device :: proc "c" ( }, ) + // ---------------------------------------- + // Buffer shape + + // Fill vertex/index buffers + fill_buffers_with_quad() + + vertex_attributes := [?]wgpu.VertexAttribute { + { + format = .Float32x3, + offset = cast(u64)offset_of(Vertex, pos), + shaderLocation = 0, + }, + { + format = .Float32x3, + offset = cast(u64)offset_of(Vertex, col), + shaderLocation = 1, + }, + } + + vertex_layout := wgpu.VertexBufferLayout { + stepMode = .Vertex, + arrayStride = size_of(Vertex), + attributeCount = len(vertex_attributes), + attributes = raw_data(vertex_attributes[:]), + } + + // ---------------------------------------- + // Render pipeline + state.pipeline_layout = wgpu.DeviceCreatePipelineLayout(state.device, &{}) state.pipeline = wgpu.DeviceCreateRenderPipeline( state.device, &{ layout = state.pipeline_layout, - vertex = {module = state.module, entryPoint = "vs_main"}, + vertex = { + module = state.module, + entryPoint = "vs_main", + bufferCount = 1, + buffers = &vertex_layout, + }, fragment = &{ module = state.module, entryPoint = "fs_main", @@ -290,3 +373,26 @@ pick_present_mode :: proc(vsync: bool) -> wgpu.PresentMode { return .Fifo // Vsync On } + +@(private = "file") +fill_buffers_with_quad :: proc() { + vertices := [?]Vertex { + {pos = {-0.5, -0.5, 0}, col = {1, 0, 0}}, // 0 bottom-left red + {pos = {+0.5, -0.5, 0}, col = {0, 1, 0}}, // 1 bottom-right green + {pos = {+0.5, +0.5, 0}, col = {0, 0, 1}}, // 2 top-right blue + {pos = {-0.5, +0.5, 0}, col = {1, 1, 0}}, // 3 top-left yellow + } + indices := [?]u16{0, 1, 2, 2, 3, 0} + + state.vertex_buffer = wgpu.DeviceCreateBufferWithData( + state.device, + &{label = "quad vertices", usage = {.Vertex}}, + vertices[:], + ) + state.index_buffer = wgpu.DeviceCreateBufferWithData( + state.device, + &{label = "quad indices", usage = {.Index}}, + indices[:], + ) + state.index_count = cast(u32)len(indices) +}