Kuesa basic-iro Example
Demonstrates the use of the KuesaSerenity API to import a glTF2 file using the Iro Materials extension 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-iro/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(::Serenity::LayerManager *layerManager)
{
Kuesa::Serenity::GLTF2Importer importer;
importer.layerManager = layerManager;
importer.source = String("examples/assets/basic-iro/car/car.gltf");
assert(importer.status() == Kuesa::Serenity::GLTF2Importer::Status::Ready);
// 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-iro/main.cpp
We make use of the GLTF2Importer to load a glTF2 file and feed our AssectCollections .
| // Setup Kuesa Scene
::Serenity::LayerManager layerManager;
layerManager.addLayer(Kuesa::Serenity::Layers::ZFillLayerName);
layerManager.addLayer(Kuesa::Serenity::Layers::OpaqueLayerName);
layerManager.addLayer(Kuesa::Serenity::Layers::AlphaLayerName);
layerManager.addLayer(Kuesa::Serenity::Layers::SkyboxLayerName);
std::unique_ptr<::Serenity::Entity> root = createKuesaScene(&layerManager);
|
Filename: basic-iro/main.cpp
Setting up the Camera
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 | ::Serenity::Camera *createDefaultCamera(::Serenity::Entity *rootEntity, KDGui::Window &w)
{
// Add Camera into the Scene
::Serenity::Camera *camera = rootEntity->createChildEntity<::Serenity::Camera>();
camera->lookAt(glm::vec3(0.0f, 10.0f, 15.0f),
glm::vec3(0.0f, 0.0f, 0.0f),
glm::vec3(0.0f, 1.0f, 0.0f));
camera->lens()->setPerspectiveProjection(45.0f,
float(w.width()) / w.height(),
1.0f, 1000.0f);
camera->lens()->aspectRatio = KDBindings::makeBinding(updateAspectRatio(w.width, w.height));
auto cameraFrameAction = camera->createComponent<::Serenity::FrameAction>();
auto rotateCamera = [=](::Serenity::FrameAction *) {
const float radius = 8.0f;
static float angle = 0.0f;
angle += 0.05f;
const float rad = glm::radians(angle);
const glm::vec3 position(radius * std::cos(rad), 1.0f, radius * std::sin(rad));
camera->lookAt(position,
glm::vec3(0.0f, 0.0f, 0.0f),
glm::vec3(0.0f, 1.0f, 0.0f));
if (angle > 360.0f)
angle -= 360.0f;
};
// cameraFrameAction->triggered.connect(rotateCamera);
return camera;
}
|
Filename: basic-iro/main.cpp
Using a CameraController
| ::Serenity::Camera *defaultCamera = createDefaultCamera(root.get(), w);
// Camera Controller
Kuesa::Serenity::CameraController *controller = w.createChild<Kuesa::Serenity::CameraController>();
controller->window = &w;
controller->camera = defaultCamera;
w.cameraController = controller;
|
Filename: basic-iro/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>();
|
Filename: basic-iro/main.cpp
And then we configure the Serenity Rendering algorithm. Rendering will be split in 3 phases, one for Opaque, one for Skybox 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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88 | 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
};
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 createSkyboxPhase = [&layerManager]() {
::Serenity::ForwardAlgorithm::RenderPhase opaquePhase{
layerManager.layerMask({ Kuesa::Serenity::Layers::SkyboxLayerName }),
::Serenity::ForwardAlgorithm::RenderPhase::Type::Opaque,
::Serenity::LayerFilterType::AcceptAny
};
auto depthState = std::make_shared<::Serenity::DepthStencilState>();
depthState->depthTestEnabled = true;
depthState->depthWritesEnabled = false;
depthState->depthCompareOperation = KDGpu::CompareOperation::LessOrEqual;
opaquePhase.renderStates.setDepthStencilState(std::move(depthState));
auto rasterizerState = std::make_shared<::Serenity::PrimitiveRasterizerState>();
rasterizerState->cullMode = KDGpu::CullModeFlagBits::None;
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
};
auto depthState = std::make_shared<::Serenity::DepthStencilState>();
depthState->depthTestEnabled = true;
depthState->depthWritesEnabled = false;
depthState->depthCompareOperation = KDGpu::CompareOperation::Less;
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::Zero;
attachmentBlendState.blending.alpha.dstFactor = KDGpu::BlendFactor::OneMinusSrcAlpha;
attachmentBlendState.blending.color.srcFactor = KDGpu::BlendFactor::SrcAlpha;
attachmentBlendState.blending.color.dstFactor = KDGpu::BlendFactor::OneMinusSrcAlpha;
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(),
createSkyboxPhase(),
createAlphaPhase() },
overlay,
defaultCamera)
};
algo->msaaSamples = Serenity::RenderAlgorithm::SamplesCount::Samples_4;
|
Filename: basic-iro/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-iro/main.cpp
Updated on 2023-07-03 at 11:02:17 +0000