diff --git a/xr/openxr_render_models/.gitattributes b/xr/openxr_render_models/.gitattributes new file mode 100644 index 00000000..8ad74f78 --- /dev/null +++ b/xr/openxr_render_models/.gitattributes @@ -0,0 +1,2 @@ +# Normalize EOL for all files that Git considers text files. +* text=auto eol=lf diff --git a/xr/openxr_render_models/.gitignore b/xr/openxr_render_models/.gitignore new file mode 100644 index 00000000..f34cd888 --- /dev/null +++ b/xr/openxr_render_models/.gitignore @@ -0,0 +1,4 @@ +# Godot 4+ specific ignores +.godot/ +.editorconfig +/android/ diff --git a/xr/openxr_render_models/README.md b/xr/openxr_render_models/README.md new file mode 100644 index 00000000..84fcbf25 --- /dev/null +++ b/xr/openxr_render_models/README.md @@ -0,0 +1,50 @@ +# OpenXR Render models demo + +This is a demo showing OpenXR's render models implementation. + +Language: GDScript + +Renderer: Compatibility + +> [!NOTE] +> +> This demo requires Godot 4.5 or later + +## Screenshots + +![Screenshot](screenshots/render_model_demo.png) + +## How does it work? + +OpenXR allows us to run our application without having knowledge of the hardware being used, +or that we as developers have access too at the time of developing our application. + +As a result we don't have direct information telling us what hardware is being used, +however there are situations where we want to visually show this hardware. + +This specifically applies to the controllers used by the user, as showing the correct hardware +improves the user's sense of immersion. + +The render model API allows us to enumerate the devices currently in use and then query +information such as its 3D asset, its position and orientation in space, and the position +and orientation of individual components of the asset. + +Godot's implementation hides most of the complexity of this through the OpenXRRenderModelManager +node as a child of the XROrigin3D node. You can add just this node by itself and let it show +all render models that are currently active, or like we do in this demo, you can add nodes +in the tree of each controller to show render models related to that controller. + +## Action map + +This demo project has a barebones action map as we're only dealing with positioning. + +## Running on PCVR + +This project can be run as normal for PCVR. Ensure that an OpenXR runtime has been installed. + +## Running on standalone VR + +You must install the Android build templates and OpenXR loader plugin and configure an export template for your device. +Please follow [the instructions for deploying on Android in the manual](https://docs.godotengine.org/en/stable/tutorials/xr/deploying_to_android.html). + + diff --git a/xr/openxr_render_models/assets/pattern.png b/xr/openxr_render_models/assets/pattern.png new file mode 100644 index 00000000..8bf420b0 Binary files /dev/null and b/xr/openxr_render_models/assets/pattern.png differ diff --git a/xr/openxr_render_models/assets/pattern.png.import b/xr/openxr_render_models/assets/pattern.png.import new file mode 100644 index 00000000..59a8cff0 --- /dev/null +++ b/xr/openxr_render_models/assets/pattern.png.import @@ -0,0 +1,41 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://btobyv4xjhltq" +path.s3tc="res://.godot/imported/pattern.png-cf6f03dfd1cdd4bc35da3414e912103d.s3tc.ctex" +metadata={ +"imported_formats": ["s3tc_bptc"], +"vram_texture": true +} + +[deps] + +source_file="res://assets/pattern.png" +dest_files=["res://.godot/imported/pattern.png-cf6f03dfd1cdd4bc35da3414e912103d.s3tc.ctex"] + +[params] + +compress/mode=2 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=true +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=0 diff --git a/xr/openxr_render_models/box.tscn b/xr/openxr_render_models/box.tscn new file mode 100644 index 00000000..3ee65e99 --- /dev/null +++ b/xr/openxr_render_models/box.tscn @@ -0,0 +1,21 @@ +[gd_scene load_steps=5 format=3 uid="uid://cfgfwisj8m2mg"] + +[ext_resource type="Texture2D" uid="uid://btobyv4xjhltq" path="res://assets/pattern.png" id="1_g2tbl"] + +[sub_resource type="BoxShape3D" id="BoxShape3D_oq5cr"] + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_bqn3j"] +albedo_color = Color(0.16217351, 0.546357, 0.5366536, 1) +albedo_texture = ExtResource("1_g2tbl") +uv1_scale = Vector3(3, 3, 3) + +[sub_resource type="BoxMesh" id="BoxMesh_3n43a"] +material = SubResource("StandardMaterial3D_bqn3j") + +[node name="Box" type="RigidBody3D"] + +[node name="CollisionShape3D" type="CollisionShape3D" parent="."] +shape = SubResource("BoxShape3D_oq5cr") + +[node name="MeshInstance3D" type="MeshInstance3D" parent="."] +mesh = SubResource("BoxMesh_3n43a") diff --git a/xr/openxr_render_models/collision_hands.gd b/xr/openxr_render_models/collision_hands.gd new file mode 100644 index 00000000..62653b4d --- /dev/null +++ b/xr/openxr_render_models/collision_hands.gd @@ -0,0 +1,19 @@ +class_name CollisionHands3D +extends AnimatableBody3D + +func _ready(): + # Make sure these are set correctly. + top_level = true + sync_to_physics = false + process_physics_priority = -90 + + +func _physics_process(_delta): + # Follow our parent node around. + var dest_transform = get_parent().global_transform + + # We just apply rotation for this example. + global_basis = dest_transform.basis + + # Attempt to move to where our tracked hand is. + move_and_collide(dest_transform.origin - global_position) diff --git a/xr/openxr_render_models/collision_hands.gd.uid b/xr/openxr_render_models/collision_hands.gd.uid new file mode 100644 index 00000000..09f8a7af --- /dev/null +++ b/xr/openxr_render_models/collision_hands.gd.uid @@ -0,0 +1 @@ +uid://c5bnmb8grcumh diff --git a/xr/openxr_render_models/icon.svg b/xr/openxr_render_models/icon.svg new file mode 100644 index 00000000..c6bbb7d8 --- /dev/null +++ b/xr/openxr_render_models/icon.svg @@ -0,0 +1 @@ + diff --git a/xr/openxr_render_models/icon.svg.import b/xr/openxr_render_models/icon.svg.import new file mode 100644 index 00000000..b7e13c90 --- /dev/null +++ b/xr/openxr_render_models/icon.svg.import @@ -0,0 +1,43 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://bi6bu4ng584fq" +path="res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://icon.svg" +dest_files=["res://.godot/imported/icon.svg-218a8f2b3041327d8a5756f3a245f83b.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 +svg/scale=1.0 +editor/scale_with_editor_scale=false +editor/convert_colors_with_editor_theme=false diff --git a/xr/openxr_render_models/main.tscn b/xr/openxr_render_models/main.tscn new file mode 100644 index 00000000..86a2d2da --- /dev/null +++ b/xr/openxr_render_models/main.tscn @@ -0,0 +1,122 @@ +[gd_scene load_steps=13 format=3 uid="uid://btli1dajen53o"] + +[ext_resource type="Script" uid="uid://bqa4r4n7b6d7s" path="res://start_vr.gd" id="1_1bvp3"] +[ext_resource type="Script" uid="uid://c5bnmb8grcumh" path="res://collision_hands.gd" id="1_ig7tw"] +[ext_resource type="Texture2D" uid="uid://btobyv4xjhltq" path="res://assets/pattern.png" id="2_h2yge"] +[ext_resource type="PackedScene" uid="uid://cfgfwisj8m2mg" path="res://box.tscn" id="3_1bvp3"] +[ext_resource type="PackedScene" uid="uid://c7ohc2o1shtu7" path="res://wall.tscn" id="5_lquwl"] + +[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_7dm0k"] +sky_horizon_color = Color(0.662243, 0.671743, 0.686743, 1) +ground_horizon_color = Color(0.662243, 0.671743, 0.686743, 1) + +[sub_resource type="Sky" id="Sky_ig7tw"] +sky_material = SubResource("ProceduralSkyMaterial_7dm0k") + +[sub_resource type="Environment" id="Environment_0xm2m"] +background_mode = 2 +sky = SubResource("Sky_ig7tw") +tonemap_mode = 2 + +[sub_resource type="SphereShape3D" id="SphereShape3D_0xm2m"] +radius = 0.02 + +[sub_resource type="WorldBoundaryShape3D" id="WorldBoundaryShape3D_lquwl"] + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_7mycd"] +albedo_color = Color(0.3241276, 0.50004333, 0.2159737, 1) +albedo_texture = ExtResource("2_h2yge") +uv1_scale = Vector3(100, 100, 100) + +[sub_resource type="PlaneMesh" id="PlaneMesh_272bh"] +material = SubResource("StandardMaterial3D_7mycd") +size = Vector2(1000, 1000) + +[node name="Main" type="Node3D"] + +[node name="StartVR" type="Node3D" parent="."] +script = ExtResource("1_1bvp3") + +[node name="DirectionalLight3D" type="DirectionalLight3D" parent="."] +transform = Transform3D(-0.866025, -0.433013, 0.25, 0, 0.5, 0.866025, -0.5, 0.75, -0.433013, 0, 0, 0) +shadow_enabled = true +shadow_bias = 0.01 +directional_shadow_max_distance = 50.0 + +[node name="WorldEnvironment" type="WorldEnvironment" parent="."] +environment = SubResource("Environment_0xm2m") + +[node name="XROrigin3D" type="XROrigin3D" parent="."] + +[node name="XRCamera3D" type="XRCamera3D" parent="XROrigin3D"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.7, 0) + +[node name="OpenXRRenderManager" type="OpenXRRenderModelManager" parent="XROrigin3D"] +tracker = 1 + +[node name="LeftHand" type="XRController3D" parent="XROrigin3D"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.5, 1, -0.5) +tracker = &"left_hand" +pose = &"grip" + +[node name="CollisionHands3D" type="AnimatableBody3D" parent="XROrigin3D/LeftHand"] +script = ExtResource("1_ig7tw") +metadata/_custom_type_script = "uid://c5bnmb8grcumh" + +[node name="OpenXRRenderManager" type="OpenXRRenderModelManager" parent="XROrigin3D/LeftHand/CollisionHands3D"] +tracker = 2 +make_local_to_pose = "grip" + +[node name="CollisionShape3D" type="CollisionShape3D" parent="XROrigin3D/LeftHand/CollisionHands3D"] +shape = SubResource("SphereShape3D_0xm2m") + +[node name="RightHand" type="XRController3D" parent="XROrigin3D"] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 1, -0.5) +tracker = &"right_hand" +pose = &"grip" + +[node name="CollisionHands3D" type="AnimatableBody3D" parent="XROrigin3D/RightHand"] +script = ExtResource("1_ig7tw") +metadata/_custom_type_script = "uid://c5bnmb8grcumh" + +[node name="OpenXRRenderManager" type="OpenXRRenderModelManager" parent="XROrigin3D/RightHand/CollisionHands3D"] +tracker = 3 +make_local_to_pose = "grip" + +[node name="CollisionShape3D" type="CollisionShape3D" parent="XROrigin3D/RightHand/CollisionHands3D"] +shape = SubResource("SphereShape3D_0xm2m") + +[node name="Floor" type="StaticBody3D" parent="."] + +[node name="CollisionShape3D" type="CollisionShape3D" parent="Floor"] +shape = SubResource("WorldBoundaryShape3D_lquwl") + +[node name="MeshInstance3D" type="MeshInstance3D" parent="Floor"] +mesh = SubResource("PlaneMesh_272bh") + +[node name="Box" parent="." instance=ExtResource("3_1bvp3")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.75, 0.5, -0.75) + +[node name="Wall01" parent="." instance=ExtResource("5_lquwl")] +transform = Transform3D(0.5077975, 0.073525466, 0.8583331, -0.14741239, 0.989072, 0.0024858303, -0.8487705, -0.12779121, 0.5130869, -1.3576927, -0.4236443, 0) + +[node name="Wall02" parent="." instance=ExtResource("5_lquwl")] +transform = Transform3D(-0.4400646, -0.09660582, 0.89275444, 0, 0.9941961, 0.10758293, -0.89796615, 0.047343437, -0.43751052, 2.5047212, -0.106753826, 0.0325222) + +[node name="Wall03" parent="." instance=ExtResource("5_lquwl")] +transform = Transform3D(0.584169, -0.048437964, 0.8101854, -0.1719119, 0.9681845, 0.181838, -0.7932168, -0.24550463, 0.5572562, 2.4887834, -0.7894809, 2.6100364) + +[node name="Wall04" parent="." instance=ExtResource("5_lquwl")] +transform = Transform3D(-0.63450754, -0.2243154, 0.7396504, -0.12261418, 0.9740546, 0.19021934, -0.763129, 0.030003972, -0.64554924, -1.2827711, -0.22003722, 2.6100364) + +[node name="Wall05" parent="." instance=ExtResource("5_lquwl")] +transform = Transform3D(-0.9373456, 0.2908417, 0.1918185, 0.3214552, 0.93427694, 0.1542501, -0.13434927, 0.20624672, -0.9692331, 0.36298275, -0.48010635, -3.1081657) + +[node name="Wall06" parent="." instance=ExtResource("5_lquwl")] +transform = Transform3D(-0.990934, 0, 0.13434926, 0, 1, 0, -0.13434926, 0, -0.990934, 0.75976896, -0.70440745, 4.5405197) + +[node name="Wall07" parent="." instance=ExtResource("5_lquwl")] +transform = Transform3D(0.02368374, 0.13314083, -0.99081427, -0.16938388, 0.97729725, 0.12727568, 0.9852657, 0.16481358, 0.045697965, 3.8073082, -0.5026467, 0.84226894) + +[node name="Wall08" parent="." instance=ExtResource("5_lquwl")] +transform = Transform3D(0.023540916, 0.2297096, -0.9729746, -0.2009241, 0.95447266, 0.22048022, 0.97932404, 0.19030367, 0.06862336, -3.4319887, -0.45930338, 0.84226894) diff --git a/xr/openxr_render_models/openxr_action_map.tres b/xr/openxr_render_models/openxr_action_map.tres new file mode 100644 index 00000000..4fc629c2 --- /dev/null +++ b/xr/openxr_render_models/openxr_action_map.tres @@ -0,0 +1,140 @@ +[gd_resource type="OpenXRActionMap" load_steps=33 format=3 uid="uid://cqrfv76xcmkca"] + +[sub_resource type="OpenXRAction" id="OpenXRAction_m08eo"] +resource_name = "aim_pose" +localized_name = "Aim pose" +action_type = 3 +toplevel_paths = PackedStringArray("/user/hand/left", "/user/hand/right") + +[sub_resource type="OpenXRAction" id="OpenXRAction_c4j1d"] +resource_name = "grip_pose" +localized_name = "Grip pose" +action_type = 3 +toplevel_paths = PackedStringArray("/user/hand/left", "/user/hand/right") + +[sub_resource type="OpenXRAction" id="OpenXRAction_sow2k"] +resource_name = "haptic" +localized_name = "Haptic" +action_type = 4 +toplevel_paths = PackedStringArray("/user/hand/left", "/user/hand/right", "/user/vive_tracker_htcx/role/left_foot", "/user/vive_tracker_htcx/role/right_foot", "/user/vive_tracker_htcx/role/left_shoulder", "/user/vive_tracker_htcx/role/right_shoulder", "/user/vive_tracker_htcx/role/left_elbow", "/user/vive_tracker_htcx/role/right_elbow", "/user/vive_tracker_htcx/role/left_knee", "/user/vive_tracker_htcx/role/right_knee", "/user/vive_tracker_htcx/role/waist", "/user/vive_tracker_htcx/role/chest", "/user/vive_tracker_htcx/role/camera", "/user/vive_tracker_htcx/role/keyboard") + +[sub_resource type="OpenXRActionSet" id="OpenXRActionSet_ngwcy"] +resource_name = "godot" +localized_name = "Godot action set" +actions = [SubResource("OpenXRAction_m08eo"), SubResource("OpenXRAction_c4j1d"), SubResource("OpenXRAction_sow2k")] + +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_pjtev"] +action = SubResource("OpenXRAction_m08eo") +binding_path = "/user/hand/left/input/aim/pose" + +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_nqyri"] +action = SubResource("OpenXRAction_m08eo") +binding_path = "/user/hand/right/input/aim/pose" + +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_86uui"] +action = SubResource("OpenXRAction_c4j1d") +binding_path = "/user/hand/left/input/grip/pose" + +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_nrtxc"] +action = SubResource("OpenXRAction_c4j1d") +binding_path = "/user/hand/right/input/grip/pose" + +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_rjtq8"] +action = SubResource("OpenXRAction_sow2k") +binding_path = "/user/hand/left/output/haptic" + +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_lce2q"] +action = SubResource("OpenXRAction_sow2k") +binding_path = "/user/hand/right/output/haptic" + +[sub_resource type="OpenXRInteractionProfile" id="OpenXRInteractionProfile_ckeh6"] +interaction_profile_path = "/interaction_profiles/khr/simple_controller" +bindings = [SubResource("OpenXRIPBinding_pjtev"), SubResource("OpenXRIPBinding_nqyri"), SubResource("OpenXRIPBinding_86uui"), SubResource("OpenXRIPBinding_nrtxc"), SubResource("OpenXRIPBinding_rjtq8"), SubResource("OpenXRIPBinding_lce2q")] + +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_chplt"] +action = SubResource("OpenXRAction_m08eo") +binding_path = "/user/hand/left/input/aim/pose" + +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_obxrh"] +action = SubResource("OpenXRAction_m08eo") +binding_path = "/user/hand/right/input/aim/pose" + +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_on7oi"] +action = SubResource("OpenXRAction_c4j1d") +binding_path = "/user/hand/left/input/grip/pose" + +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_ege4h"] +action = SubResource("OpenXRAction_c4j1d") +binding_path = "/user/hand/right/input/grip/pose" + +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_me87v"] +action = SubResource("OpenXRAction_sow2k") +binding_path = "/user/hand/left/output/haptic" + +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_d8myu"] +action = SubResource("OpenXRAction_sow2k") +binding_path = "/user/hand/right/output/haptic" + +[sub_resource type="OpenXRInteractionProfile" id="OpenXRInteractionProfile_hsh5n"] +interaction_profile_path = "/interaction_profiles/oculus/touch_controller" +bindings = [SubResource("OpenXRIPBinding_chplt"), SubResource("OpenXRIPBinding_obxrh"), SubResource("OpenXRIPBinding_on7oi"), SubResource("OpenXRIPBinding_ege4h"), SubResource("OpenXRIPBinding_me87v"), SubResource("OpenXRIPBinding_d8myu")] + +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_gosqu"] +action = SubResource("OpenXRAction_m08eo") +binding_path = "/user/hand/left/input/aim/pose" + +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_n52fm"] +action = SubResource("OpenXRAction_m08eo") +binding_path = "/user/hand/right/input/aim/pose" + +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_vushy"] +action = SubResource("OpenXRAction_c4j1d") +binding_path = "/user/hand/left/input/grip/pose" + +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_lbhgg"] +action = SubResource("OpenXRAction_c4j1d") +binding_path = "/user/hand/right/input/grip/pose" + +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_8xxre"] +action = SubResource("OpenXRAction_sow2k") +binding_path = "/user/hand/left/output/haptic" + +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_jceb4"] +action = SubResource("OpenXRAction_sow2k") +binding_path = "/user/hand/right/output/haptic" + +[sub_resource type="OpenXRInteractionProfile" id="OpenXRInteractionProfile_lvl5r"] +interaction_profile_path = "/interaction_profiles/bytedance/pico4_controller" +bindings = [SubResource("OpenXRIPBinding_gosqu"), SubResource("OpenXRIPBinding_n52fm"), SubResource("OpenXRIPBinding_vushy"), SubResource("OpenXRIPBinding_lbhgg"), SubResource("OpenXRIPBinding_8xxre"), SubResource("OpenXRIPBinding_jceb4")] + +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_34k6i"] +action = SubResource("OpenXRAction_m08eo") +binding_path = "/user/hand/left/input/aim/pose" + +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_biq8g"] +action = SubResource("OpenXRAction_m08eo") +binding_path = "/user/hand/right/input/aim/pose" + +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_7rnxc"] +action = SubResource("OpenXRAction_c4j1d") +binding_path = "/user/hand/left/input/grip/pose" + +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_go0kb"] +action = SubResource("OpenXRAction_c4j1d") +binding_path = "/user/hand/right/input/grip/pose" + +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_nudtj"] +action = SubResource("OpenXRAction_sow2k") +binding_path = "/user/hand/left/output/haptic" + +[sub_resource type="OpenXRIPBinding" id="OpenXRIPBinding_2a6v2"] +action = SubResource("OpenXRAction_sow2k") +binding_path = "/user/hand/right/output/haptic" + +[sub_resource type="OpenXRInteractionProfile" id="OpenXRInteractionProfile_jhul1"] +interaction_profile_path = "/interaction_profiles/valve/index_controller" +bindings = [SubResource("OpenXRIPBinding_34k6i"), SubResource("OpenXRIPBinding_biq8g"), SubResource("OpenXRIPBinding_7rnxc"), SubResource("OpenXRIPBinding_go0kb"), SubResource("OpenXRIPBinding_nudtj"), SubResource("OpenXRIPBinding_2a6v2")] + +[resource] +action_sets = [SubResource("OpenXRActionSet_ngwcy")] +interaction_profiles = [SubResource("OpenXRInteractionProfile_ckeh6"), SubResource("OpenXRInteractionProfile_hsh5n"), SubResource("OpenXRInteractionProfile_lvl5r"), SubResource("OpenXRInteractionProfile_jhul1")] diff --git a/xr/openxr_render_models/project.godot b/xr/openxr_render_models/project.godot new file mode 100644 index 00000000..141a7475 --- /dev/null +++ b/xr/openxr_render_models/project.godot @@ -0,0 +1,34 @@ +; Engine configuration file. +; It's best edited using the editor UI and not directly, +; since the parameters that go here are not all obvious. +; +; Format: +; [section] ; section goes between [] +; param=value ; assign values to parameters + +config_version=5 + +[application] + +config/name="openxr render models" +run/main_scene="uid://btli1dajen53o" +config/features=PackedStringArray("4.5", "GL Compatibility") +config/icon="res://icon.svg" + +[physics] + +3d/physics_engine="Jolt Physics" + +[rendering] + +renderer/rendering_method="gl_compatibility" +renderer/rendering_method.mobile="gl_compatibility" + +[xr] + +openxr/enabled=true +openxr/reference_space=2 +openxr/foveation_level=3 +openxr/foveation_dynamic=true +shaders/enabled=true +openxr/extensions/render_model=true diff --git a/xr/openxr_render_models/screenshots/render_model_demo.png b/xr/openxr_render_models/screenshots/render_model_demo.png new file mode 100644 index 00000000..9f83bab3 Binary files /dev/null and b/xr/openxr_render_models/screenshots/render_model_demo.png differ diff --git a/xr/openxr_render_models/screenshots/render_model_demo.png.import b/xr/openxr_render_models/screenshots/render_model_demo.png.import new file mode 100644 index 00000000..7c9f0c38 --- /dev/null +++ b/xr/openxr_render_models/screenshots/render_model_demo.png.import @@ -0,0 +1,40 @@ +[remap] + +importer="texture" +type="CompressedTexture2D" +uid="uid://4eulsha6ncfa" +path="res://.godot/imported/render_model_demo.png-3043378ddd018812c86616f385e65c99.ctex" +metadata={ +"vram_texture": false +} + +[deps] + +source_file="res://screenshots/render_model_demo.png" +dest_files=["res://.godot/imported/render_model_demo.png-3043378ddd018812c86616f385e65c99.ctex"] + +[params] + +compress/mode=0 +compress/high_quality=false +compress/lossy_quality=0.7 +compress/uastc_level=0 +compress/rdo_quality_loss=0.0 +compress/hdr_compression=1 +compress/normal_map=0 +compress/channel_pack=0 +mipmaps/generate=false +mipmaps/limit=-1 +roughness/mode=0 +roughness/src_normal="" +process/channel_remap/red=0 +process/channel_remap/green=1 +process/channel_remap/blue=2 +process/channel_remap/alpha=3 +process/fix_alpha_border=true +process/premult_alpha=false +process/normal_map_invert_y=false +process/hdr_as_srgb=false +process/hdr_clamp_exposure=false +process/size_limit=0 +detect_3d/compress_to=1 diff --git a/xr/openxr_render_models/start_vr.gd b/xr/openxr_render_models/start_vr.gd new file mode 100644 index 00000000..c610b4c2 --- /dev/null +++ b/xr/openxr_render_models/start_vr.gd @@ -0,0 +1,112 @@ +extends Node3D + +signal focus_lost +signal focus_gained +signal pose_recentered + +@export var maximum_refresh_rate : int = 90 + +var xr_interface : OpenXRInterface +var xr_is_focused := false + + +func _ready() -> void: + xr_interface = XRServer.find_interface("OpenXR") + if xr_interface and xr_interface.is_initialized(): + print("OpenXR instantiated successfully.") + var vp : Viewport = get_viewport() + + # Enable XR on our viewport. + vp.use_xr = true + + # Make sure V-Sync is off, as V-Sync is handled by OpenXR. + DisplayServer.window_set_vsync_mode(DisplayServer.VSYNC_DISABLED) + + # Enable variable rate shading. + if RenderingServer.get_rendering_device(): + vp.vrs_mode = Viewport.VRS_XR + elif int(ProjectSettings.get_setting("xr/openxr/foveation_level")) == 0: + push_warning("OpenXR: Recommend setting Foveation level to High in Project Settings") + + # Connect the OpenXR events. + xr_interface.session_begun.connect(_on_openxr_session_begun) + xr_interface.session_visible.connect(_on_openxr_visible_state) + xr_interface.session_focussed.connect(_on_openxr_focused_state) + xr_interface.session_stopping.connect(_on_openxr_stopping) + xr_interface.pose_recentered.connect(_on_openxr_pose_recentered) + else: + # We couldn't start OpenXR. + print("OpenXR not instantiated!") + get_tree().quit() + + +# Handle OpenXR session ready. +func _on_openxr_session_begun() -> void: + # Get the reported refresh rate. + var current_refresh_rate := xr_interface.get_display_refresh_rate() + if current_refresh_rate > 0: + print("OpenXR: Refresh rate reported as ", str(current_refresh_rate)) + else: + print("OpenXR: No refresh rate given by XR runtime") + + # See if we have a better refresh rate available. + var new_rate := current_refresh_rate + var available_rates: Array = xr_interface.get_available_display_refresh_rates() + if available_rates.is_empty(): + print("OpenXR: Target does not support refresh rate extension") + elif available_rates.size() == 1: + # Only one available, so use it. + new_rate = available_rates[0] + else: + for rate in available_rates: + if rate > new_rate and rate <= maximum_refresh_rate: + new_rate = rate + + # Did we find a better rate? + if current_refresh_rate != new_rate: + print("OpenXR: Setting refresh rate to ", str(new_rate)) + xr_interface.set_display_refresh_rate(new_rate) + current_refresh_rate = new_rate + + # Now match our physics rate. This is currently needed to avoid jittering, + # due to physics interpolation not being used. + Engine.physics_ticks_per_second = roundi(current_refresh_rate) + + +# Handle OpenXR visible state. +func _on_openxr_visible_state() -> void: + # We always pass this state at startup, + # but the second time we get this, it means our player took off their headset. + if xr_is_focused: + print("OpenXR lost focus") + + xr_is_focused = false + + # Pause our game. + process_mode = Node.PROCESS_MODE_DISABLED + + focus_lost.emit() + + +# Handle OpenXR focused state +func _on_openxr_focused_state() -> void: + print("OpenXR gained focus") + xr_is_focused = true + + # Unpause our game. + process_mode = Node.PROCESS_MODE_INHERIT + + focus_gained.emit() + + +# Handle OpenXR stopping state. +func _on_openxr_stopping() -> void: + # Our session is being stopped. + print("OpenXR is stopping") + + +# Handle OpenXR pose recentered signal. +func _on_openxr_pose_recentered() -> void: + # User recentered view, we have to react to this by recentering the view. + # This is game implementation dependent. + pose_recentered.emit() diff --git a/xr/openxr_render_models/start_vr.gd.uid b/xr/openxr_render_models/start_vr.gd.uid new file mode 100644 index 00000000..dd1e64c4 --- /dev/null +++ b/xr/openxr_render_models/start_vr.gd.uid @@ -0,0 +1 @@ +uid://bqa4r4n7b6d7s diff --git a/xr/openxr_render_models/wall.tscn b/xr/openxr_render_models/wall.tscn new file mode 100644 index 00000000..7522507c --- /dev/null +++ b/xr/openxr_render_models/wall.tscn @@ -0,0 +1,25 @@ +[gd_scene load_steps=5 format=3 uid="uid://c7ohc2o1shtu7"] + +[ext_resource type="Texture2D" uid="uid://btobyv4xjhltq" path="res://assets/pattern.png" id="1_xxgf6"] + +[sub_resource type="BoxShape3D" id="BoxShape3D_oh6kn"] +size = Vector3(2, 2, 0.2) + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_y4f30"] +albedo_color = Color(0.76257527, 0.49292004, 0.17555861, 1) +albedo_texture = ExtResource("1_xxgf6") +uv1_scale = Vector3(2, 2, 2) + +[sub_resource type="BoxMesh" id="BoxMesh_xxgf6"] +material = SubResource("StandardMaterial3D_y4f30") +size = Vector3(2, 2, 0.2) + +[node name="Wall" type="StaticBody3D"] + +[node name="CollisionShape3D" type="CollisionShape3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0) +shape = SubResource("BoxShape3D_oh6kn") + +[node name="MeshInstance3D" type="MeshInstance3D" parent="."] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0) +mesh = SubResource("BoxMesh_xxgf6")