Qt3D: how to print text? (QText2DEntity)



  • Hello,

    I want to use Qt3D to do a 3D graph visualizer. I'm at the very beginning, studying if Qt3D could be a good option for me.

    Among other, I want to print the names of the nodes in the 3D view (next to the nodes). These names should always face the camera, even if the camera moves. I have the feeling that Qt3DExtras::QText2DEntity is the class I should use. Unfortunately, its documentation is very succinct, and I can't find better example than what is shown here: https://blog.qt.io/blog/2017/05/24/qt3d/ (scroll to "Text Support").
    I don't think I want to use the other mentioned text class (QExtrudedText...) as I don't want 3D text.

    I tried this in C++:

    auto *text2d = new Qt3DExtras::QText2DEntity(_rootEntity);
    text2d->setText("LOL!!!");
    text2d->setHeight(3);
    text2d->setWidth(3);
    text2d->setColor(Qt::green);
    text2d->setFont(QFont("Courier New", 10));
    

    Unfortunately, nothing appears. From this video, it seems that the QText2DEntity class embed a mesh and a material, so I'm not supposed to add it (?): https://youtu.be/YP8alTWV_BI?t=17m38s

    I also tried in QML, even if I want a C++ solution in the end (just to test). I changed the "simple-qml" example from Qt3D to this:

    import QtQuick 2.2 as QQ2
    import Qt3D.Core 2.0
    import Qt3D.Render 2.0
    import Qt3D.Input 2.0
    import Qt3D.Extras 2.9
    
    Entity {
        id: sceneRoot
    
        Camera {
            id: camera
            projectionType: CameraLens.PerspectiveProjection
            fieldOfView: 45
            aspectRatio: 16/9
            nearPlane : 0.1
            farPlane : 1000.0
            position: Qt.vector3d( 0.0, 0.0, -40.0 )
            upVector: Qt.vector3d( 0.0, 1.0, 0.0 )
            viewCenter: Qt.vector3d( 0.0, 0.0, 0.0 )
        }
    
        OrbitCameraController {
            camera: camera
        }
    
        components: [
            RenderSettings {
                activeFrameGraph: ForwardRenderer {
                    clearColor: Qt.rgba(0, 0.5, 1, 1)
                    camera: camera
                }
            },
            // Event Source will be set by the Qt3DQuickWindow
            InputSettings { }
        ]
    
        Text2DEntity {
            id: text
            text: "Hello World"
            width: 20
            height: 10
            color: Qt.red
        }
    }
    

    But I have the same problem: nothing appears...

    What am I doing wrong? Do you have any inputs on this class?
    Thanks for any help :D



  • Nice, the documentation of this class is less than unsatisfactory, it's simply absent. But it looks like (and that's what they say in the video) that you can simply use it like any other entity, transform it, etc. What do you mean by "so I'm not supposed to add it"? I would say your setup looks about right. Did you try out different camera positions? Or to rotate the text? I'd assume that it's located at the origin, but maybe the text is flat, for example, and for the camera it becomes a 1D line. Do you know what I mean? Your camera is set to look along the z-axis, maybe add some height or rotate the text around the x-axis by 90 degrees.

    If you managed to display some text, the next step would probably be to maybe subclass QEntity (I'm not sure if this is the nicest solution), and add the node and update it whenever the entity is moved. This way, you could achieve that the entity is always labeled. You then only have to define a branch of the framgraph using a QLayerFilter and a disabled depth-test (such that the text is always visible) and add the respective QLayer to the text node. You then still have to link rotation changes in the camera to a function that sets that rotation (or inverse? not sure...) on the text node.

    Hope this helps.



  • Hi!

    Thanks for your answer :) I actually have been able to display some text with this code:

    // camera position
    auto *camera = _view->camera();
    camera->setPosition({ 0, 40.0f, 0 });
    camera->setViewCenter({ 0, 0, 0 });
    camera->setUpVector({ 0, 0, 1.0f });
    camera->panAboutViewCenter(180.0f)
    // text
    auto *text2D = new Qt3DExtras::QText2DEntity(_rootEntity);
    text2D->setFont(QFont("monospace"));
    text2D->setHeight(20);
    text2D->setWidth(100);
    text2D->setText("monospace");
    text2D->setColor(Qt::yellow);
    auto *textTransform = new Qt3DCore::QTransform(text2D);
    textTransform->setRotation(QQuaternion::fromAxisAndAngle({ 1, 0, 0 }, 90.0f));
    textTransform->setScale(0.125f);
    text2D->addComponent(textTransform);
    

    I have to downscale it a lot! setScale(0.125f)
    The size of the font doesn't change anything, everything must be done with the scale (apparently).

    I think, this is why I didn't have text before: the size (3*3) was too little and the text too big... Do you know how I could compute this window? Here I put 20*100, but it is just some magic numbers, results of tries and fails...

    I'm totally new to Qt3D and really not familiar with the framegraph and all. Do you have links to some tutorials or good starting points? Thanks a lot :)



  • And Qt3D's documentation is a pain in the ***, unfortunately. And I don't know how to compute the size to fit the text. Maybe have a look into the git repository, they compute some sizes there.

    They compute the size of the The only good starting points that I know of are actual examples. I wrote two relatively simple framegraphs for a background image and offscreen rendering. This example helped me understand the framgraph, as well.

    One things that really confused me was that the framegraph and the scenegraph classes share the same baseclass: QNode. But the framegraph nodes are all of type QFrameGraphNode and the scene graph nodes are all of type QEntity.

    Another thing I really didn't understand was how to set a custom framegraph on the Qt3DWindow and what this render settings thing was. Well, the QRenderSettings are supposed to hold the whole frame graph. But in the end, you have to add the settings to a root QEntity, which is the root of the WHOLE graph, i.e. the root of the framegraph and the scene graph. This root entity is then set as the root on the aspect engine, which does all the processing in the background. If you want rendernig, you have to add a QRenderAspect to the engine (same goes for input, etc.). The aspect (or whatever) uses the render settings to retrieve the framegraph and process all nodes. Like I said before, I was really confused first because I didn't sometimes get the difference between the nodes (e.g. there is a QLayerFilter, which should be part of the framegraph, and a QLayer, which should be part of the scene graph -> this way you can ensure that only one branch of the scene graph is being process by the branch employing the QLayerFilter).

    This was probably a quite confusing text but I hope I helped you in some way :D



  • Thanks for all your help :)

    I mark this thread as solved. I'll eventually update it if / when I succeed to do the last steps.
    As it is just for demo right now, I don't want to go in to much complexity.

    I looked at your repos, and it helped me a lot to understand how it works. It will surely help me in the future :D



  • @zespy , hello!
    I've been trying to do exactly same thing - print text with QText2DEntity.
    And, unfortunately, that code doesn't work for me. I've been variating all parameters for a while and haven't reach any result - just nothing appears on screene.
    Is it possible to add any other example or\and example project?

    And thank's you for this thread, it's still biggest conversation about QText2DEntity usage



  • I've got the same issue with Qt3DExtras::QText2DEntity. But I found the workaround.
    Look like there is a bug? in implementation of Qt3DExtras::QText2DEntity that prevents internal initialization (QText2DEntityPrivate::setScene is never called). This happens when scene is already visible and Qt3DExtras::QText2DEntity is created with parent entity passed to constructor. The workaround I used to make it work is to pass nullptr (which is default) as parent in constructor and call ->setParent(parentEntity) in separate call.

    m_text2dLabel = new Qt3DExtras::QText2DEntity();
    m_text2dLabel->setParent(parentNode);