Example: gui_display3D_custom_render

See: Tutorial: 3D scenes

C++ example source code:

/* +------------------------------------------------------------------------+
   |                     Mobile Robot Programming Toolkit (MRPT)            |
   |                          https://www.mrpt.org/                         |
   |                                                                        |
   | Copyright (c) 2005-2024, Individual contributors, see AUTHORS file     |
   | See: https://www.mrpt.org/Authors - All rights reserved.               |
   | Released under BSD License. See: https://www.mrpt.org/License          |
   +------------------------------------------------------------------------+ */

#include <mrpt/core/round.h>
#include <mrpt/gui/CDisplayWindow3D.h>
#include <mrpt/opengl/CAxis.h>
#include <mrpt/opengl/CBox.h>
#include <mrpt/opengl/CGridPlaneXY.h>
#include <mrpt/opengl/CSphere.h>
#include <mrpt/opengl/CText.h>
#include <mrpt/opengl/stock_objects.h>
#include <mrpt/system/CObserver.h>
#include <mrpt/system/CTicTac.h>
#include <mrpt/system/os.h>

#include <chrono>
#include <iostream>
#include <thread>

using namespace std;
using namespace mrpt;
using namespace mrpt::gui;
using namespace mrpt::opengl;
using namespace mrpt::system;

// This is my custom class to handle the pre/post render events:
struct TMyExtraRenderingStuff : public mrpt::system::CObserver
{
    opengl::CSphere::Ptr ball_obj;  // The ball moving in the scene

    TMyExtraRenderingStuff() {}
    void OnEvent(const mrptEvent& e) override
    {
        if (e.isOfType<mrptEventGLPreRender>())
        {
            // const mrptEventGLPreRender* ev = e.getAs<mrptEventGLPreRender>();
            // ev-> ...
        }
        else if (e.isOfType<mrptEventGLPostRender>())
        {
            // const mrptEventGLPostRender* ev =
            // e.getAs<mrptEventGLPostRender>();
        }
    }
};

// ------------------------------------------------------
//              TestDisplay3D
// ------------------------------------------------------
void TestDisplay3D()
{
    CDisplayWindow3D win("Example of 3D Scene Visualization - MRPT", 640, 480);

    Scene::Ptr& theScene = win.get3DSceneAndLock();

    // The unique instance of the observer class:
    TMyExtraRenderingStuff my_extra_rendering;

    // And start subscribing to the viewport events:
    opengl::Viewport::Ptr the_main_view = theScene->getViewport("main");
    my_extra_rendering.observeBegin(*the_main_view);

    // Modify the scene:
    // ------------------------------------------------------
    {
        opengl::CGridPlaneXY::Ptr obj =
            opengl::CGridPlaneXY::Create(-20, 20, -20, 20, 0, 1);
        obj->setColor(0.8f, 0.8f, 0.8f);
        theScene->insert(obj);
    }

    theScene->insert(mrpt::opengl::stock_objects::CornerXYZ());

    if (true)
    {
        opengl::CAxis::Ptr obj = opengl::CAxis::Create();
        obj->setFrequency(5);
        obj->enableTickMarks();
        obj->setAxisLimits(-10, -10, -10, 10, 10, 10);
        theScene->insert(obj);
    }

    {
        opengl::CSphere::Ptr obj = opengl::CSphere::Create();
        obj->setColor(0, 0, 1);
        obj->setRadius(0.3f);
        obj->setLocation(0, 0, 1);
        obj->setName("ball_1");
        theScene->insert(obj);

        // And also let my rendering object access this ball properties:
        my_extra_rendering.ball_obj = obj;
    }

    // IMPORTANT!!! IF NOT UNLOCKED, THE WINDOW WILL NOT BE UPDATED!
    win.unlockAccess3DScene();

    // Texts:
    win.addTextMessage(0.01, 0.85, "This is a 2D message", 0 /*id */);

    win.setCameraElevationDeg(25.0f);
    // win.setCameraProjective(false);

    win.addTextMessage(0.7, 0.9, "Press 'h' for help", 1 /*id*/);

    cout << endl;
    cout << "Control with mouse or keyboard. Valid keys:" << endl;
    cout << "  ESC                        -> Exit" << endl;
    cout << "  Left/right cursor arrow    -> Camera azimuth" << endl;
    cout << endl;

    bool end = false;

    CTicTac timer;
    timer.Tic();

    while (!end && win.isOpen())
    {
        // Move the scene:
        Scene::Ptr& theScene = win.get3DSceneAndLock();

        opengl::CRenderizable::Ptr obj1 = theScene->getByName("ball_1");
        const double t = timer.Tac();
        const double R = 8;
        const double W = 5.0, Q = 3.3;
        obj1->setLocation(
            R * cos(W * t) * sin(Q * t), R * sin(W * t),
            R * cos(W * t) * cos(Q * t));

        // Update the texts on the gl display:
        win.addTextMessage(
            5, 5, mrpt::format("FPS=%5.02f", win.getRenderingFPS()), 0);

        // IMPORTANT!!! IF NOT UNLOCKED, THE WINDOW WILL NOT BE UPDATED!
        win.unlockAccess3DScene();

        // Update window:
        win.forceRepaint();
        std::this_thread::sleep_for(1ms);

        if (mrpt::system::os::kbhit()) end = true;
        if (win.keyHit())
        {
            mrptKeyModifier kmods;
            int key = win.getPushedKey(&kmods);
            // printf("Key pushed: %c (%i) - modifiers:
            // 0x%04X\n",char(key),key,kmods);

            if (key == MRPTK_ESCAPE) end = true;

            if (key == 'h' || key == 'H')
            {
                std::cout << "These are the supported commands:\n"
                             " - 'h': Toggle help view\n"
                             " - '<-' and '->': Rotate camera\n"
                             " - 'Alt+Enter': Toggle fullscreen\n"
                             " - 'ESC': Quit"
                             "\n";
            }

            if (key == MRPTK_RIGHT)
                win.setCameraAzimuthDeg(win.getCameraAzimuthDeg() + 5);
            if (key == MRPTK_LEFT)
                win.setCameraAzimuthDeg(win.getCameraAzimuthDeg() - 5);
        }
    };
}

// ------------------------------------------------------
//                      MAIN
// ------------------------------------------------------
int main()
{
    try
    {
        TestDisplay3D();
        // leave time for the window to close
        std::this_thread::sleep_for(50ms);
        return 0;
    }
    catch (const std::exception& e)
    {
        std::cerr << "MRPT error: " << mrpt::exception_to_str(e) << std::endl;
        return -1;
    }
}