Skip to content

Commit

Permalink
Implement XR_FB_space_warp extension
Browse files Browse the repository at this point in the history
  • Loading branch information
devloglogan committed Oct 28, 2024
1 parent dbc2180 commit c6a129b
Show file tree
Hide file tree
Showing 13 changed files with 9,101 additions and 210 deletions.
4 changes: 4 additions & 0 deletions demo/.editorconfig
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
root = true

[*]
charset = utf-8
Binary file added demo/bone-cube.glb
Binary file not shown.
37 changes: 37 additions & 0 deletions demo/bone-cube.glb.import
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
[remap]

importer="scene"
importer_version=1
type="PackedScene"
uid="uid://detbvmjyhigan"
path="res://.godot/imported/bone-cube.glb-853e452a3ff35207a1fee3eefca16cf0.scn"

[deps]

source_file="res://bone-cube.glb"
dest_files=["res://.godot/imported/bone-cube.glb-853e452a3ff35207a1fee3eefca16cf0.scn"]

[params]

nodes/root_type=""
nodes/root_name=""
nodes/apply_root_scale=true
nodes/root_scale=1.0
nodes/import_as_skeleton_bones=false
nodes/use_node_type_suffixes=true
meshes/ensure_tangents=true
meshes/generate_lods=true
meshes/create_shadow_meshes=true
meshes/light_baking=1
meshes/lightmap_texel_size=0.2
meshes/force_disable_compression=false
skins/use_named_skins=true
animation/import=true
animation/fps=30
animation/trimming=false
animation/remove_immutable_tracks=true
animation/import_rest_as_RESET=false
import_script/path=""
_subresources={}
gltf/naming_version=1
gltf/embedded_image_handling=1
8 changes: 8 additions & 0 deletions demo/bone_cube.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
extends Node3D

@onready var skeleton := $Armature/Skeleton3D

func _physics_process(delta: float) -> void:
var new_basis := basis.rotated(Vector3.FORWARD, TAU * (Time.get_ticks_msec() / 2000.0))
var quat := Quaternion(new_basis)
skeleton.set_bone_pose_rotation(0, quat)
7 changes: 7 additions & 0 deletions demo/bone_cube.tscn
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[gd_scene load_steps=3 format=3 uid="uid://cfbmcnbws28t1"]

[ext_resource type="PackedScene" uid="uid://detbvmjyhigan" path="res://bone-cube.glb" id="1_fud8w"]
[ext_resource type="Script" path="res://bone_cube.gd" id="2_n023m"]

[node name="BoneCube" instance=ExtResource("1_fud8w")]
script = ExtResource("2_n023m")
63 changes: 62 additions & 1 deletion demo/main.tscn
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
[gd_scene load_steps=10 format=3 uid="uid://cqsodpswgup8w"]
[gd_scene load_steps=17 format=3 uid="uid://cqsodpswgup8w"]

[ext_resource type="Script" path="res://main.gd" id="1_fsva1"]
[ext_resource type="Script" path="res://multi_mesh_move.gd" id="3_h2yge"]
[ext_resource type="PackedScene" uid="uid://cfbmcnbws28t1" path="res://bone_cube.tscn" id="4_1bvp3"]
[ext_resource type="Material" uid="uid://bdwh0vc86hsdb" path="res://assets/hand_silhouette_outline_mat.tres" id="7_tpkib"]

[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_0x6cv"]
Expand Down Expand Up @@ -30,6 +32,24 @@ size = Vector3(0.1, 0.1, 0.1)
material = SubResource("StandardMaterial3D_k604q")
size = Vector2(10, 10)

[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_yvvw4"]
spread = 0.0
initial_velocity_min = 3.0
initial_velocity_max = 3.0
gravity = Vector3(0, 0, 0)

[sub_resource type="BoxMesh" id="BoxMesh_t5jnj"]

[sub_resource type="BoxMesh" id="BoxMesh_0xm2m"]

[sub_resource type="BoxMesh" id="BoxMesh_h2yge"]

[sub_resource type="MultiMesh" id="MultiMesh_1bvp3"]
transform_format = 1
instance_count = 1
mesh = SubResource("BoxMesh_h2yge")
buffer = PackedFloat32Array(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)

[node name="Main" type="Node3D"]
script = ExtResource("1_fsva1")

Expand Down Expand Up @@ -114,5 +134,46 @@ hand_tracker = &"/user/hand_tracker/right"
[node name="Floor" type="MeshInstance3D" parent="."]
mesh = SubResource("PlaneMesh_mjcgt")

[node name="GPUParticles3D" type="GPUParticles3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -1, 1, -4)
amount = 2
process_material = SubResource("ParticleProcessMaterial_yvvw4")
draw_pass_1 = SubResource("BoxMesh_t5jnj")

[node name="Label3D" type="Label3D" parent="GPUParticles3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -1, 0, 0)
text = "GPU
Particles"

[node name="CPUParticles3D" type="CPUParticles3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -1, 2.21267, -4)
amount = 2
mesh = SubResource("BoxMesh_0xm2m")
spread = 0.0
gravity = Vector3(0, 0, 0)
initial_velocity_min = 3.0
initial_velocity_max = 3.0

[node name="Label3D" type="Label3D" parent="CPUParticles3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -1, 0, 0)
text = "CPU
Particles"

[node name="MultiMeshInstance3D" type="MultiMeshInstance3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 3.40488, -4)
multimesh = SubResource("MultiMesh_1bvp3")
script = ExtResource("3_h2yge")

[node name="Label3D" type="Label3D" parent="MultiMeshInstance3D"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -2, 0, 0)
text = "MultiMesh"

[node name="BoneCube" parent="." instance=ExtResource("4_1bvp3")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3, 1, -2)

[node name="Label3D" type="Label3D" parent="BoneCube"]
transform = Transform3D(0.5, 0, 0.866025, 0, 1, 0, -0.866025, 0, 0.5, 0, 1, 0)
text = "Skeleton"

[connection signal="openxr_fb_render_model_loaded" from="XROrigin3D/LeftHand/LeftControllerFbRenderModel" to="." method="_on_left_controller_fb_render_model_render_model_loaded"]
[connection signal="openxr_fb_render_model_loaded" from="XROrigin3D/RightHand/RightControllerFbRenderModel" to="." method="_on_right_controller_fb_render_model_render_model_loaded"]
6 changes: 6 additions & 0 deletions demo/multi_mesh_move.gd
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
extends MultiMeshInstance3D

func _physics_process(delta: float) -> void:
var origin := Vector3(sin(Time.get_ticks_msec() / 500.0), 0, 0)
var new_transform := Transform3D(basis, origin)
multimesh.set_instance_transform(0, new_transform)
2 changes: 1 addition & 1 deletion demo/project.godot
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ config_version=5

config/name="OpenXR Demo"
run/main_scene="res://main.tscn"
config/features=PackedStringArray("4.3", "GL Compatibility")
config/features=PackedStringArray("4.4", "GL Compatibility")
config/icon="res://icon.svg"

[debug]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
/**************************************************************************/
/* openxr_fb_space_warp_extension_wrapper.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT XR */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2022-present Godot XR contributors (see CONTRIBUTORS.md) */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/

#include "extensions/openxr_fb_space_warp_extension_wrapper.h"

#include <godot_cpp/classes/open_xr_interface.hpp>
#include <godot_cpp/classes/open_xrapi_extension.hpp>
#include <godot_cpp/classes/xr_server.hpp>

#define GL_RGBA16F 0x881A
#define GL_DEPTH24_STENCIL8 0x88F0

OpenXRFbSpaceWarpExtensionWrapper *OpenXRFbSpaceWarpExtensionWrapper::singleton = nullptr;

OpenXRFbSpaceWarpExtensionWrapper *OpenXRFbSpaceWarpExtensionWrapper::get_singleton() {
if (singleton == nullptr) {
singleton = memnew(OpenXRFbSpaceWarpExtensionWrapper());
}
return singleton;
}

OpenXRFbSpaceWarpExtensionWrapper::OpenXRFbSpaceWarpExtensionWrapper() :
OpenXRExtensionWrapperExtension() {
ERR_FAIL_COND_MSG(singleton != nullptr, "An OpenXRFbSpaceWarpExtensionWrapper singleton already exists.");

request_extensions[XR_FB_SPACE_WARP_EXTENSION_NAME] = &fb_space_warp_ext;
singleton = this;
}

OpenXRFbSpaceWarpExtensionWrapper::~OpenXRFbSpaceWarpExtensionWrapper() {
singleton = nullptr;
}

godot::Dictionary OpenXRFbSpaceWarpExtensionWrapper::_get_requested_extensions() {
godot::Dictionary result;
for (auto ext : request_extensions) {
godot::String key = ext.first;
uint64_t value = reinterpret_cast<uint64_t>(ext.second);
result[key] = (godot::Variant)value;
}
return result;
}

uint64_t OpenXRFbSpaceWarpExtensionWrapper::_set_system_properties_and_get_next_pointer(void *p_next_pointer) {
if (fb_space_warp_ext) {
system_space_warp_properties.next = p_next_pointer;
return reinterpret_cast<uint64_t>(&system_space_warp_properties);
} else {
return reinterpret_cast<uint64_t>(p_next_pointer);
}
}

uint64_t OpenXRFbSpaceWarpExtensionWrapper::_set_projection_views_and_get_next_pointer(int p_view_index, void *p_next_pointer) {
if (enabled) {
return reinterpret_cast<uint64_t>(&space_warp_info[p_view_index]);
} else {
return 0;
}
}

void OpenXRFbSpaceWarpExtensionWrapper::_on_session_created(uint64_t p_instance) {
if (fb_space_warp_ext) {
get_openxr_api()->register_projection_views_extension(this);
}
}

void OpenXRFbSpaceWarpExtensionWrapper::_on_session_destroyed() {
if (fb_space_warp_ext) {
get_openxr_api()->unregister_projection_views_extension(this);
}
}

void OpenXRFbSpaceWarpExtensionWrapper::_on_state_ready() {
if (!fb_space_warp_ext) {
return;
}

Ref<OpenXRInterface> openxr_interface = XRServer::get_singleton()->find_interface("OpenXR");
int view_count = openxr_interface->get_view_count();
int width = system_space_warp_properties.recommendedMotionVectorImageRectWidth;
int height = system_space_warp_properties.recommendedMotionVectorImageRectHeight;

motion_vector_swapchain_info = get_openxr_api()->openxr_swap_chain_info_create(0, XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_COLOR_ATTACHMENT_BIT, GL_RGBA16F, width, height, 1, view_count);
motion_vector_depth_swapchain_info = get_openxr_api()->openxr_swap_chain_info_create(0, XR_SWAPCHAIN_USAGE_SAMPLED_BIT | XR_SWAPCHAIN_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT, GL_DEPTH24_STENCIL8, width, height, 1, view_count);
}

void OpenXRFbSpaceWarpExtensionWrapper::_on_main_swapchains_created() {
if (!fb_space_warp_ext) {
return;
}

Ref<OpenXRInterface> openxr_interface = XRServer::get_singleton()->find_interface("OpenXR");
int view_count = openxr_interface->get_view_count();

XrPosef identity_pose = { { 0.0, 0.0, 0.0, 1.0 }, { 0.0, 0.0, 0.0 } };

space_warp_info = memnew_arr(XrCompositionLayerSpaceWarpInfoFB, 2);
for (int i = 0; i < view_count; i++) {
space_warp_info[i].type = XR_TYPE_COMPOSITION_LAYER_SPACE_WARP_INFO_FB;

space_warp_info[i].next = nullptr;

// @todo Provide a way to set XR_COMPOSITION_LAYER_SPACE_WARP_INFO_FRAME_SKIP_BIT_FB.
space_warp_info[i].layerFlags = 0;

space_warp_info[i].motionVectorSubImage.swapchain = (XrSwapchain)get_openxr_api()->openxr_swap_chain_info_get_swapchain(motion_vector_swapchain_info);
space_warp_info[i].motionVectorSubImage.imageRect.offset.x = 0;
space_warp_info[i].motionVectorSubImage.imageRect.offset.y = 0;
space_warp_info[i].motionVectorSubImage.imageRect.extent.width = system_space_warp_properties.recommendedMotionVectorImageRectWidth;
space_warp_info[i].motionVectorSubImage.imageRect.extent.height = system_space_warp_properties.recommendedMotionVectorImageRectHeight;
space_warp_info[i].motionVectorSubImage.imageArrayIndex = i;

// @todo Update this to account for locomotion.
space_warp_info[i].appSpaceDeltaPose = identity_pose;

space_warp_info[i].depthSubImage.swapchain = (XrSwapchain)get_openxr_api()->openxr_swap_chain_info_get_swapchain(motion_vector_depth_swapchain_info);
space_warp_info[i].depthSubImage.imageRect.offset.x = 0;
space_warp_info[i].depthSubImage.imageRect.offset.y = 0;
space_warp_info[i].depthSubImage.imageRect.extent.width = system_space_warp_properties.recommendedMotionVectorImageRectWidth;
space_warp_info[i].depthSubImage.imageRect.extent.height = system_space_warp_properties.recommendedMotionVectorImageRectHeight;
space_warp_info[i].depthSubImage.imageArrayIndex = i;

space_warp_info[i].minDepth = 0.0;
space_warp_info[i].maxDepth = 1.0;

space_warp_info[i].farZ = get_openxr_api()->get_render_state_z_near();
space_warp_info[i].nearZ = get_openxr_api()->get_render_state_z_far();
}
}

void OpenXRFbSpaceWarpExtensionWrapper::_on_pre_render() {
if (!fb_space_warp_ext) {
return;
}

get_openxr_api()->openxr_swap_chain_info_acquire(motion_vector_swapchain_info);
get_openxr_api()->openxr_swap_chain_info_acquire(motion_vector_depth_swapchain_info);

RID motion_vector_swapchain_image = get_openxr_api()->openxr_swap_chain_info_get_image(motion_vector_swapchain_info);
get_openxr_api()->set_motion_vector_texture(motion_vector_swapchain_image);
RID motion_vector_depth_swapchain_image = get_openxr_api()->openxr_swap_chain_info_get_image(motion_vector_depth_swapchain_info);
get_openxr_api()->set_motion_vector_depth_texture(motion_vector_depth_swapchain_image);

int target_width = system_space_warp_properties.recommendedMotionVectorImageRectWidth;
int target_height = system_space_warp_properties.recommendedMotionVectorImageRectHeight;
Size2i render_target_size = { target_width, target_height };

get_openxr_api()->set_motion_vector_target_size(render_target_size);

Ref<OpenXRInterface> openxr_interface = XRServer::get_singleton()->find_interface("OpenXR");
int view_count = openxr_interface->get_view_count();
for (int i = 0; i < view_count; i++) {
space_warp_info[i].farZ = get_openxr_api()->get_render_state_z_near();
space_warp_info[i].nearZ = get_openxr_api()->get_render_state_z_far();
}
}

void OpenXRFbSpaceWarpExtensionWrapper::_on_post_draw_viewport(const RID &p_render_target) {
get_openxr_api()->openxr_swap_chain_info_release(motion_vector_swapchain_info);
get_openxr_api()->openxr_swap_chain_info_release(motion_vector_depth_swapchain_info);
}

bool OpenXRFbSpaceWarpExtensionWrapper::is_available() {
return available;
}

void OpenXRFbSpaceWarpExtensionWrapper::set_space_warp_enabled(bool p_enable) {
enabled = p_enable;
}

void OpenXRFbSpaceWarpExtensionWrapper::_bind_methods() {
ClassDB::bind_method(D_METHOD("set_space_warp_enabled", "enable"), &OpenXRFbSpaceWarpExtensionWrapper::set_space_warp_enabled);
}
Loading

0 comments on commit c6a129b

Please sign in to comment.