001 — First Vulkan 1.3 Render: Behind the Cube
What it took to display a simple cube
First working render on InGa — a cube. Looks simple, but here's what's underneath.
What was implemented
- Vulkan logical device creation
- Multi-GPU role system —
eGraphicsandeCompute - Swapchain: 4 images, FIFO mode,
VK_FORMAT_B8G8R8A8_SRGBformat - ImageViews
- Dynamic rendering (Vulkan 1.3 — no render pass, no framebuffers)
- Full graphics pipeline
- Custom memory management — no
std::containers
Main to test it
At the current moment, the code may seem complicate and very verbose.
#include <InGa/CEngine.h>
#include <InGa/core/CWindowManager.h>
#include <InGa/core/log.h>
#include <InGa/core/allocator.h>
#include <InGa/core/containers.h>
I32 main(I32 argc, char** argv)
{
//Configure the engine
Inga::SContextInfo info;
info.logInfo.logLevel = Inga::ELogLevel::eDEBUG;
info.logInfo.logPath = "logs/project0.log";
info.logInfo.logToFile = true;
info.logInfo.logAppend = false;
info.overridePageSize((U32)EArenaGroup::eGFX, 4, INGA_MiB(2));
info.showMemoryStateAtExit = true;
info.gfxInfo.mainWindowWidth = 1280;
info.gfxInfo.mainWindowHeight = 720;
#ifdef INGA_PLATFORM_LINUX
info.gfxInfo.linuxWindowBackend = Inga::ELinuxWindowBackend::eAuto;
#endif
snprintf(info.gfxInfo.appName, APP_NAME_SIZE, "Project O");
//start engine
Inga::CEngine engine(info);
if (!engine.isReady())
{
LINFO("App", "Engine Not ready");
return -1;
}
//here we call the mainWindow created with engine init.
Inga::CWindowManager& wm = engine.getWindowManager();
Inga::CWindow* mainWindow = engine.getMainWindow();
mainWindow->setClearColor(0.08f, 0.08f, 0.12f);
// Renderer SOLID — pipeline built-in, created internaly by InGa, use for debug
engine.setRenderer(mainWindow, Inga::ERenderer::eSolid);
Inga::IRenderer* renderer = engine.getRenderer(mainWindow);
//creating an orbital camera that will focus by default at 0, 0, 0
F32 aspect = (F32)mainWindow->getWidth() / (F32)mainWindow->getHeight();
Inga::CCameraOrbital * cam = engine.createOrbitalCamera(60.0f, aspect, 0.1f, 100.0f);
renderer->setCamera(cam);
//let modify a little bit the camera setup
float pitch = 30.0f;
float yaw = 0.0f;
cam->setPitch(pitch);
cam->setDistance(4.0f);
cam->setYaw(yaw);
// set the rederer in phong shading, to be able to see faces.
//with solid renderer only one colore is used by mesh.
renderer->setRenderMode(Inga::ERenderMode::ePhong);
//solid renderer allows only one light (sun or moon)
renderer->setPhongLightDir(Inga::Vec3(0.577f));
// create builtin geometry box/cercle/sphere/... in this case a Box
U32 boxId = engine.createBox();
// loading an animated gltf. for NodeAnimations
U32 modelId = engine.loadGltfNodeAnim("data/models/cube/AnimatedCube.gltf");
// we take the model definition to add to ower actor(in the futur)
const Inga::SModelDef * model = engine.getModelDef(modelId);
//creating an Array, placeholder for actor
Inga::Array<U32> nodeIds((U16)EArenaGroup::eGFX);
nodeIds.resize(model->nodeCount);
//let fill the actor
for(U32 i = 0; i < model->nodeCount; ++i)
{
const Inga::SNodeDef & node = model->nodes[i];
U32 parentId = (node.parentIdx == 0xFFFFFFFF)
? Inga::INVALID_NODE_ID
: nodeIds[node.parentIdx];
nodeIds[i] = engine.createNode(parentId);
engine.setNodeTRS(nodeIds[i], node.localTrs);
}
//main loop
while (engine.isRunning())
{
//tell InGa we are ready to draw something
float dt = engine.beginFrame();
//update all window related stuff.
wm.update();
if (wm.isQuitRequested())
{
engine.quit();
break;
}
engine.updateSceneGraph();
if (wm.beginFrame(mainWindow))
{
cam->computeMatrices();
//for this window we start rendering
renderer->begin();
//set all node from the gltf in scenegraph
for(U32 i = 0; i < model->nodeCount; ++i)
{
if(model->nodes[i].meshId == Inga::CEngine::INVALID_MESH_ID) {continue;}
Inga::SDrawCall dc;
dc.meshId = model->nodes[i].meshId;
dc.transform = engine.getWorldMatrix(nodeIds[i]);
renderer->submit(dc);
}
//we draw the cube (builtin) twice
Inga::SDrawCall dc2;
dc2.meshId = boxId;
dc2.transform = Inga::translation(Inga::Vec3(-0.2f, 0.0f, -16.0f));
dc2.color = Inga::Vec4(0.8f, 0.8f, 0.9f, 1.0f);
renderer->submit(dc2);
dc2.meshId = boxId;
dc2.transform = Inga::translation(Inga::Vec3(0.0f, 0.0f, -8.0f));
dc2.color = Inga::Vec4(0.8f, 0.2f, 0.2f, 1.0f);
renderer->submit(dc2);
renderer->flush();
wm.endFrame(mainWindow);
}
engine.endFrame();
(void)dt;
}
engine.quit();
return 0;
}Tested on
Intel ADL-N iGPU — 1280x720, confirmed working.
litte probleme
Some culling issues due tu viewport with negatif y:
But by changing the culling from back to front I got
The solution was change the front face from clockwise to counterClckwise

Next step
Node animation (TRS interpolation on GLTF nodes) — the Wind Waker style is coming.