0

My Qt widget show the 3D world.

I have the 3D world's Qt3DRender::QCamera

How can I use Qcamera to convert the mouse position in widget to 3D world coordinate?

I tried to use point * .viewMatrix4x4().transposed().inverted() but it is wrong.

xyLi
  • 27
  • 5

2 Answers2

0

You cannot simply convert a 2D click to a 3D coordinate, only to a 3D vector which is a direction vector. You can only obtain a 3D coordinate if you have an object which is underneath the mouse.

In this case, you can use QObjectPicker to obtain the coordinate.

The steps to do so are:

  1. Create the object picker in your scene hierarchy (it's best to create it as a child of the object you want to pick, but I'm not sure if that's necessary)
  2. Attach the object picker as a component to the object you want to pick
  3. Connect the object picker's signal that gets fired when you click the object to a function of yours that gives you the coordinates

You can also check out the manual Qt3D test on GitHub. It's in QML but you should be able to translate it to C++ (if that's what you're programming in).

Florian Blume
  • 3,237
  • 17
  • 37
  • Thanks. But using QObjectPicker need to make camera be close to the 3D Object to trigger the click pickerEvent. I just want to click screen and get the x y coordinate without z in camera view's 3D world surface. – xyLi Jun 24 '20 at 18:18
  • or QObjectPicker can be setted trigger in long distance from camera. But I don't know How to do. – xyLi Jun 24 '20 at 18:30
  • I think that I want the method lik camera.ScreenToWorldPoint( .. ) in unity. – xyLi Jun 24 '20 at 18:33
  • Where did you get from that QObjectPicker has to be close to the camera? I know that if the object is *really* far away, that this can imply floating-point inaccuracies. If you really don't want to use QObjectPicker you can compute the points lying on the near and far plane. Check out [this answer](https://stackoverflow.com/questions/51293859/how-to-calculate-3d-point-world-coordinate-from-2d-mouse-clicked-screen-coordi) to see how. – Florian Blume Jun 25 '20 at 08:45
  • **Where did you get from that QObjectPicker has to be close to the camera?** I try to build qt 3d example simple-cpp and add picker to 3D Object. I found that picker can be trigger, regardless of how far distance between camera and 3D object. How Surprised. thanks. I would think that, because I added picker to map chunks and click to get coordinates in QGIS 3D but I need to make my camera very close to the map terrain. – xyLi Jun 25 '20 at 11:05
  • Hm ok, that sounds strange. It could have something to do with the far plane. In the docs of QObjectPicker, it says that if the far plane is greater than 100.000 then there can be precision errors. But you can definitely also compute the vector into 3D space yourself. – Florian Blume Jun 25 '20 at 12:21
  • I found that the picker would not be trigger when the camera's farPlane > 100,000. Is that normal? Can I trigger it in bigger farPlane ? – xyLi Jun 29 '20 at 02:18
  • That's what I told you in my previous comment. It says in the documentation that a far plane of > 100.000 causes issues. – Florian Blume Jul 01 '20 at 09:21
  • ohohoh~~ sorry. I now understand what you say. Thanks~~~~~~~~ – xyLi Jul 02 '20 at 16:07
0

I know this question is old, but I had the same problem and wanted to go from qt3d window coordinates to 3d space. I hunted around in the qt3d source code and was able to come up with the following. I think it should work as long as you are using the Qt3DWindow from Qt3DExtras and no additional Viewports.

QVector3D mouseEventToSpace(const QMouseEvent* mouseEvent, const Qt3DRender::QCamera* camera, const QSurface* surface) {

const QPointF glCorrectSurfacePosition{static_cast<float>(mouseEvent->pos().x()),
                                       surface->size().height() - static_cast<float>(mouseEvent->pos().y())};
const QMatrix4x4 viewMatrix{camera->viewMatrix()};
const QMatrix4x4 projectionMatrix{camera->lens()->projectionMatrix()};

const int areaWidth         = surface->size().width();
const int areaHeight        = surface->size().height();
const auto relativeViewport = QRectF(0.0f, 0.0f, 1.0f, 1.0f);
const auto viewport =
    QRect(relativeViewport.x() * areaWidth, (1.0 - relativeViewport.y() - relativeViewport.height()) * areaHeight,
          relativeViewport.width() * areaWidth, relativeViewport.height() * areaHeight);
const auto nearPos = QVector3D{static_cast<float>(glCorrectSurfacePosition.x()),
                               static_cast<float>(glCorrectSurfacePosition.y()), 0.0f}
                         .unproject(viewMatrix, projectionMatrix, viewport);
return nearPos;}