Рекурсивные обратные вызовы: первый обратный вызов отключает следующие обратные вызовы
Я запускаю приведение лучей в своем коде:
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);
}