001 — First Vulkan 1.3 Render: Behind the 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 — eGraphics and eCompute
  • Swapchain: 4 images, FIFO mode, VK_FORMAT_B8G8R8A8_SRGB format
  • 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: Culling back was wrong But by changing the culling from back to front I got No culling probleme but not acceptable to cull font The solution was change the front face from clockwise to counterClckwise Cube no problemes

Next step

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