Kuesa Many Ducks C++ Example
#include <ForwardRenderer>
#include <SceneEntity>
#include <DepthOfFieldEffect>
#include <GLTF2Importer>
#include <Skybox>
#include <QScreen>
#include <MeshInstantiator>
#include <Qt3DCore/QEntity>
#include <Qt3DCore/QTransform>
#include <Qt3DRender/QEnvironmentLight>
#include <Qt3DRender/QCamera>
#include <Qt3DRender/QMaterial>
#include <Qt3DRender/QGeometryRenderer>
#include <Qt3DExtras/Qt3DWindow>
#include <Qt3DExtras/QOrbitCameraController>
#include <QGuiApplication>
#include <QTimer>
#include <QRandomGenerator>
#include <array>
#ifdef Q_OS_ANDROID
#include <QOpenGLContext>
#endif
namespace {
template<typename ComponentType>
inline ComponentType *componentFromEntity(Qt3DCore::QEntity *e)
{
const auto cmps = e->componentsOfType<ComponentType>();
return cmps.size() > 0 ? cmps.first() : nullptr;
}
static QString envmap(QString name)
{
return QStringLiteral("qrc:///pink_sunrise_16f_%1").arg(name);
}
}
class DefaultEnvMap : public Qt3DRender::QEnvironmentLight
{
public:
DefaultEnvMap(Qt3DCore::QNode *parent = nullptr)
: Qt3DRender::QEnvironmentLight{ parent }
{
auto tli = new Qt3DRender::QTextureLoader(this);
tli->setSource(QUrl(envmap("irradiance.dds")));
tli->setMinificationFilter(Qt3DRender::QAbstractTexture::LinearMipMapLinear);
tli->setMagnificationFilter(Qt3DRender::QAbstractTexture::Linear);
tli->wrapMode()->setX(Qt3DRender::QTextureWrapMode::ClampToEdge);
tli->wrapMode()->setY(Qt3DRender::QTextureWrapMode::ClampToEdge);
tli->setGenerateMipMaps(0);
setIrradiance(tli);
auto tls = new Qt3DRender::QTextureLoader(this);
tls->setSource(QUrl(envmap("specular.dds")));
tls->setMinificationFilter(Qt3DRender::QAbstractTexture::LinearMipMapLinear);
tls->setMagnificationFilter(Qt3DRender::QAbstractTexture::Linear);
tls->wrapMode()->setX(Qt3DRender::QTextureWrapMode::ClampToEdge);
tls->wrapMode()->setY(Qt3DRender::QTextureWrapMode::ClampToEdge);
tls->setGenerateMipMaps(0);
setSpecular(tls);
}
};
class Window : public Qt3DExtras::Qt3DWindow
{
public:
static constexpr int Ducks = 2000;
static constexpr int r = 2000;
explicit Window(const bool usesInstancing)
: m_usesInstancing(usesInstancing)
{
m_scene = new Kuesa::SceneEntity();
m_scene->addComponent(new DefaultEnvMap(m_scene));
camera()->setPosition(QVector3D(5, 1.5, 5));
camera()->setViewCenter(QVector3D(0, 0.5, 0));
camera()->setUpVector(QVector3D(0, 1, 0));
camera()->setAspectRatio(16.f / 9.f);
auto camController = new Qt3DExtras::QOrbitCameraController(m_scene);
camController->setCamera(camera());
auto fg = new Kuesa::ForwardRenderer();
fg->setCamera(camera());
fg->setGamma(2.2f);
fg->setExposure(1.f);
fg->setClearColor("white");
fg->setSkinning(true);
fg->setFrustumCulling(!usesInstancing);
setActiveFrameGraph(fg);
auto gltfImporter = new Kuesa::GLTF2Importer(m_scene);
gltfImporter->setSceneEntity(m_scene);
gltfImporter->setSource(QUrl{ "qrc:///assets/models/duck/Duck.glb" });
connect(gltfImporter, &Kuesa::GLTF2Importer::statusChanged,
this, &Window::on_sceneLoaded);
auto skybox = new Kuesa::Skybox(m_scene);
skybox->setBaseName(envmap("radiance"));
skybox->setExtension(".dds");
auto dof = new Kuesa::DepthOfFieldEffect();
dof->setRadius(15.0f);
dof->setFocusRange(2.0f);
dof->setFocusDistance(6.5f);
fg->addPostProcessingEffect(dof);
setRootEntity(m_scene);
}
private:
void on_sceneLoaded(Kuesa::GLTF2Importer::Status status)
{
if (status == Kuesa::GLTF2Importer::Ready) {
if (!m_usesInstancing) {
auto parent = m_scene->entity("KuesaEntity_0");
auto *orig_entity = qobject_cast<Qt3DCore::QEntity *>(m_scene->entity("KuesaEntity_2")->childNodes()[1]);
auto *orig_geometry = componentFromEntity<Qt3DRender::QGeometryRenderer>(orig_entity);
auto *orig_material = componentFromEntity<Qt3DRender::QMaterial>(orig_entity);
QRandomGenerator *rand = QRandomGenerator::global();
for (int i = 0; i < Ducks; i++) {
auto new_entity = new Qt3DCore::QEntity{ parent };
auto new_transform = new Qt3DCore::QTransform;
new_transform->setScale(0.2f);
new_transform->setTranslation(QVector3D(rand->generate() % r - r / 2, rand->generate() % r - r / 2, rand->generate() % r - r / 2));
new_transform->setRotationX(rand->generate() % 360);
new_transform->setRotationY(rand->generate() % 360);
new_transform->setRotationZ(rand->generate() % 360);
new_entity->addComponent(new_transform);
new_entity->addComponent(orig_geometry);
new_entity->addComponent(orig_material);
m_transforms[i] = new_transform;
}
} else {
QRandomGenerator *rand = QRandomGenerator::global();
m_matrices.reserve(Ducks + 1);
for (int i = 0; i < Ducks; i++) {
QMatrix4x4 m;
const int extent = r / 20;
m.translate(QVector3D(rand->generate() % extent - extent * 0.5, rand->generate() % extent - extent * 0.5, rand->generate() % extent - extent * 0.5));
m.rotate(rand->generate() % 360, QVector3D(1.0f, 0.0f, 0.0f));
m.rotate(rand->generate() % 360, QVector3D(0.0f, 1.0f, 0.0f));
m.rotate(rand->generate() % 360, QVector3D(0.0f, 0.0f, 1.0f));
m_matrices.push_back(m);
}
m_matrices.push_back({});
m_meshInstantiator = new Kuesa::MeshInstantiator(m_scene);
m_meshInstantiator->setEntityName(QStringLiteral("KuesaEntity_2"));
m_meshInstantiator->setTransformationMatrices(m_matrices);
}
qreal ms = 1000. / this->screen()->refreshRate();
startTimer(static_cast<int>(ms));
}
}
void timerEvent(QTimerEvent *event) override
{
Q_UNUSED(event)
if (!m_usesInstancing) {
for (auto transform : m_transforms) {
transform->setRotationX(transform->rotationX() + 0.1f);
transform->setRotationY(transform->rotationY() + 0.1f);
transform->setRotationZ(transform->rotationZ() + 0.1f);
}
} else {
static bool wasInitialized = false;
static QMatrix4x4 rotationIncrementMatrix;
if (!wasInitialized) {
rotationIncrementMatrix.rotate(0.1f, QVector3D(1.0f, 0.0f, 0.0f));
rotationIncrementMatrix.rotate(0.1f, QVector3D(0.0f, 1.0f, 0.0f));
rotationIncrementMatrix.rotate(0.1f, QVector3D(0.0f, 0.0f, 1.0f));
wasInitialized = true;
}
for (QMatrix4x4 &m : m_matrices)
m *= rotationIncrementMatrix;
m_meshInstantiator->setTransformationMatrices(m_matrices);
}
}
const bool m_usesInstancing;
Kuesa::SceneEntity *m_scene{};
std::array<Qt3DCore::QTransform *, Ducks> m_transforms;
std::vector<QMatrix4x4> m_matrices;
Kuesa::MeshInstantiator *m_meshInstantiator = nullptr;
};
int main(int argc, char *argv[])
{
Kuesa::setupDefaultSurfaceFormat();
QGuiApplication app(argc, argv);
constexpr bool useInstancing = true;
Window window(useInstancing);
window.show();
return app.exec();
}