Mercurial > games > semicongine
diff tests/hello_triangle.nim @ 1443:768bf1a8407b default tip main
add: hello-triangle example, did: update according readme parts
author | sam <sam@basx.dev> |
---|---|
date | Mon, 24 Feb 2025 10:09:41 +0700 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tests/hello_triangle.nim Mon Feb 24 10:09:41 2025 +0700 @@ -0,0 +1,89 @@ +import ../semicongine +import ../semicongine/rendering +import ../semicongine/input + +# required +initEngine("Hello triangle") + +# set up a simple render pass to render the displayed frame +var renderpass = createDirectPresentationRenderPass( + depthBuffer = false, samples = VK_SAMPLE_COUNT_1_BIT +) + +# the swapchain, needs to be attached to the main renderpass +setupSwapchain(renderpass = renderpass) + +# render data is used for memory management on the GPU +var renderdata = initRenderData() + +type + # define a push constant, to have something moving + PushConstant = object + scale: float32 + + # This is how we define shaders: the interface needs to be "typed" + # but the shader code itself can freely be written in glsl + Shader = object + position {.VertexAttribute.}: Vec3f + color {.VertexAttribute.}: Vec3f + pushConstant {.PushConstant.}: PushConstant + fragmentColor {.Pass.}: Vec3f + outColor {.ShaderOutput.}: Vec4f + # code + vertexCode: string = + """void main() { + fragmentColor = color; + gl_Position = vec4(position * pushConstant.scale, 1);}""" + fragmentCode: string = + """void main() { + outColor = vec4(fragmentColor, 1);}""" + + # And we also need to define our Mesh, which does describe the vertex layout + TriangleMesh = object + position: GPUArray[Vec3f, VertexBuffer] + color: GPUArray[Vec3f, VertexBuffer] + +# instantiate the mesh and fill with data +var mesh = TriangleMesh( + position: asGPUArray([vec3(-0.5, -0.5), vec3(0, 0.5), vec3(0.5, -0.5)], VertexBuffer), + color: asGPUArray([vec3(0, 0, 1), vec3(0, 1, 0), vec3(1, 0, 0)], VertexBuffer), +) + +# this allocates GPU data, uploads the data to the GPU and flushes any thing that is host-cached +# this is a shortcut version, more fine-grained control is possible +assignBuffers(renderdata, mesh) +renderdata.flushAllMemory() + +# Now we need to instantiate the shader as a pipeline object that is attached to a renderpass +var pipeline = createPipeline(Shader(), renderPass = renderPass) + +# the main render-loop will exit if we get a kill-signal from the OS +while updateInputs(): + # starts the drawing for the next frame and provides us necesseary framebuffer and commandbuffer objects in this scope + withNextFrame(framebuffer, commandbuffer): + # start the main (and only) renderpass we have, needs to know the target framebuffer and a commandbuffer + withRenderPass( + renderPass, + framebuffer, + commandbuffer, + frameWidth(), + frameHeight(), + vec4(0, 0, 0, 0), + ): + # now activate our shader-pipeline + withPipeline(commandbuffer, pipeline): + # and finally, draw the mesh and set a single parameter + # more complicated setups with descriptors/uniforms are of course possible + renderWithPushConstant( + commandbuffer = commandbuffer, + pipeline = pipeline, + mesh = mesh, + pushConstant = PushConstant(scale: 0.3), + ) + +# cleanup +checkVkResult vkDeviceWaitIdle(engine().vulkan.device) +destroyPipeline(pipeline) +destroyRenderData(renderdata) +destroyRenderPass(renderpass) +destroyVulkan()