-
Notifications
You must be signed in to change notification settings - Fork 25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add OpenXR extension wrappers for fb_scene, fb_spatial_entity, fb_spatial_entity_query, fb_spatial_entity_container #66
Conversation
c7cbe0a
to
970d1aa
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good overall as a first step to add support for these extensions!
@BastiaanOlij @Malcolmnixon As a follow-up to this PR, we'll need to evaluate how we want to expose this functionality on the Godot side in a manner that's consistent.
/* Original contributed implementation: */ | ||
/* Copyright (c) 2022-2023 MattaKis Consulting Kft. (Migeran) */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can remove this section and instead use the pattern in https://github.com/GodotVR/godot_openxr_vendors/blob/master/godotopenxrmeta/src/main/cpp/register_types.h
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
updated all headers to match that pattern (also switched to #pragma once)
bool initialize_fb_spatial_entity_extension(const XrInstance instance); | ||
|
||
std::map<godot::String, bool *> request_extensions; | ||
std::map<XrAsyncRequestIdFB, SetSpaceComponentStatusCallback_t> set_status_callbacks; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: formatting issue?
|
||
bool initialize_fb_spatial_entity_query_extension(const XrInstance instance); | ||
void on_space_query_results(const XrEventDataSpaceQueryResultsAvailableFB* event); | ||
void on_space_query_complete(const XrEventDataSpaceQueryCompleteFB* event); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: here and below, formatting issues?
XrSpaceContainerFB spaceContainer = {XR_TYPE_SPACE_CONTAINER_FB}; | ||
xrGetSpaceContainerFB(SESSION, space, &spaceContainer); | ||
std::vector<XrUuidEXT> uuids(spaceContainer.uuidCountOutput); | ||
spaceContainer.uuidCapacityInput = uuids.size(); | ||
spaceContainer.uuids = uuids.data(); | ||
xrGetSpaceContainerFB(SESSION, space, &spaceContainer); | ||
return uuids; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: the indents used in this method are different from the ones used in the previous methods.
In Godot 3 we introduced Anchors based on how ARKit solved this, most of this logic is still in place but not all. So that part of the solution I think is sound and we should apply here too. I don't know the details of the flow Meta currently has but if any spatial location is detected, we should create a positional tracker and register it with the XRServer by calling Using What is missing is what to do after this, I wasn't at all happy with how we did things in Godot 3 so I left this out in the conversion to Godot 4. We need a mechanism to expose further meta data about the anchor and provide a collision shape and/or occlusion mesh. These could become |
addressed the formatting nits (now using tabs throughout, turned on whitespace rendering in my editor to catch these sooner lol). Is there / should there be a linter for this project? |
We use |
Added an export option to request the USE_SCENE permission as per https://developer.oculus.com/documentation/native/native-spatial-data-perm/ From a discussion in #68 the permission is already auto-requested by GodotActivity's existing logic |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good!
} | ||
|
||
void OpenXRFbSceneExtensionWrapper::_bind_methods() { | ||
ClassDB::bind_static_method("OpenXRFbSceneExtensionWrapper", D_METHOD("get_singleton"), &OpenXRFbSceneExtensionWrapper::get_singleton); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@m4gr3d @BastiaanOlij I know this is a pre-existing pattern from OpenXRFbSceneCaptureExtensionWrapper
which is already merged, but I'm curious how this pattern of registering a static get_singleton()
method came to be? Because this isn't how singletons are usually bound in Godot.
Usually, you'd register a singleton in register_types.cpp
via:
Engine::get_singleton()->register_singleton("OpenXRFbSceneExtensionWrapper", OpenXRFbSceneExtensionWrapper::get_singleton());
The pattern used here certainly works, but it leads to an unusual API from GDScript. If there isn't a compelling reason for this, I think we should switch to the usual way. I can open a separate issue about that, though! I just happened to notice it when reviewing this PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wasn't aware of the reigster_singleton(...)
approach; let's use that instead and update OpenXRFbSceneCaptureExtensionWrapper
accordingly!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dsnopek What's the earliest that Engine::get_singleton()->register_singleton(...)
can be used?
I tried invoking it during MODULE_INITIALIZATION_LEVEL_CORE
and I kept getting crash errors because the Engine
singleton was not available.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, doing Engine.get_singleton(...)
does not retrieve the singleton instance.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's a draft PR for reference: #72
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm getting segfaults when registering using this approach. Did a bit of digging, and I think it's due to Engine::get_singleton() not being available in a gdextension
ERROR: Failed to retrieve non-existent singleton 'Engine'.
at: get_singleton_object (core/config/engine.cpp:270)
ERROR: Parameter "singleton_obj" is null.
at: get_singleton (thirdparty/godot-cpp/gen/src/classes/engine.cpp:49)
Digging into the code, godot-cpp auto-generates bindings that eventually call into Engine::get_singleton_object(const StringName &p_name) and passes the class name (in this case "Engine"), but godot never actually registers Engine itself as a singleton in this form, within the godot/ source tree it's just a raw pointer, and since it doesn't extend Object it's not trivial to register like all the other Singleton instances. From godot/core/config/engine.cpp
Engine *Engine::singleton = nullptr;
Engine *Engine::get_singleton() {
return singleton;
}
I think we'd need to address that in the engine before we can use Engine::get_singleton() from an extension? In contrast, main.cpp registers Performance as follows: engine->add_singleton(Engine::Singleton("Performance", performance));
Not sure if you encountered something similar in #73 @dsnopek
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@maunvz There was some logic missing in register_types.cpp
that @dsnopek fixed in https://github.com/GodotVR/godot_openxr_vendors/pull/73/files#diff-aaccbe3504fdb5822961fde8149e985f5539985f9e60a4a31d804f3bf5feb5e4.
In the updated register_types.cpp
, the singleton registration is initialized at the MODULE_INITIALIZATION_LEVEL_SCENE
level and the minimum initialization level was also updated to MODULE_INITIALIZATION_LEVEL_SCENE
.
With those changes in place, registration via the engine works as expected.
struct XrSceneObjectInternal { | ||
XrUuidEXT uuid; | ||
XrSpace space; | ||
std::optional<std::string> label; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please use Godots implementations whenever possible to prevent needless conversions of data between internal data and Godot.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This collection of OpenXR extensions use XrSpace and XrUuidEXT across multiple API calls to request different metadata and poses (for example, once you've queried for all available anchors, you can locate that anchor or ask about it's semantic labels / bounding volumes, all with uuid / space). The intent here is to store in the original OpenXR formats to facilitate later calls associated with the same anchor, and translate to a Godot type in some higher level "XrSceneManager" that gets used by gdscript (rather than using these wrappers directly)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@maunvz The comment was referring to the use of the std::string
type, you can use godot::String
instead here and throughout this PR.
updated to remove unused constants, request the scene permission manually, based on #71, and use reigster_singleton(...) in the new additions. |
|
||
// Vertices and lines on a plane, probably use this for a floor / ceiling as they are irregularly shaped | ||
// We store a std::vector instead of XrBoundary2DFB to own the vertex memory | ||
std::optional<std::vector<XrVector2f>> boundary2D; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similarly, you can replace std::vector
with godot::Vector
throughout this PR
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
related: If I have "using namespace godot;" at the top, should I omit the godot:: prefix throughout the rest of the file (including in headers and variable types)?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, you can omit the godot::
prefix.
"TABLE,COUCH,FLOOR,CEILING,WALL_FACE,WINDOW_FRAME,DOOR_FRAME,STORAGE,BED,SCREEN,LAMP,PLANT,WALL_ART,INVISIBLE_WALL_FACE,OTHER"; | ||
const XrSemanticLabelsSupportInfoFB semanticLabelsSupportInfo = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this list also include GLOBAL_MESH
as mentioned in the documentation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@maunvz ping on this ^^
std::optional<std::string> get_semantic_labels(const XrSpace space); | ||
void get_shapes(const XrSpace space, XrSceneObjectInternal& object); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here and throughout the PR, we should use a const XrSpace &space
argument instead.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hm does that actually make a difference? the XrSpace, like many other XR handles, is just a pointer / uint64_t, not a full object. See openxr.h:
#if !defined(XR_DEFINE_HANDLE)
#if (XR_PTR_SIZE == 8)
#define XR_DEFINE_HANDLE(object) typedef struct object##_T* object;
#else
#define XR_DEFINE_HANDLE(object) typedef uint64_t object;
#endif
#endif
...
XR_DEFINE_HANDLE(XrSpace)
Will update to match the style of the rest of the codebase
updated XrSpace usage, removed unnecessary godot:: prefixes, switched to Godot types throughout, and switched to new singleton registration |
return std::nullopt; | ||
} | ||
|
||
static const std::string recognizedLabels = |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another usage of std:string
// First call. | ||
xrGetSpaceSemanticLabelsFB(SESSION, space, &labels); | ||
// Second call | ||
std::vector<char> labelData(labels.bufferCountOutput); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another usage of std::vector
bool OpenXRFbSpatialEntityExtensionWrapper::is_component_supported(const XrSpace& space, XrSpaceComponentTypeFB type) { | ||
uint32_t numComponents = 0; | ||
xrEnumerateSpaceSupportedComponentsFB(space, 0, &numComponents, nullptr); | ||
std::vector<XrSpaceComponentTypeFB> components(numComponents); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Another usage of std::vector
|
||
std::map<XrAsyncRequestIdFB, Vector<XrSpaceQueryResultFB>> query_results; | ||
std::map<String, bool *> request_extensions; | ||
std::map<XrAsyncRequestIdFB, SpaceQueryCompleteCallback_t> query_complete_callbacks; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You should be able to use godot::HashMap
here instead
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks good overall!
Just some minor nits regarding types to address before merging.
addressed final type nits, added missing SCENE_MESH semantic label |
Thanks for the contribution! |
As title, following the pattern from the fb_scene_capture extension wrapper, I've added wrappers for the 4 extensions mentioned. They don't yet expose useful functionality to godot projects (beyond getting the singletons and checking if the extensions are supported), but provide most of the openxr related boilterplate so a later change can add an "XR Scene" abstraction that expresses a physical room's layout and surfaces to the a Godot scene.