Kuesa basic Example
Demonstrates the use of the KuesaSerenity API to import a glTF2 file inside a Window

Setup Window
Kuesa::Serenity::Window can be conveniently used to create a Window suitable for Serenity.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24  | int main(int ac, char **av)
{
    KDGui::GuiApplication app;
    // Setup Window
    Kuesa::Serenity::Window w;
    auto engine = std::make_unique<::Serenity::AspectEngine>();
    w.visible.valueChanged().connect([&app](const bool &visible) {
        if (visible == false)
            app.quit();
    });
    w.title = KDBindings::makeBinding(makeTitle(w.width, w.height, engine->fps));
    w.width = 1920;
    w.height = 1080;
    w.visible = true;
    // Setup KDGpu
    std::unique_ptr<KDGpu::GraphicsApi> api = std::make_unique<KDGpu::VulkanGraphicsApi>();
    KDGpu::Instance instance = api->createInstance(KDGpu::InstanceOptions{
            .applicationVersion = SERENITY_MAKE_API_VERSION(0, 1, 0, 0) });
    const KDGpu::SurfaceOptions surfaceOptions = KDGpuKDGui::View::surfaceOptions(&w);
    KDGpu::Surface surface = instance.createSurface(surfaceOptions);
    KDGpu::AdapterAndDevice defaultDevice = instance.createDefaultDevice(surface);
    KDGpu::Device device = std::move(defaultDevice.device);
  | 
 
Filename: basic/main.cpp
Importing a glTF2 File
We add the default Kuesa Layers  to the [LayerManager ] so that meshes can be tagged with the appropriate layer mask based on their material properties upon importing.
This will be required to filter which Entities to render at the appropriate time.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27  | std::unique_ptr<::Serenity::Entity> createKuesaScene(Kuesa::Serenity::AssetCollections *assetCollections,
                                                     ::Serenity::LayerManager *layerManager)
{
    Kuesa::Serenity::GLTF2Importer importer;
    importer.assetCollections = assetCollections;
    importer.layerManager = layerManager;
    importer.source = String(SHARED_ASSETS_DIR "/models/car/DodgeViper.gltf");
    // Insert the generated Kuesa Entities into rootEntity
    auto rootEntity = std::make_unique<::Serenity::Entity>();
    {
        // Add a default light
        ::Serenity::Entity *e = rootEntity->createChildEntity<::Serenity::Entity>();
        ::Serenity::Light *l = e->createComponent<::Serenity::Light>();
        l->type = ::Serenity::Light::Type::Point;
        ::Serenity::SrtTransform *t = e->createComponent<::Serenity::SrtTransform>();
        t->translation = glm::vec3(0.0f, 40.0f, 0.0f);
    }
    for (std::unique_ptr<::Serenity::Entity> &sceneRoot : importer.sceneRoots()) {
        // Take ownership
        rootEntity->addChildEntity(std::move(sceneRoot));
    }
    return std::move(rootEntity);
}
  | 
 
Filename: basic/main.cpp
We make use of the GLTF2Importer  to load a glTF2 file and feed our AssectCollections .
 |     // Setup Kuesa Scene
    Kuesa::Serenity::AssetCollections collections;
    ::Serenity::LayerManager layerManager;
    layerManager.addLayer(Kuesa::Serenity::Layers::ZFillLayerName);
    layerManager.addLayer(Kuesa::Serenity::Layers::OpaqueLayerName);
    layerManager.addLayer(Kuesa::Serenity::Layers::AlphaLayerName);
    std::unique_ptr<::Serenity::Entity> root = createKuesaScene(&collections, &layerManager);
  | 
 
Filename: basic/main.cpp
Retrieving Animations
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34  |     // Animations
    {
        auto *camCenterAnimator = root->createComponent<::Serenity::ClipAnimator>();
        camCenterAnimator->clip = collections.animationClips("SweepCamCenterAction");
        camCenterAnimator->mapper = collections.animationMappers("SweepCamCenterAction");
        camCenterAnimator->loops = -1;
        camCenterAnimator->setRunning(true);
        auto *camPitchAnimator = root->createComponent<::Serenity::ClipAnimator>();
        camPitchAnimator->clip = collections.animationClips("SweepCamPitchAction");
        camPitchAnimator->mapper = collections.animationMappers("SweepCamPitchAction");
        camPitchAnimator->loops = -1;
        camPitchAnimator->setRunning(true);
        auto *hoodAnimator = root->createComponent<::Serenity::ClipAnimator>();
        hoodAnimator->clip = collections.animationClips("HoodAction");
        hoodAnimator->mapper = collections.animationMappers("HoodAction");
        hoodAnimator->loops = 1;
        hoodAnimator->setRunning(true);
        const String wheelNames[] = {
            String("WheelBLDriveAction"),
            String("WheelBRDriveAction"),
            String("WheelFLDriveAction"),
            String("WheelFRDriveAction"),
        };
        for (const String &wheelName : wheelNames) {
            auto *wheelAnimator = root->createComponent<::Serenity::ClipAnimator>();
            wheelAnimator->clip = collections.animationClips(wheelName);
            wheelAnimator->mapper = collections.animationMappers(wheelName);
            wheelAnimator->loops = -1;
            wheelAnimator->setRunning(true);
        }
    }
  | 
 
Filename: basic/main.cpp
Setting up serenity
We start by creating the various aspects we will make use of.
 |     // Setup Serenity Engine
    auto renderAspect = engine->createAspect<::Serenity::RenderAspect>(std::move(device));
    auto spatialAspect = engine->createAspect<::Serenity::SpatialAspect>();
    auto logicAspect = engine->createAspect<::Serenity::LogicAspect>();
    auto animationAspect = engine->createAspect<::Serenity::AnimationAspect>();
  | 
 
Filename: basic/main.cpp
And then we configure the Serenity Rendering algorithm. Rendering will be split in 2 phases, one for Opaque and one for Alpha objects.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73  |     // Create Render Algo
    auto algo = std::make_unique<::Serenity::ForwardAlgorithm>();
    auto createOpaquePhase = [&layerManager]() {
        ::Serenity::ForwardAlgorithm::RenderPhase opaquePhase{
            layerManager.layerMask({ Kuesa::Serenity::Layers::OpaqueLayerName }),
            ::Serenity::ForwardAlgorithm::RenderPhase::Type::Opaque,
            ::Serenity::LayerFilterType::AcceptAny,
            {},
            true
        };
        auto depthState = std::make_shared<::Serenity::DepthStencilState>();
        depthState->depthTestEnabled = true;
        depthState->depthWritesEnabled = true;
        depthState->depthCompareOperation = KDGpu::CompareOperation::Less;
        opaquePhase.renderStates.setDepthStencilState(std::move(depthState));
        auto rasterizerState = std::make_shared<::Serenity::PrimitiveRasterizerState>();
        rasterizerState->cullMode = KDGpu::CullModeFlagBits::BackBit;
        opaquePhase.renderStates.setPrimitiveRasterizerState(std::move(rasterizerState));
        return opaquePhase;
    };
    auto createAlphaPhase = [&layerManager]() {
        ::Serenity::ForwardAlgorithm::RenderPhase alphaPhase{
            layerManager.layerMask({ Kuesa::Serenity::Layers::AlphaLayerName }),
            ::Serenity::ForwardAlgorithm::RenderPhase::Type::Alpha,
            ::Serenity::LayerFilterType::AcceptAll,
            {},
            true
        };
        auto depthState = std::make_shared<::Serenity::DepthStencilState>();
        depthState->depthTestEnabled = true;
        depthState->depthWritesEnabled = false;
        depthState->depthCompareOperation = KDGpu::CompareOperation::LessOrEqual;
        alphaPhase.renderStates.setDepthStencilState(std::move(depthState));
        auto blendState = std::make_shared<::Serenity::ColorBlendState>();
        ::Serenity::ColorBlendState::AttachmentBlendState attachmentBlendState;
        attachmentBlendState.format = KDGpu::Format::UNDEFINED;
        attachmentBlendState.blending.blendingEnabled = true;
        attachmentBlendState.blending.alpha.operation = KDGpu::BlendOperation::Add;
        attachmentBlendState.blending.color.operation = KDGpu::BlendOperation::Add;
        attachmentBlendState.blending.alpha.srcFactor = KDGpu::BlendFactor::SrcAlpha;
        attachmentBlendState.blending.color.srcFactor = KDGpu::BlendFactor::SrcAlpha;
        attachmentBlendState.blending.color.dstFactor = KDGpu::BlendFactor::OneMinusSrcAlpha;
        attachmentBlendState.blending.alpha.dstFactor = KDGpu::BlendFactor::One;
        blendState->attachmentBlendStates = { attachmentBlendState };
        alphaPhase.renderStates.setColorBlendState(std::move(blendState));
        return alphaPhase;
    };
    // ImGUI Overlay
    Serenity::AbstractOverlay *overlay = createOverlay(w, engine.get(), algo.get());
    // Specify RT
    algo->renderTargetRefs = { Serenity::RenderTargetRef::fromWindow(&w, instance) };
    // Specify which RenderTarget to render to, which render phases and which camera to use
    algo->renderViews = {
        Serenity::ForwardAlgorithm::RenderView(0,
                                               { createOpaquePhase(),
                                                 createAlphaPhase() },
                                               overlay,
                                               sweepCamera)
    };
    algo->msaaSamples = Serenity::RenderAlgorithm::SamplesCount::Samples_4;
  | 
 
Filename: basic/main.cpp
Finally we can set the Render Algorithm and the scene root before starting the Serenity engine.
 |     renderAspect->setRenderAlgorithm(std::move(algo));
    engine->setRootEntity(std::move(root));
    engine->running = true;
  | 
 
Filename: basic/main.cpp
Updated on 2023-07-03 at 11:02:17 +0000