Skip to content

Commit

Permalink
Load controller 3D models even with multiple interaction profiles
Browse files Browse the repository at this point in the history
Now that hand interaction profile is available for some devices it
might happen that the user uses it at start time. That profile, as
it's meant to be used for hand tracking, does not require any
3D model (the hand 3D model uses hand tracking joints that are
not related to the hand interaction profile). This means that the
callback of OnControllersReady() in BrowserWorld will not load
any 3D model, and then DeviceDelegateOpenXR will uninstall the
callback. Then if the user ever grabs a physical controller,
the interaction profile will be updated (as we get notified by
the runtime), but no 3D models would be ever loaded for those
controllers as the callback is now null.

In order to fix that, the callback for OnControllersReady now
returns a boolean representing whether or not the 3D models
where loaded. If it's true then we can uninstall the callback
after completion. If it's false then we leave the callback
ready for eventual interaction profile changes.

Note that this do not support having multiple physical controllers
for a single device, but that scenario is unlikely to happen.

We have also moved the callback signature to a typedef, that
is not really required but it's better for robustness and
maintenance as it allows us to modify it in a single place.

Fixes #1445
  • Loading branch information
svillar committed Jun 18, 2024
1 parent dcf7947 commit d462d15
Show file tree
Hide file tree
Showing 4 changed files with 14 additions and 6 deletions.
4 changes: 4 additions & 0 deletions app/src/main/cpp/BrowserWorld.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -977,6 +977,7 @@ BrowserWorld::InitializeJava(JNIEnv* aEnv, jobject& aActivity, jobject& aAssetMa

if (!m.modelsLoaded) {
m.device->OnControllersReady([this](){
bool loadStarted = false;
const int32_t modelCount = m.device->GetControllerModelCount();
for (int32_t index = 0; index < modelCount; index++) {
vrb::LoadTask task = m.device->GetControllerModelTask(index);
Expand All @@ -986,15 +987,18 @@ BrowserWorld::InitializeJava(JNIEnv* aEnv, jobject& aActivity, jobject& aAssetMa
// we need to do the model load right when the controller becomes available)
m.controllers->SetControllerModelTask(index, task);
m.controllers->LoadControllerModel(index);
loadStarted = true;
} else {
const std::string fileName = m.device->GetControllerModelName(index);
if (!fileName.empty()) {
m.controllers->LoadControllerModel(index, m.loader, fileName);
loadStarted = true;
}
}
}
if (m.device->IsControllerLightEnabled())
m.rootController->AddLight(m.light);
return loadStarted;
});

VRBrowser::CheckTogglePassthrough();
Expand Down
3 changes: 2 additions & 1 deletion app/src/main/cpp/DeviceDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,8 @@ class DeviceDelegate {
virtual bool IsControllerLightEnabled() const { return true; }
virtual vrb::LoadTask GetControllerModelTask(int32_t index) { return nullptr; } ;
virtual void OnControllersCreated(std::function<void()> callback) { callback(); }
virtual void OnControllersReady(const std::function<void()>& callback) {
typedef std::function<bool()> ControllersReadyCallback;
virtual void OnControllersReady(const ControllersReadyCallback& callback) {
callback();
}
class ReorientClient {
Expand Down
11 changes: 7 additions & 4 deletions app/src/openxr/cpp/DeviceDelegateOpenXR.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ struct DeviceDelegateOpenXR::State {
device::DeviceType deviceType = device::UnknownType;
std::vector<const XrCompositionLayerBaseHeader*> frameEndLayers;
std::function<void()> controllersCreatedCallback;
std::function<void()> controllersReadyCallback;
std::function<bool()> controllersReadyCallback;
std::optional<XrPosef> firstPose;
bool mHandTrackingSupported = false;
std::vector<float> refreshRates;
Expand Down Expand Up @@ -756,8 +756,11 @@ struct DeviceDelegateOpenXR::State {
ASSERT(input);
if (!controllersReadyCallback || !input->AreControllersReady())
return;
controllersReadyCallback();
controllersReadyCallback = nullptr;
// Invoke the callback. We only clear it if the load has started. Otherwise we will retry later.
// This happens for example if a profile with no 3D models like hand interaction is loaded on
// start. If we clear the callback it won't ever be called in case the user grabs a controller.
if (controllersReadyCallback())
controllersReadyCallback = nullptr;
}

void UpdateInteractionProfile() {
Expand Down Expand Up @@ -898,7 +901,7 @@ void DeviceDelegateOpenXR::OnControllersCreated(std::function<void()> callback)
}

void
DeviceDelegateOpenXR::OnControllersReady(const std::function<void()>& callback) {
DeviceDelegateOpenXR::OnControllersReady(const ControllersReadyCallback& callback) {
if (m.input && m.input->AreControllersReady()) {
callback();
return;
Expand Down
2 changes: 1 addition & 1 deletion app/src/openxr/cpp/DeviceDelegateOpenXR.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ class DeviceDelegateOpenXR : public DeviceDelegate {
const std::string GetControllerModelName(const int32_t aModelIndex) const override;
bool IsPositionTrackingSupported() const override;
void OnControllersCreated(std::function<void()> callback) override;
void OnControllersReady(const std::function<void()>& callback) override;
void OnControllersReady(const ControllersReadyCallback& callback) override;
void SetCPULevel(const device::CPULevel aLevel) override;
void ProcessEvents() override;
bool SupportsFramePrediction(FramePrediction aPrediction) const override;
Expand Down

0 comments on commit d462d15

Please sign in to comment.