递归回调:第一个回调禁用后面的回调

Recursive callbacks: the first callback disables the following callbacks

我正在我的代码中触发光线投射:

m_rayCaster = new Qt3DRender::QRayCaster(m_rootEntity);
// Connect ray-caster signal to callback/slot
QObject::connect(m_rayCaster, &Qt3DRender::QRayCaster::hitsChanged, this, &MySceneClass::handleRayCasterHits);
// ...
// ...
m_rayCaster->trigger(origin, direction, length);

光线投射结果由 callback/slot 处理,它以递归方式再次进行光线投射:

void MySceneClass::handleRayCasterHits(const Qt3DRender::QAbstractRayCaster::Hits hits)
{
    // ...
    // Handle ray caster hits
    // ...

    // Condition to stop ray casting
    m_counter++;
    if ( m_counter >= m_size )  {
        return;
    }


    // Recursive ray casting: trigger ray casting again:
    m_rayCaster->trigger(origin, direction, length);
}

问题是当callback/slot MySceneClass::handleRayCasterHits returns时,光线投射器组件将自动禁用,并且无法再执行光线投射测试。那是因为 RunMode 设置为 SingleShot,如 documentation.

中所述

一种解决方案是将 RunMode 设置为 Continuous,但这是不可取的,因为它会连续且不必要地进行光线投射。有没有其他我不知道的可能的解决方案?

可能有点老套的解决方法,但您可以设置一个 队列 待测试的光线:

struct Ray {
    QVector3D origin;
    QVector3D direction;
    float length;
};

std::deque<Ray> m_raysEnqueued;

然后,您可以通过将新光线推入队列来启动光线追踪:

m_raysEnqueued.push_back({ origin, direction, length });

在你的帧回调中,你检查队列并处理光线直到它为空:

while (!m_raysEnqueued.empty()) {
    Ray r = m_raysEnqueued.pop_front();
    m_rayCaster->trigger(r.origin, r.direction, r.length);
}

...在光线投射器的回调中,您只需将更多光线加入队列即可:

void MySceneClass::handleRayCasterHits(const Qt3DRender::QAbstractRayCaster::Hits hits)
{
    // ...
    // Handle ray caster hits
    // ...

    // Recursive ray casting: trigger ray casting again:
    m_raysEnqueued.push_back(Ray(origin, direction, length));
}

@UKMonkey 在评论中提供了 link 帮助我以这种方式解决问题,我仍然不确定这是否是解决问题的最佳方式:

void MySceneClass::handleRayCasterHits(const Qt3DRender::QAbstractRayCaster::Hits hits)
{
    // ...
    // Handle ray caster hits
    // ...

    // // Wait 1 milli-second, then suggest doing next possible ray casting
    // // We wait for QRayCaster to be disabled, before doing the next ray casting
    QTimer::singleShot(1, this, &MySceneClass::handleRayCasterFinish);
}

这个新插槽实际上触发了下一个可能的光线投射:

void MySceneClass::handleRayCasterFinish()
{

    while ( m_rayCaster->isEnabled() ) {
        qDebug() << __func__ << "Wait for ray caster to be disabled by the previous ray casting ... enabled: " << m_rayCaster->isEnabled();
        // Above debug message never gets logged, so I guess waiting for 1 milli-second is already enough
    }

    // Condition to stop ray casting
    m_counter++;
    if ( m_counter >= m_size ) return;

    // ...

    // Now we are sure that ray caster is disabled by previous ray casting test, therefore we can trigger the next ray casting test
    m_rayCaster->trigger(origin, direction, length);
}