Рекурсивные обратные вызовы: первый обратный вызов отключает следующие обратные вызовы

Я запускаю приведение лучей в своем коде:

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);

Результаты заклинателя лучей обрабатываются обратным вызовом / слотом, который рекурсивно выполняет приведение лучей:

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);
}

Проблема в том, когда обратный вызов / слот MySceneClass::handleRayCasterHits Возвращает, компонент Ray-Caster автоматически отключится, и больше не может быть выполнено тестирование наведения лучей. Это потому что RunMode установлен в SingleShot, как указано в документации.

Одним из решений является установка RunMode в Continuous, но это нежелательно, так как это делает наложение лучей непрерывно и излишне. Случайно, есть ли другое возможное решение, о котором я не знаю?

1 ответ

Возможно, это немного хакерский обходной путь, но вы можете настроить очередь лучей для тестирования:

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);
}

... и в обратном вызове raycaster вы просто ставите больше лучей в очередь:

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 предоставил ссылку на комментарий, который помог мне решить проблему таким образом, я все еще не уверен, является ли это лучшим способом ее решения:

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);
}
Другие вопросы по тегам