[QT3D] minExtent and maxExtent not giving the right values
-
Hey there,
I'm trying to create a pretty simple .obj viewer rendering a mesh on a floor, like the default 3D viewer in Windows 10 illustrated below.
Here is my result for now :
As you can see, I'm having a problem with the plane which is not correctly positionned.
I'm using a
Qt3DExtras::QPlaneMesh
for it, and translating it usingQt3DCore::QTransform
.The problem is about this translation. What I'd like to do is to apply a translation
(0.0f, minY, 0.0f)
to the plane whereminY
is the lowest y value of the bounding box of the displayed mesh. As far as I understand the documentation, I need to use theminExtent
function on theQGeometry
associated to theQMesh
. Let's saymesh
is the mesh to display, I'm doingminY = mesh.geometry()->minExtent().y()
. This is not working,minY
is always0.0f
.So basically I'm asking for help to compute this
minY
value :/I made a minimal example, here is the code (the problem is in
handleStatusChanged
inmyscene.cpp
) :
main.cpp
myscene.h
myscene.cppThanks for your help !
-
aaaaand I found the solution :
As you can see, the floor origin is now perfectly positionned (and fits with any mesh).
Solution in short :
- handle the
statusChanged
signal - once
status == Qt3DRender::QMesh::Ready
, the mesh is loaded BUT its bounding box is not (necessarily?) computed yet. I made this assumption and this was the mistake. - handle the
minExtentChanged
signal by connecting it to a function ONLY ONCEstatus == Qt3DRender::QMesh::Ready
- update the plane position in this function
Here is the final code :
void MyScene::handleStatusChanged(const Qt3DRender::QMesh::Status &status) { if(status == Qt3DRender::QMesh::Ready) { connect(mesh.geometry(), &Qt3DCore::QGeometry::minExtentChanged, this, &MyScene::handleMinExtentChanged); } } void MyScene::handleMinExtentChanged(const QVector3D & minExtent) { Qt3DCore::QTransform * planeTransform = new Qt3DCore::QTransform(&floorEntity); planeTransform->setTranslation(QVector3D(0.0f,mesh.geometry()->minExtent().y(),0.0f)); floorEntity.addComponent(planeTransform); }
More details
I was mistaken by the
Qt3DRender::QMesh::Ready
status : the fact that the QMesh is flagged as "Ready" does not mean that its bounding box is computed.Indeed, minExtent and maxExtent values are set by the function setExtent in qgeometry.cpp. By setting one breakpoint in setExtent and one breakpoint in the
if
statement ofhandleStatusChanged
, you will observe that theif
statement breakpoint is triggered the first. That is, even thought the QMesh is "Ready", it's extents are not yet set.Furthermore, you have to wait for the QMesh to be "Ready" before connecting to the
minExtentChanged
signal because before that, the QGeometry associated tomesh
is not set. You cannot connect tomesh.geometry()
immediatly after a call tosetSource
because the QGeometry it points to isnullptr
, and change only once the QMesh is "Ready". That's the reason why I'm connecting only in theif
statement ofhandleStatusChanged
Hopefully this will help some people.
PS : I still do not understand how to use
updateImplicitBounds()
, but I'm done with my issue here. - handle the
-
Still stucked, but I noticed that the call to
mesh.updateImplicitBounds();
is returningfalse
inmyscene.cpp
Sadly, the documentation is not giving any detail about the possible reasons for such a thing :/
I'm going to debug the source code, I'll keep you informed.
-
Reason why
updateImplicitBounds()
is returning falseI found out why
mesh.updateImplicitBounds();
is returning false :mesh
has noQGeometryView
associated to it, andupdateImplicitBounds()
is defined inqboundingvolume.cpp
as :bool QBoundingVolume::updateImplicitBounds() { Q_D(QBoundingVolume); if (!d->m_view) //Triggered since there is no view associated to mesh return false; auto data = BoundingVolumeComputeData::fromView(d->m_view); if (!data.valid()) return false; auto res = data.compute(); if (!res.valid()) return false; d->setImplicitBounds(res.m_min, res.m_max, res.m_center, res.m_radius); return true; }
The fact that this function always returns false makes sense because the first conditional is always triggered since I have no
QGeometryView
associated tomesh
.Now I have a new problem because I don't even understand what is a
QGeometryView
, which is defined in the documentation as "A GeometryView holds all the information necessary to handle a Geometry" which is way too fuzzy to be understandable. -
aaaaand I found the solution :
As you can see, the floor origin is now perfectly positionned (and fits with any mesh).
Solution in short :
- handle the
statusChanged
signal - once
status == Qt3DRender::QMesh::Ready
, the mesh is loaded BUT its bounding box is not (necessarily?) computed yet. I made this assumption and this was the mistake. - handle the
minExtentChanged
signal by connecting it to a function ONLY ONCEstatus == Qt3DRender::QMesh::Ready
- update the plane position in this function
Here is the final code :
void MyScene::handleStatusChanged(const Qt3DRender::QMesh::Status &status) { if(status == Qt3DRender::QMesh::Ready) { connect(mesh.geometry(), &Qt3DCore::QGeometry::minExtentChanged, this, &MyScene::handleMinExtentChanged); } } void MyScene::handleMinExtentChanged(const QVector3D & minExtent) { Qt3DCore::QTransform * planeTransform = new Qt3DCore::QTransform(&floorEntity); planeTransform->setTranslation(QVector3D(0.0f,mesh.geometry()->minExtent().y(),0.0f)); floorEntity.addComponent(planeTransform); }
More details
I was mistaken by the
Qt3DRender::QMesh::Ready
status : the fact that the QMesh is flagged as "Ready" does not mean that its bounding box is computed.Indeed, minExtent and maxExtent values are set by the function setExtent in qgeometry.cpp. By setting one breakpoint in setExtent and one breakpoint in the
if
statement ofhandleStatusChanged
, you will observe that theif
statement breakpoint is triggered the first. That is, even thought the QMesh is "Ready", it's extents are not yet set.Furthermore, you have to wait for the QMesh to be "Ready" before connecting to the
minExtentChanged
signal because before that, the QGeometry associated tomesh
is not set. You cannot connect tomesh.geometry()
immediatly after a call tosetSource
because the QGeometry it points to isnullptr
, and change only once the QMesh is "Ready". That's the reason why I'm connecting only in theif
statement ofhandleStatusChanged
Hopefully this will help some people.
PS : I still do not understand how to use
updateImplicitBounds()
, but I'm done with my issue here. - handle the