How to mirror coordinate system in Qt3D?
I've loaded panorama using slightly modified (to be able to use metainformation (tilt and heading values to adjust initial view) and unpack individual faces from custom format *.360
(just six *.jpg
-s)) SkyboxEntity
. Suddenly, all the textural information are wrongly mirrored.
Scene3D {
aspects: ["input", "logic"]
cameraAspectRatioMode: Scene3D.AutomaticAspectRatio
multisample: true
entity: Entity {
Camera {
id: basicCamera
projectionType: CameraLens.PerspectiveProjection
fieldOfView: 60
nearPlane: 0.1
farPlane: 2.0 * Math.SQRT2
position: Qt.vector3d(0.0, 0.0, 0.0)
upVector: Qt.vector3d(0.0, 1.0, 0.0)
viewCenter: Qt.vector3d(1.0, 0.0, 0.0)
}
FirstPersonCameraController {
camera: basicCamera
lookSpeed: -200.0
}
components: [
RenderSettings {
activeFrameGraph: ForwardRenderer {
camera: basicCamera
clearColor: "transparent"
}
},
InputSettings {}
]
PanoEntity {
source: "file:///home/user/library.360"
}
}
}
panoentity.hpp
:
#pragma once
#include <Qt3DExtras>
class PanoEntity
: public Qt3DCore::QEntity
{
Q_OBJECT
Q_PROPERTY(QUrl source MEMBER source NOTIFY sourceChanged)
public :
explicit PanoEntity(Qt3DCore::QNode * const parent = Q_NULLPTR);
private Q_SLOTS :
void reloadTexture(QUrl source);
Q_SIGNALS :
void sourceChanged(const QUrl & path);
private :
Qt3DRender::QEffect * const effect = ::new Qt3DRender::QEffect;
Qt3DRender::QMaterial * const material = ::new Qt3DRender::QMaterial;
Qt3DRender::QTextureCubeMap * const texture = ::new Qt3DRender::QTextureCubeMap;
Qt3DRender::QTextureLoader * const textureLoader = ::new Qt3DRender::QTextureLoader;
Qt3DRender::QShaderProgram * const shader = ::new Qt3DRender::QShaderProgram;
Qt3DRender::QTechnique * const techinque = ::new Qt3DRender::QTechnique;
Qt3DRender::QFilterKey * const filterKey = ::new Qt3DRender::QFilterKey;
Qt3DRender::QRenderPass * const renderPass = ::new Qt3DRender::QRenderPass;
Qt3DExtras::QCuboidMesh * const mesh = ::new Qt3DExtras::QCuboidMesh;
Qt3DRender::QParameter * const textureParameter = ::new Qt3DRender::QParameter{QStringLiteral("skyboxTexture"), texture};
Qt3DRender::QTextureImage * const posXImage = ::new Qt3DRender::QTextureImage;
Qt3DRender::QTextureImage * const posYImage = ::new Qt3DRender::QTextureImage;
Qt3DRender::QTextureImage * const posZImage = ::new Qt3DRender::QTextureImage;
Qt3DRender::QTextureImage * const negXImage = ::new Qt3DRender::QTextureImage;
Qt3DRender::QTextureImage * const negYImage = ::new Qt3DRender::QTextureImage;
Qt3DRender::QTextureImage * const negZImage = ::new Qt3DRender::QTextureImage;
Qt3DCore::QTransform * const transform = ::new Qt3DCore::QTransform;
QUrl source;
};
panoentity.cpp
:
#include "panoentity.h"
#include "configurator.hpp"
#include "pano.h"
#include "common.hpp"
#include "utility.hpp"
using namespace Qt3DCore;
using namespace Qt3DExtras;
using namespace Qt3DRender;
PanoEntity::PanoEntity(QNode * const parent)
: QEntity{parent}
{
textureLoader->setGenerateMipMaps(false);
shader->setVertexShaderCode(QShaderProgram::loadSource(QStringLiteral("qrc:/shaders/pano.vert")));
shader->setFragmentShaderCode(QShaderProgram::loadSource(QStringLiteral("qrc:/shaders/pano.frag")));
filterKey->setParent(effect);
filterKey->setName(QStringLiteral("renderingStyle"));
filterKey->setValue(QStringLiteral("forward"));
techinque->addFilterKey(filterKey);
renderPass->setShaderProgram(shader);
const auto cullFront = ::new QCullFace;
cullFront->setMode(QCullFace::Front);
const auto depthTest = ::new QDepthTest;
depthTest->setDepthFunction(QDepthTest::LessOrEqual);
renderPass->addRenderState(cullFront);
renderPass->addRenderState(depthTest);
techinque->addRenderPass(renderPass);
effect->addTechnique(techinque);
material->setEffect(effect);
material->addParameter(textureParameter);
mesh->setXYMeshResolution({2, 2});
mesh->setXZMeshResolution({2, 2});
mesh->setYZMeshResolution({2, 2});
posXImage->setFace(QTextureCubeMap::CubeMapPositiveX);
posXImage->setMirrored(false);
posYImage->setFace(QTextureCubeMap::CubeMapPositiveY);
posYImage->setMirrored(false);
posZImage->setFace(QTextureCubeMap::CubeMapPositiveZ);
posZImage->setMirrored(false);
negXImage->setFace(QTextureCubeMap::CubeMapNegativeX);
negXImage->setMirrored(false);
negYImage->setFace(QTextureCubeMap::CubeMapNegativeY);
negYImage->setMirrored(false);
negZImage->setFace(QTextureCubeMap::CubeMapNegativeZ);
negZImage->setMirrored(false);
transform->setRotationY(90.0f);
transform->setRotationX(90.0f);
texture->setMagnificationFilter(QTextureCubeMap::Linear);
texture->setMinificationFilter(QTextureCubeMap::Linear);
texture->setGenerateMipMaps(false);
texture->setWrapMode(QTextureWrapMode{QTextureWrapMode::ClampToEdge});
texture->addTextureImage(posXImage);
texture->addTextureImage(posYImage);
texture->addTextureImage(posZImage);
texture->addTextureImage(negXImage);
texture->addTextureImage(negYImage);
texture->addTextureImage(negZImage);
addComponent(mesh);
addComponent(material);
addComponent(transform);
connect(this, &PanoEntity::sourceChanged, this, &PanoEntity::reloadTexture);
}
void PanoEntity::reloadTexture(QUrl source)
{
if (!source.isLocalFile()) {
return;
}
QDir currentCatalog = Configurator::currentCatalogPath();
QFileInfo fileInfo{currentCatalog, source.toLocalFile()};
if (fileInfo.suffix() != "360") {
return;
}
if (fileInfo.canonicalPath().startsWith(currentCatalog.canonicalPath())) {
return;
}
QFile panoFile{fileInfo.filePath()};
if (!panoFile.open(QFile::ReadOnly)) {
return;
}
QDataStream dataStream{&panoFile};
Pano pano;
if (!pano.readHeader(dataStream)) {
return;
}
if (!pano.readBody(dataStream)) {
return;
}
dataStream.setDevice(Q_NULLPTR);
QDir cacheDir{getBaseDirName() + "/var/cache/pano"};
if (!cacheDir.exists() && !cacheDir.mkpath(cacheDir.path())) {
return;
}
if (!panoFile.reset()) {
return;
}
QCryptographicHash hash{QCryptographicHash::Md5};
hash.addData(&panoFile);
const auto baseName = QString::fromUtf8(hash.result().toHex());
int i = 0;
for (const auto suffix : {"_posx", "_negx", "_posy", "_negy", "_posz", "_negz"}) {
const QString fileName = baseName + suffix;
if (!cacheDir.exists(fileName)) {
QSaveFile file{cacheDir.filePath(fileName)};
if (file.open(QFile::WriteOnly | QFile::Truncate)) {
Q_ASSERT(file.pos() == 0);
if (file.write(pano.frames[i].buffer())) {
if (!file.commit()) {
qWarning() << QStringLiteral("Unable to commit %1").arg(file.fileName());
} else {
qDebug().noquote()
<< QStringLiteral("QML URL: %1; suffix %2 is cached in file %3")
.arg(source.toString(), QString::fromLatin1(suffix), file.fileName());
}
}
} else {
qDebug() << file.errorString();
}
} else {
qDebug() << "file already exist: " << cacheDir.filePath(fileName);
}
++i;
}
const auto basePath = cacheDir.filePath(baseName);
posXImage->setSource(QUrl::fromLocalFile(basePath + "_posx"));
posYImage->setSource(QUrl::fromLocalFile(basePath + "_negy"));
posZImage->setSource(QUrl::fromLocalFile(basePath + "_posz"));
negXImage->setSource(QUrl::fromLocalFile(basePath + "_negx"));
negYImage->setSource(QUrl::fromLocalFile(basePath + "_posy"));
negZImage->setSource(QUrl::fromLocalFile(basePath + "_negz"));
textureParameter->setValue(QVariant::fromValue(texture));
transform->setRotationZ(transform->rotationZ() + pano.heading());
transform->setRotationX(transform->rotationX() + pano.tilt());
}
Can I achieve this by means of swapping pos
and neg
textures for each axis and make them mirrored, or can I somehow swap two axis of coordinate system using CameraLens
of Camera
stuff? Or maybe it is better to just modify shader to sample texture coordinates in different order?