From afc8c6391ccc2d3d69dcad0d93b2639bfe77dc55 Mon Sep 17 00:00:00 2001 From: Bastiaan Olij Date: Thu, 9 Apr 2020 00:47:36 +1000 Subject: Renaming all ARVR nodes to XR --- doc/classes/@GlobalScope.xml | 4 +- doc/classes/ARVRAnchor.xml | 66 --- doc/classes/ARVRCamera.xml | 17 - doc/classes/ARVRController.xml | 106 ---- doc/classes/ARVRInterface.xml | 127 ----- doc/classes/ARVROrigin.xml | 25 - doc/classes/ARVRPositionalTracker.xml | 111 ---- doc/classes/ARVRServer.xml | 188 ------- doc/classes/RenderingServer.xml | 6 +- doc/classes/SubViewport.xml | 2 +- doc/classes/XRAnchor3D.xml | 66 +++ doc/classes/XRCamera3D.xml | 17 + doc/classes/XRController3D.xml | 106 ++++ doc/classes/XRInterface.xml | 127 +++++ doc/classes/XROrigin3D.xml | 25 + doc/classes/XRPositionalTracker.xml | 111 ++++ doc/classes/XRServer.xml | 188 +++++++ main/main.cpp | 12 +- modules/arkit/arkit_interface.h | 18 +- modules/arkit/arkit_interface.mm | 54 +- modules/arkit/register_types.cpp | 2 +- modules/gdnative/SCsub | 2 +- modules/gdnative/arvr/SCsub | 6 - modules/gdnative/arvr/arvr_interface_gdnative.cpp | 439 --------------- modules/gdnative/arvr/arvr_interface_gdnative.h | 91 --- modules/gdnative/arvr/config.py | 6 - modules/gdnative/arvr/register_types.cpp | 39 -- modules/gdnative/arvr/register_types.h | 37 -- modules/gdnative/config.py | 2 +- .../gdnative/doc_classes/ARVRInterfaceGDNative.xml | 15 - modules/gdnative/doc_classes/GDNativeLibrary.xml | 2 +- .../gdnative/doc_classes/XRInterfaceGDNative.xml | 15 + modules/gdnative/gdnative_api.json | 28 +- modules/gdnative/gdnative_builders.py | 2 +- modules/gdnative/include/arvr/godot_arvr.h | 92 --- modules/gdnative/include/xr/godot_xr.h | 91 +++ modules/gdnative/register_types.cpp | 6 +- modules/gdnative/xr/SCsub | 6 + modules/gdnative/xr/config.py | 6 + modules/gdnative/xr/register_types.cpp | 40 ++ modules/gdnative/xr/register_types.h | 37 ++ modules/gdnative/xr/xr_interface_gdnative.cpp | 428 ++++++++++++++ modules/gdnative/xr/xr_interface_gdnative.h | 91 +++ .../mobile_vr/doc_classes/MobileVRInterface.xml | 8 +- modules/mobile_vr/mobile_vr_interface.cpp | 44 +- modules/mobile_vr/mobile_vr_interface.h | 14 +- modules/mobile_vr/register_types.cpp | 2 +- scene/3d/arvr_nodes.cpp | 621 --------------------- scene/3d/arvr_nodes.h | 176 ------ scene/3d/xr_nodes.cpp | 621 +++++++++++++++++++++ scene/3d/xr_nodes.h | 176 ++++++ scene/main/viewport.cpp | 19 +- scene/main/viewport.h | 6 +- scene/register_scene_types.cpp | 14 +- servers/SCsub | 2 +- servers/arvr/SCsub | 5 - servers/arvr/arvr_interface.cpp | 145 ----- servers/arvr/arvr_interface.h | 126 ----- servers/arvr/arvr_positional_tracker.cpp | 237 -------- servers/arvr/arvr_positional_tracker.h | 104 ---- servers/arvr_server.cpp | 386 ------------- servers/arvr_server.h | 192 ------- servers/register_server_types.cpp | 18 +- servers/rendering/rendering_server_raster.h | 2 +- servers/rendering/rendering_server_scene.cpp | 12 +- servers/rendering/rendering_server_scene.h | 4 +- servers/rendering/rendering_server_viewport.cpp | 53 +- servers/rendering/rendering_server_viewport.h | 12 +- servers/rendering/rendering_server_wrap_mt.h | 2 +- servers/rendering_server.cpp | 2 +- servers/rendering_server.h | 2 +- servers/xr/SCsub | 5 + servers/xr/xr_interface.cpp | 145 +++++ servers/xr/xr_interface.h | 126 +++++ servers/xr/xr_positional_tracker.cpp | 237 ++++++++ servers/xr/xr_positional_tracker.h | 104 ++++ servers/xr_server.cpp | 386 +++++++++++++ servers/xr_server.h | 192 +++++++ 78 files changed, 3528 insertions(+), 3531 deletions(-) delete mode 100644 doc/classes/ARVRAnchor.xml delete mode 100644 doc/classes/ARVRCamera.xml delete mode 100644 doc/classes/ARVRController.xml delete mode 100644 doc/classes/ARVRInterface.xml delete mode 100644 doc/classes/ARVROrigin.xml delete mode 100644 doc/classes/ARVRPositionalTracker.xml delete mode 100644 doc/classes/ARVRServer.xml create mode 100644 doc/classes/XRAnchor3D.xml create mode 100644 doc/classes/XRCamera3D.xml create mode 100644 doc/classes/XRController3D.xml create mode 100644 doc/classes/XRInterface.xml create mode 100644 doc/classes/XROrigin3D.xml create mode 100644 doc/classes/XRPositionalTracker.xml create mode 100644 doc/classes/XRServer.xml delete mode 100644 modules/gdnative/arvr/SCsub delete mode 100644 modules/gdnative/arvr/arvr_interface_gdnative.cpp delete mode 100644 modules/gdnative/arvr/arvr_interface_gdnative.h delete mode 100644 modules/gdnative/arvr/config.py delete mode 100644 modules/gdnative/arvr/register_types.cpp delete mode 100644 modules/gdnative/arvr/register_types.h delete mode 100644 modules/gdnative/doc_classes/ARVRInterfaceGDNative.xml create mode 100644 modules/gdnative/doc_classes/XRInterfaceGDNative.xml delete mode 100644 modules/gdnative/include/arvr/godot_arvr.h create mode 100644 modules/gdnative/include/xr/godot_xr.h create mode 100644 modules/gdnative/xr/SCsub create mode 100644 modules/gdnative/xr/config.py create mode 100644 modules/gdnative/xr/register_types.cpp create mode 100644 modules/gdnative/xr/register_types.h create mode 100644 modules/gdnative/xr/xr_interface_gdnative.cpp create mode 100644 modules/gdnative/xr/xr_interface_gdnative.h delete mode 100644 scene/3d/arvr_nodes.cpp delete mode 100644 scene/3d/arvr_nodes.h create mode 100644 scene/3d/xr_nodes.cpp create mode 100644 scene/3d/xr_nodes.h delete mode 100644 servers/arvr/SCsub delete mode 100644 servers/arvr/arvr_interface.cpp delete mode 100644 servers/arvr/arvr_interface.h delete mode 100644 servers/arvr/arvr_positional_tracker.cpp delete mode 100644 servers/arvr/arvr_positional_tracker.h delete mode 100644 servers/arvr_server.cpp delete mode 100644 servers/arvr_server.h create mode 100644 servers/xr/SCsub create mode 100644 servers/xr/xr_interface.cpp create mode 100644 servers/xr/xr_interface.h create mode 100644 servers/xr/xr_positional_tracker.cpp create mode 100644 servers/xr/xr_positional_tracker.h create mode 100644 servers/xr_server.cpp create mode 100644 servers/xr_server.h diff --git a/doc/classes/@GlobalScope.xml b/doc/classes/@GlobalScope.xml index 8c6821eaac..f462aa989d 100644 --- a/doc/classes/@GlobalScope.xml +++ b/doc/classes/@GlobalScope.xml @@ -12,8 +12,8 @@ - - The [ARVRServer] singleton. + + The [XRServer] singleton. The [AudioServer] singleton. diff --git a/doc/classes/ARVRAnchor.xml b/doc/classes/ARVRAnchor.xml deleted file mode 100644 index 82575ce7cb..0000000000 --- a/doc/classes/ARVRAnchor.xml +++ /dev/null @@ -1,66 +0,0 @@ - - - - An anchor point in AR space. - - - The [ARVRAnchor] point is a spatial node that maps a real world location identified by the AR platform to a position within the game world. For example, as long as plane detection in ARKit is on, ARKit will identify and update the position of planes (tables, floors, etc) and create anchors for them. - This node is mapped to one of the anchors through its unique ID. When you receive a signal that a new anchor is available, you should add this node to your scene for that anchor. You can predefine nodes and set the ID; the nodes will simply remain on 0,0,0 until a plane is recognized. - Keep in mind that, as long as plane detection is enabled, the size, placing and orientation of an anchor will be updated as the detection logic learns more about the real world out there especially if only part of the surface is in view. - - - - - - - - - Returns the name given to this anchor. - - - - - - - Returns [code]true[/code] if the anchor is being tracked and [code]false[/code] if no anchor with this ID is currently known. - - - - - - - If provided by the [ARVRInterface], this returns a mesh object for the anchor. For an anchor, this can be a shape related to the object being tracked or it can be a mesh that provides topology related to the anchor and can be used to create shadows/reflections on surfaces or for generating collision shapes. - - - - - - - Returns a plane aligned with our anchor; handy for intersection testing. - - - - - - - Returns the estimated size of the plane that was detected. Say when the anchor relates to a table in the real world, this is the estimated size of the surface of that table. - - - - - - The anchor's ID. You can set this before the anchor itself exists. The first anchor gets an ID of [code]1[/code], the second an ID of [code]2[/code], etc. When anchors get removed, the engine can then assign the corresponding ID to new anchors. The most common situation where anchors "disappear" is when the AR server identifies that two anchors represent different parts of the same plane and merges them. - - - - - - - - Emitted when the mesh associated with the anchor changes or when one becomes available. This is especially important for topology that is constantly being [code]mesh_updated[/code]. - - - - - - diff --git a/doc/classes/ARVRCamera.xml b/doc/classes/ARVRCamera.xml deleted file mode 100644 index c97d5cf1d8..0000000000 --- a/doc/classes/ARVRCamera.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - A camera node with a few overrules for AR/VR applied, such as location tracking. - - - This is a helper spatial node for our camera; note that, if stereoscopic rendering is applicable (VR-HMD), most of the camera properties are ignored, as the HMD information overrides them. The only properties that can be trusted are the near and far planes. - The position and orientation of this node is automatically updated by the ARVR Server to represent the location of the HMD if such tracking is available and can thus be used by game logic. Note that, in contrast to the ARVR Controller, the render thread has access to the most up-to-date tracking data of the HMD and the location of the ARVRCamera can lag a few milliseconds behind what is used for rendering as a result. - - - https://docs.godotengine.org/en/latest/tutorials/vr/index.html - - - - - - diff --git a/doc/classes/ARVRController.xml b/doc/classes/ARVRController.xml deleted file mode 100644 index 572b47ce6d..0000000000 --- a/doc/classes/ARVRController.xml +++ /dev/null @@ -1,106 +0,0 @@ - - - - A spatial node representing a spatially-tracked controller. - - - This is a helper spatial node that is linked to the tracking of controllers. It also offers several handy passthroughs to the state of buttons and such on the controllers. - Controllers are linked by their ID. You can create controller nodes before the controllers are available. If your game always uses two controllers (one for each hand), you can predefine the controllers with ID 1 and 2; they will become active as soon as the controllers are identified. If you expect additional controllers to be used, you should react to the signals and add ARVRController nodes to your scene. - The position of the controller node is automatically updated by the [ARVRServer]. This makes this node ideal to add child nodes to visualize the controller. - - - https://docs.godotengine.org/en/latest/tutorials/vr/index.html - - - - - - - If active, returns the name of the associated controller if provided by the AR/VR SDK used. - - - - - - - Returns the hand holding this controller, if known. See [enum ARVRPositionalTracker.TrackerHand]. - - - - - - - Returns [code]true[/code] if the bound controller is active. ARVR systems attempt to track active controllers. - - - - - - - - - Returns the value of the given axis for things like triggers, touchpads, etc. that are embedded into the controller. - - - - - - - Returns the ID of the joystick object bound to this. Every controller tracked by the [ARVRServer] that has buttons and axis will also be registered as a joystick within Godot. This means that all the normal joystick tracking and input mapping will work for buttons and axis found on the AR/VR controllers. This ID is purely offered as information so you can link up the controller with its joystick entry. - - - - - - - If provided by the [ARVRInterface], this returns a mesh associated with the controller. This can be used to visualize the controller. - - - - - - - - - Returns [code]true[/code] if the button at index [code]button[/code] is pressed. See [enum JoystickList], in particular the [code]JOY_VR_*[/code] constants. - - - - - - The controller's ID. - A controller ID of 0 is unbound and will always result in an inactive node. Controller ID 1 is reserved for the first controller that identifies itself as the left-hand controller and ID 2 is reserved for the first controller that identifies itself as the right-hand controller. - For any other controller that the [ARVRServer] detects, we continue with controller ID 3. - When a controller is turned off, its slot is freed. This ensures controllers will keep the same ID even when controllers with lower IDs are turned off. - - - The degree to which the controller vibrates. Ranges from [code]0.0[/code] to [code]1.0[/code] with precision [code].01[/code]. If changed, updates [member ARVRPositionalTracker.rumble] accordingly. - This is a useful property to animate if you want the controller to vibrate for a limited duration. - - - - - - - - Emitted when a button on this controller is pressed. - - - - - - - Emitted when a button on this controller is released. - - - - - - - Emitted when the mesh associated with the controller changes or when one becomes available. Generally speaking this will be a static mesh after becoming available. - - - - - - diff --git a/doc/classes/ARVRInterface.xml b/doc/classes/ARVRInterface.xml deleted file mode 100644 index 0727bda668..0000000000 --- a/doc/classes/ARVRInterface.xml +++ /dev/null @@ -1,127 +0,0 @@ - - - - Base class for an AR/VR interface implementation. - - - This class needs to be implemented to make an AR or VR platform available to Godot and these should be implemented as C++ modules or GDNative modules (note that for GDNative the subclass ARVRScriptInterface should be used). Part of the interface is exposed to GDScript so you can detect, enable and configure an AR or VR platform. - Interfaces should be written in such a way that simply enabling them will give us a working setup. You can query the available interfaces through [ARVRServer]. - - - https://docs.godotengine.org/en/latest/tutorials/vr/index.html - - - - - - - If this is an AR interface that requires displaying a camera feed as the background, this method returns the feed ID in the [CameraServer] for this interface. - - - - - - - Returns a combination of [enum Capabilities] flags providing information about the capabilities of this interface. - - - - - - - Returns the name of this interface (OpenVR, OpenHMD, ARKit, etc). - - - - - - - Returns the resolution at which we should render our intermediate results before things like lens distortion are applied by the VR platform. - - - - - - - If supported, returns the status of our tracking. This will allow you to provide feedback to the user whether there are issues with positional tracking. - - - - - - - Call this to initialize this interface. The first interface that is initialized is identified as the primary interface and it will be used for rendering output. - After initializing the interface you want to use you then need to enable the AR/VR mode of a viewport and rendering should commence. - [b]Note:[/b] You must enable the AR/VR mode on the main viewport for any device that uses the main output of Godot, such as for mobile VR. - If you do this for a platform that handles its own output (such as OpenVR) Godot will show just one eye without distortion on screen. Alternatively, you can add a separate viewport node to your scene and enable AR/VR on that viewport. It will be used to output to the HMD, leaving you free to do anything you like in the main window, such as using a separate camera as a spectator camera or rendering something completely different. - While currently not used, you can activate additional interfaces. You may wish to do this if you want to track controllers from other platforms. However, at this point in time only one interface can render to an HMD. - - - - - - - Returns [code]true[/code] if the current output of this interface is in stereo. - - - - - - - Turns the interface off. - - - - - - On an AR interface, [code]true[/code] if anchor detection is enabled. - - - [code]true[/code] if this interface been initialized. - - - [code]true[/code] if this is the primary interface. - - - - - No ARVR capabilities. - - - This interface can work with normal rendering output (non-HMD based AR). - - - This interface supports stereoscopic rendering. - - - This interface supports AR (video background and real world tracking). - - - This interface outputs to an external device. If the main viewport is used, the on screen output is an unmodified buffer of either the left or right eye (stretched if the viewport size is not changed to the same aspect ratio of [method get_render_targetsize]). Using a separate viewport node frees up the main viewport for other purposes. - - - Mono output, this is mostly used internally when retrieving positioning information for our camera node or when stereo scopic rendering is not supported. - - - Left eye output, this is mostly used internally when rendering the image for the left eye and obtaining positioning and projection information. - - - Right eye output, this is mostly used internally when rendering the image for the right eye and obtaining positioning and projection information. - - - Tracking is behaving as expected. - - - Tracking is hindered by excessive motion (the player is moving faster than tracking can keep up). - - - Tracking is hindered by insufficient features, it's too dark (for camera-based tracking), player is blocked, etc. - - - We don't know the status of the tracking or this interface does not provide feedback. - - - Tracking is not functional (camera not plugged in or obscured, lighthouses turned off, etc.). - - - diff --git a/doc/classes/ARVROrigin.xml b/doc/classes/ARVROrigin.xml deleted file mode 100644 index a88a89c927..0000000000 --- a/doc/classes/ARVROrigin.xml +++ /dev/null @@ -1,25 +0,0 @@ - - - - The origin point in AR/VR. - - - This is a special node within the AR/VR system that maps the physical location of the center of our tracking space to the virtual location within our game world. - There should be only one of these nodes in your scene and you must have one. All the ARVRCamera, ARVRController and ARVRAnchor nodes should be direct children of this node for spatial tracking to work correctly. - It is the position of this node that you update when your character needs to move through your game world while we're not moving in the real world. Movement in the real world is always in relation to this origin point. - For example, if your character is driving a car, the ARVROrigin node should be a child node of this car. Or, if you're implementing a teleport system to move your character, you should change the position of this node. - - - https://docs.godotengine.org/en/latest/tutorials/vr/index.html - - - - - - Allows you to adjust the scale to your game's units. Most AR/VR platforms assume a scale of 1 game world unit = 1 real world meter. - [b]Note:[/b] This method is a passthrough to the [ARVRServer] itself. - - - - - diff --git a/doc/classes/ARVRPositionalTracker.xml b/doc/classes/ARVRPositionalTracker.xml deleted file mode 100644 index 640b721d37..0000000000 --- a/doc/classes/ARVRPositionalTracker.xml +++ /dev/null @@ -1,111 +0,0 @@ - - - - A tracked object. - - - An instance of this object represents a device that is tracked, such as a controller or anchor point. HMDs aren't represented here as they are handled internally. - As controllers are turned on and the AR/VR interface detects them, instances of this object are automatically added to this list of active tracking objects accessible through the [ARVRServer]. - The [ARVRController] and [ARVRAnchor] both consume objects of this type and should be used in your project. The positional trackers are just under-the-hood objects that make this all work. These are mostly exposed so that GDNative-based interfaces can interact with them. - - - https://docs.godotengine.org/en/latest/tutorials/vr/index.html - - - - - - - Returns the hand holding this tracker, if known. See [enum TrackerHand] constants. - - - - - - - If this is a controller that is being tracked, the controller will also be represented by a joystick entry with this ID. - - - - - - - Returns the mesh related to a controller or anchor point if one is available. - - - - - - - Returns the controller or anchor point's name if available. - - - - - - - Returns the controller's orientation matrix. - - - - - - - Returns the world-space controller position. - - - - - - - Returns the internal tracker ID. This uniquely identifies the tracker per tracker type and matches the ID you need to specify for nodes such as the [ARVRController] and [ARVRAnchor] nodes. - - - - - - - Returns [code]true[/code] if this device tracks orientation. - - - - - - - Returns [code]true[/code] if this device tracks position. - - - - - - - - - Returns the transform combining this device's orientation and position. - - - - - - - Returns the tracker's type. - - - - - - The degree to which the tracker rumbles. Ranges from [code]0.0[/code] to [code]1.0[/code] with precision [code].01[/code]. - - - - - The hand this tracker is held in is unknown or not applicable. - - - This tracker is the left hand controller. - - - This tracker is the right hand controller. - - - diff --git a/doc/classes/ARVRServer.xml b/doc/classes/ARVRServer.xml deleted file mode 100644 index d8d069c048..0000000000 --- a/doc/classes/ARVRServer.xml +++ /dev/null @@ -1,188 +0,0 @@ - - - - Server for AR and VR features. - - - The AR/VR server is the heart of our Advanced and Virtual Reality solution and handles all the processing. - - - https://docs.godotengine.org/en/latest/tutorials/vr/index.html - - - - - - - - - - - This is an important function to understand correctly. AR and VR platforms all handle positioning slightly differently. - For platforms that do not offer spatial tracking, our origin point (0,0,0) is the location of our HMD, but you have little control over the direction the player is facing in the real world. - For platforms that do offer spatial tracking, our origin point depends very much on the system. For OpenVR, our origin point is usually the center of the tracking space, on the ground. For other platforms, it's often the location of the tracking camera. - This method allows you to center your tracker on the location of the HMD. It will take the current location of the HMD and use that to adjust all your tracking data; in essence, realigning the real world to your player's current position in the game world. - For this method to produce usable results, tracking information must be available. This often takes a few frames after starting your game. - You should call this method after a few seconds have passed. For instance, when the user requests a realignment of the display holding a designated button on a controller for a short period of time, or when implementing a teleport mechanism. - - - - - - - - - Finds an interface by its name. For instance, if your project uses capabilities of an AR/VR platform, you can find the interface for that platform by name and initialize it. - - - - - - - Returns the primary interface's transformation. - - - - - - - - - Returns the interface registered at a given index in our list of interfaces. - - - - - - - Returns the number of interfaces currently registered with the AR/VR server. If your project supports multiple AR/VR platforms, you can look through the available interface, and either present the user with a selection or simply try to initialize each interface and use the first one that returns [code]true[/code]. - - - - - - - Returns a list of available interfaces the ID and name of each interface. - - - - - - - Returns the absolute timestamp (in μs) of the last [ARVRServer] commit of the AR/VR eyes to [RenderingServer]. The value comes from an internal call to [method OS.get_ticks_usec]. - - - - - - - Returns the duration (in μs) of the last frame. This is computed as the difference between [method get_last_commit_usec] and [method get_last_process_usec] when committing. - - - - - - - Returns the absolute timestamp (in μs) of the last [ARVRServer] process callback. The value comes from an internal call to [method OS.get_ticks_usec]. - - - - - - - Returns the reference frame transform. Mostly used internally and exposed for GDNative build interfaces. - - - - - - - - - Returns the positional tracker at the given ID. - - - - - - - Returns the number of trackers currently registered. - - - - - - The primary [ARVRInterface] currently bound to the [ARVRServer]. - - - Allows you to adjust the scale to your game's units. Most AR/VR platforms assume a scale of 1 game world unit = 1 real world meter. - - - - - - - - Emitted when a new interface has been added. - - - - - - - Emitted when an interface is removed. - - - - - - - - - - - Emitted when a new tracker has been added. If you don't use a fixed number of controllers or if you're using [ARVRAnchor]s for an AR solution, it is important to react to this signal to add the appropriate [ARVRController] or [ARVRAnchor] nodes related to this new tracker. - - - - - - - - - - - Emitted when a tracker is removed. You should remove any [ARVRController] or [ARVRAnchor] points if applicable. This is not mandatory, the nodes simply become inactive and will be made active again when a new tracker becomes available (i.e. a new controller is switched on that takes the place of the previous one). - - - - - - The tracker tracks the location of a controller. - - - The tracker tracks the location of a base station. - - - The tracker tracks the location and size of an AR anchor. - - - Used internally to filter trackers of any known type. - - - Used internally if we haven't set the tracker type yet. - - - Used internally to select all trackers. - - - Fully reset the orientation of the HMD. Regardless of what direction the user is looking to in the real world. The user will look dead ahead in the virtual world. - - - Resets the orientation but keeps the tilt of the device. So if we're looking down, we keep looking down but heading will be reset. - - - Does not reset the orientation of the HMD, only the position of the player gets centered. - - - diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index d2d13fe406..aa393877b2 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -3067,15 +3067,15 @@ Sets when the viewport should be updated. See [enum ViewportUpdateMode] constants for options. - + - + - If [code]true[/code], the viewport uses augmented or virtual reality technologies. See [ARVRInterface]. + If [code]true[/code], the viewport uses augmented or virtual reality technologies. See [XRInterface]. diff --git a/doc/classes/SubViewport.xml b/doc/classes/SubViewport.xml index dc3d748496..e877050bf8 100644 --- a/doc/classes/SubViewport.xml +++ b/doc/classes/SubViewport.xml @@ -9,7 +9,7 @@ - + If [code]true[/code], the sub-viewport will be used in AR/VR process. diff --git a/doc/classes/XRAnchor3D.xml b/doc/classes/XRAnchor3D.xml new file mode 100644 index 0000000000..a409c79230 --- /dev/null +++ b/doc/classes/XRAnchor3D.xml @@ -0,0 +1,66 @@ + + + + An anchor point in AR space. + + + The [XRAnchor3D] point is a spatial node that maps a real world location identified by the AR platform to a position within the game world. For example, as long as plane detection in ARKit is on, ARKit will identify and update the position of planes (tables, floors, etc) and create anchors for them. + This node is mapped to one of the anchors through its unique ID. When you receive a signal that a new anchor is available, you should add this node to your scene for that anchor. You can predefine nodes and set the ID; the nodes will simply remain on 0,0,0 until a plane is recognized. + Keep in mind that, as long as plane detection is enabled, the size, placing and orientation of an anchor will be updated as the detection logic learns more about the real world out there especially if only part of the surface is in view. + + + + + + + + + Returns the name given to this anchor. + + + + + + + Returns [code]true[/code] if the anchor is being tracked and [code]false[/code] if no anchor with this ID is currently known. + + + + + + + If provided by the [XRInterface], this returns a mesh object for the anchor. For an anchor, this can be a shape related to the object being tracked or it can be a mesh that provides topology related to the anchor and can be used to create shadows/reflections on surfaces or for generating collision shapes. + + + + + + + Returns a plane aligned with our anchor; handy for intersection testing. + + + + + + + Returns the estimated size of the plane that was detected. Say when the anchor relates to a table in the real world, this is the estimated size of the surface of that table. + + + + + + The anchor's ID. You can set this before the anchor itself exists. The first anchor gets an ID of [code]1[/code], the second an ID of [code]2[/code], etc. When anchors get removed, the engine can then assign the corresponding ID to new anchors. The most common situation where anchors "disappear" is when the AR server identifies that two anchors represent different parts of the same plane and merges them. + + + + + + + + Emitted when the mesh associated with the anchor changes or when one becomes available. This is especially important for topology that is constantly being [code]mesh_updated[/code]. + + + + + + diff --git a/doc/classes/XRCamera3D.xml b/doc/classes/XRCamera3D.xml new file mode 100644 index 0000000000..4d86e24daa --- /dev/null +++ b/doc/classes/XRCamera3D.xml @@ -0,0 +1,17 @@ + + + + A camera node with a few overrules for AR/VR applied, such as location tracking. + + + This is a helper spatial node for our camera; note that, if stereoscopic rendering is applicable (VR-HMD), most of the camera properties are ignored, as the HMD information overrides them. The only properties that can be trusted are the near and far planes. + The position and orientation of this node is automatically updated by the XR Server to represent the location of the HMD if such tracking is available and can thus be used by game logic. Note that, in contrast to the XR Controller, the render thread has access to the most up-to-date tracking data of the HMD and the location of the XRCamera3D can lag a few milliseconds behind what is used for rendering as a result. + + + https://docs.godotengine.org/en/latest/tutorials/vr/index.html + + + + + + diff --git a/doc/classes/XRController3D.xml b/doc/classes/XRController3D.xml new file mode 100644 index 0000000000..e4a06a80db --- /dev/null +++ b/doc/classes/XRController3D.xml @@ -0,0 +1,106 @@ + + + + A spatial node representing a spatially-tracked controller. + + + This is a helper spatial node that is linked to the tracking of controllers. It also offers several handy passthroughs to the state of buttons and such on the controllers. + Controllers are linked by their ID. You can create controller nodes before the controllers are available. If your game always uses two controllers (one for each hand), you can predefine the controllers with ID 1 and 2; they will become active as soon as the controllers are identified. If you expect additional controllers to be used, you should react to the signals and add XRController3D nodes to your scene. + The position of the controller node is automatically updated by the [XRServer]. This makes this node ideal to add child nodes to visualize the controller. + + + https://docs.godotengine.org/en/latest/tutorials/vr/index.html + + + + + + + If active, returns the name of the associated controller if provided by the AR/VR SDK used. + + + + + + + Returns the hand holding this controller, if known. See [enum XRPositionalTracker.TrackerHand]. + + + + + + + Returns [code]true[/code] if the bound controller is active. XR systems attempt to track active controllers. + + + + + + + + + Returns the value of the given axis for things like triggers, touchpads, etc. that are embedded into the controller. + + + + + + + Returns the ID of the joystick object bound to this. Every controller tracked by the [XRServer] that has buttons and axis will also be registered as a joystick within Godot. This means that all the normal joystick tracking and input mapping will work for buttons and axis found on the AR/VR controllers. This ID is purely offered as information so you can link up the controller with its joystick entry. + + + + + + + If provided by the [XRInterface], this returns a mesh associated with the controller. This can be used to visualize the controller. + + + + + + + + + Returns [code]true[/code] if the button at index [code]button[/code] is pressed. See [enum JoystickList], in particular the [code]JOY_VR_*[/code] constants. + + + + + + The controller's ID. + A controller ID of 0 is unbound and will always result in an inactive node. Controller ID 1 is reserved for the first controller that identifies itself as the left-hand controller and ID 2 is reserved for the first controller that identifies itself as the right-hand controller. + For any other controller that the [XRServer] detects, we continue with controller ID 3. + When a controller is turned off, its slot is freed. This ensures controllers will keep the same ID even when controllers with lower IDs are turned off. + + + The degree to which the controller vibrates. Ranges from [code]0.0[/code] to [code]1.0[/code] with precision [code].01[/code]. If changed, updates [member XRPositionalTracker.rumble] accordingly. + This is a useful property to animate if you want the controller to vibrate for a limited duration. + + + + + + + + Emitted when a button on this controller is pressed. + + + + + + + Emitted when a button on this controller is released. + + + + + + + Emitted when the mesh associated with the controller changes or when one becomes available. Generally speaking this will be a static mesh after becoming available. + + + + + + diff --git a/doc/classes/XRInterface.xml b/doc/classes/XRInterface.xml new file mode 100644 index 0000000000..1985010223 --- /dev/null +++ b/doc/classes/XRInterface.xml @@ -0,0 +1,127 @@ + + + + Base class for an AR/VR interface implementation. + + + This class needs to be implemented to make an AR or VR platform available to Godot and these should be implemented as C++ modules or GDNative modules (note that for GDNative the subclass XRScriptInterface should be used). Part of the interface is exposed to GDScript so you can detect, enable and configure an AR or VR platform. + Interfaces should be written in such a way that simply enabling them will give us a working setup. You can query the available interfaces through [XRServer]. + + + https://docs.godotengine.org/en/latest/tutorials/vr/index.html + + + + + + + If this is an AR interface that requires displaying a camera feed as the background, this method returns the feed ID in the [CameraServer] for this interface. + + + + + + + Returns a combination of [enum Capabilities] flags providing information about the capabilities of this interface. + + + + + + + Returns the name of this interface (OpenVR, OpenHMD, ARKit, etc). + + + + + + + Returns the resolution at which we should render our intermediate results before things like lens distortion are applied by the VR platform. + + + + + + + If supported, returns the status of our tracking. This will allow you to provide feedback to the user whether there are issues with positional tracking. + + + + + + + Call this to initialize this interface. The first interface that is initialized is identified as the primary interface and it will be used for rendering output. + After initializing the interface you want to use you then need to enable the AR/VR mode of a viewport and rendering should commence. + [b]Note:[/b] You must enable the AR/VR mode on the main viewport for any device that uses the main output of Godot, such as for mobile VR. + If you do this for a platform that handles its own output (such as OpenVR) Godot will show just one eye without distortion on screen. Alternatively, you can add a separate viewport node to your scene and enable AR/VR on that viewport. It will be used to output to the HMD, leaving you free to do anything you like in the main window, such as using a separate camera as a spectator camera or rendering something completely different. + While currently not used, you can activate additional interfaces. You may wish to do this if you want to track controllers from other platforms. However, at this point in time only one interface can render to an HMD. + + + + + + + Returns [code]true[/code] if the current output of this interface is in stereo. + + + + + + + Turns the interface off. + + + + + + On an AR interface, [code]true[/code] if anchor detection is enabled. + + + [code]true[/code] if this interface been initialized. + + + [code]true[/code] if this is the primary interface. + + + + + No XR capabilities. + + + This interface can work with normal rendering output (non-HMD based AR). + + + This interface supports stereoscopic rendering. + + + This interface supports AR (video background and real world tracking). + + + This interface outputs to an external device. If the main viewport is used, the on screen output is an unmodified buffer of either the left or right eye (stretched if the viewport size is not changed to the same aspect ratio of [method get_render_targetsize]). Using a separate viewport node frees up the main viewport for other purposes. + + + Mono output, this is mostly used internally when retrieving positioning information for our camera node or when stereo scopic rendering is not supported. + + + Left eye output, this is mostly used internally when rendering the image for the left eye and obtaining positioning and projection information. + + + Right eye output, this is mostly used internally when rendering the image for the right eye and obtaining positioning and projection information. + + + Tracking is behaving as expected. + + + Tracking is hindered by excessive motion (the player is moving faster than tracking can keep up). + + + Tracking is hindered by insufficient features, it's too dark (for camera-based tracking), player is blocked, etc. + + + We don't know the status of the tracking or this interface does not provide feedback. + + + Tracking is not functional (camera not plugged in or obscured, lighthouses turned off, etc.). + + + diff --git a/doc/classes/XROrigin3D.xml b/doc/classes/XROrigin3D.xml new file mode 100644 index 0000000000..57cf673d30 --- /dev/null +++ b/doc/classes/XROrigin3D.xml @@ -0,0 +1,25 @@ + + + + The origin point in AR/VR. + + + This is a special node within the AR/VR system that maps the physical location of the center of our tracking space to the virtual location within our game world. + There should be only one of these nodes in your scene and you must have one. All the XRCamera3D, XRController3D and XRAnchor3D nodes should be direct children of this node for spatial tracking to work correctly. + It is the position of this node that you update when your character needs to move through your game world while we're not moving in the real world. Movement in the real world is always in relation to this origin point. + For example, if your character is driving a car, the XROrigin3D node should be a child node of this car. Or, if you're implementing a teleport system to move your character, you should change the position of this node. + + + https://docs.godotengine.org/en/latest/tutorials/vr/index.html + + + + + + Allows you to adjust the scale to your game's units. Most AR/VR platforms assume a scale of 1 game world unit = 1 real world meter. + [b]Note:[/b] This method is a passthrough to the [XRServer] itself. + + + + + diff --git a/doc/classes/XRPositionalTracker.xml b/doc/classes/XRPositionalTracker.xml new file mode 100644 index 0000000000..2f7cc21703 --- /dev/null +++ b/doc/classes/XRPositionalTracker.xml @@ -0,0 +1,111 @@ + + + + A tracked object. + + + An instance of this object represents a device that is tracked, such as a controller or anchor point. HMDs aren't represented here as they are handled internally. + As controllers are turned on and the AR/VR interface detects them, instances of this object are automatically added to this list of active tracking objects accessible through the [XRServer]. + The [XRController3D] and [XRAnchor3D] both consume objects of this type and should be used in your project. The positional trackers are just under-the-hood objects that make this all work. These are mostly exposed so that GDNative-based interfaces can interact with them. + + + https://docs.godotengine.org/en/latest/tutorials/vr/index.html + + + + + + + Returns the hand holding this tracker, if known. See [enum TrackerHand] constants. + + + + + + + If this is a controller that is being tracked, the controller will also be represented by a joystick entry with this ID. + + + + + + + Returns the mesh related to a controller or anchor point if one is available. + + + + + + + Returns the controller or anchor point's name if available. + + + + + + + Returns the controller's orientation matrix. + + + + + + + Returns the world-space controller position. + + + + + + + Returns the internal tracker ID. This uniquely identifies the tracker per tracker type and matches the ID you need to specify for nodes such as the [XRController3D] and [XRAnchor3D] nodes. + + + + + + + Returns [code]true[/code] if this device tracks orientation. + + + + + + + Returns [code]true[/code] if this device tracks position. + + + + + + + + + Returns the transform combining this device's orientation and position. + + + + + + + Returns the tracker's type. + + + + + + The degree to which the tracker rumbles. Ranges from [code]0.0[/code] to [code]1.0[/code] with precision [code].01[/code]. + + + + + The hand this tracker is held in is unknown or not applicable. + + + This tracker is the left hand controller. + + + This tracker is the right hand controller. + + + diff --git a/doc/classes/XRServer.xml b/doc/classes/XRServer.xml new file mode 100644 index 0000000000..5e6002aee3 --- /dev/null +++ b/doc/classes/XRServer.xml @@ -0,0 +1,188 @@ + + + + Server for AR and VR features. + + + The AR/VR server is the heart of our Advanced and Virtual Reality solution and handles all the processing. + + + https://docs.godotengine.org/en/latest/tutorials/vr/index.html + + + + + + + + + + + This is an important function to understand correctly. AR and VR platforms all handle positioning slightly differently. + For platforms that do not offer spatial tracking, our origin point (0,0,0) is the location of our HMD, but you have little control over the direction the player is facing in the real world. + For platforms that do offer spatial tracking, our origin point depends very much on the system. For OpenVR, our origin point is usually the center of the tracking space, on the ground. For other platforms, it's often the location of the tracking camera. + This method allows you to center your tracker on the location of the HMD. It will take the current location of the HMD and use that to adjust all your tracking data; in essence, realigning the real world to your player's current position in the game world. + For this method to produce usable results, tracking information must be available. This often takes a few frames after starting your game. + You should call this method after a few seconds have passed. For instance, when the user requests a realignment of the display holding a designated button on a controller for a short period of time, or when implementing a teleport mechanism. + + + + + + + + + Finds an interface by its name. For instance, if your project uses capabilities of an AR/VR platform, you can find the interface for that platform by name and initialize it. + + + + + + + Returns the primary interface's transformation. + + + + + + + + + Returns the interface registered at a given index in our list of interfaces. + + + + + + + Returns the number of interfaces currently registered with the AR/VR server. If your project supports multiple AR/VR platforms, you can look through the available interface, and either present the user with a selection or simply try to initialize each interface and use the first one that returns [code]true[/code]. + + + + + + + Returns a list of available interfaces the ID and name of each interface. + + + + + + + Returns the absolute timestamp (in μs) of the last [XRServer] commit of the AR/VR eyes to [RenderingServer]. The value comes from an internal call to [method OS.get_ticks_usec]. + + + + + + + Returns the duration (in μs) of the last frame. This is computed as the difference between [method get_last_commit_usec] and [method get_last_process_usec] when committing. + + + + + + + Returns the absolute timestamp (in μs) of the last [XRServer] process callback. The value comes from an internal call to [method OS.get_ticks_usec]. + + + + + + + Returns the reference frame transform. Mostly used internally and exposed for GDNative build interfaces. + + + + + + + + + Returns the positional tracker at the given ID. + + + + + + + Returns the number of trackers currently registered. + + + + + + The primary [XRInterface] currently bound to the [XRServer]. + + + Allows you to adjust the scale to your game's units. Most AR/VR platforms assume a scale of 1 game world unit = 1 real world meter. + + + + + + + + Emitted when a new interface has been added. + + + + + + + Emitted when an interface is removed. + + + + + + + + + + + Emitted when a new tracker has been added. If you don't use a fixed number of controllers or if you're using [XRAnchor3D]s for an AR solution, it is important to react to this signal to add the appropriate [XRController3D] or [XRAnchor3D] nodes related to this new tracker. + + + + + + + + + + + Emitted when a tracker is removed. You should remove any [XRController3D] or [XRAnchor3D] points if applicable. This is not mandatory, the nodes simply become inactive and will be made active again when a new tracker becomes available (i.e. a new controller is switched on that takes the place of the previous one). + + + + + + The tracker tracks the location of a controller. + + + The tracker tracks the location of a base station. + + + The tracker tracks the location and size of an AR anchor. + + + Used internally to filter trackers of any known type. + + + Used internally if we haven't set the tracker type yet. + + + Used internally to select all trackers. + + + Fully reset the orientation of the HMD. Regardless of what direction the user is looking to in the real world. The user will look dead ahead in the virtual world. + + + Resets the orientation but keeps the tilt of the device. So if we're looking down, we keep looking down but heading will be reset. + + + Does not reset the orientation of the HMD, only the position of the player gets centered. + + + diff --git a/main/main.cpp b/main/main.cpp index a53e52e485..92f40588f2 100644 --- a/main/main.cpp +++ b/main/main.cpp @@ -61,7 +61,6 @@ #include "scene/main/window.h" #include "scene/register_scene_types.h" #include "scene/resources/packed_scene.h" -#include "servers/arvr_server.h" #include "servers/audio_server.h" #include "servers/camera_server.h" #include "servers/display_server.h" @@ -72,6 +71,7 @@ #include "servers/register_server_types.h" #include "servers/rendering/rendering_server_raster.h" #include "servers/rendering/rendering_server_wrap_mt.h" +#include "servers/xr_server.h" #ifdef TOOLS_ENABLED #include "editor/doc_data.h" @@ -105,7 +105,7 @@ static AudioServer *audio_server = nullptr; static DisplayServer *display_server = nullptr; static RenderingServer *rendering_server = nullptr; static CameraServer *camera_server = nullptr; -static ARVRServer *arvr_server = nullptr; +static XRServer *xr_server = nullptr; static PhysicsServer3D *physics_server = nullptr; static PhysicsServer2D *physics_2d_server = nullptr; static NavigationServer3D *navigation_server = nullptr; @@ -1297,8 +1297,8 @@ Error Main::setup2(Thread::ID p_main_tid_override) { audio_server = memnew(AudioServer); audio_server->init(); - // also init our arvr_server from here - arvr_server = memnew(ARVRServer); + // also init our xr_server from here + xr_server = memnew(XRServer); register_core_singletons(); @@ -2276,9 +2276,9 @@ void Main::cleanup() { EditorNode::unregister_editor_types(); #endif - if (arvr_server) { + if (xr_server) { // cleanup now before we pull the rug from underneath... - memdelete(arvr_server); + memdelete(xr_server); } ImageLoader::cleanup(); diff --git a/modules/arkit/arkit_interface.h b/modules/arkit/arkit_interface.h index 4f8f726816..1044f3cf6f 100644 --- a/modules/arkit/arkit_interface.h +++ b/modules/arkit/arkit_interface.h @@ -31,9 +31,9 @@ #ifndef ARKIT_INTERFACE_H #define ARKIT_INTERFACE_H -#include "servers/arvr/arvr_interface.h" -#include "servers/arvr/arvr_positional_tracker.h" #include "servers/camera/camera_feed.h" +#include "servers/xr/xr_interface.h" +#include "servers/xr/xr_positional_tracker.h" /** @author Bastiaan Olij @@ -44,8 +44,8 @@ // forward declaration for some needed objects class ARKitShader; -class ARKitInterface : public ARVRInterface { - GDCLASS(ARKitInterface, ARVRInterface); +class ARKitInterface : public XRInterface { + GDCLASS(ARKitInterface, XRInterface); private: bool initialized; @@ -65,7 +65,7 @@ private: Vector img_data[2]; struct anchor_map { - ARVRPositionalTracker *tracker; + XRPositionalTracker *tracker; unsigned char uuid[16]; }; @@ -73,7 +73,7 @@ private: unsigned int num_anchors; unsigned int max_anchors; anchor_map *anchors; - ARVRPositionalTracker *get_anchor_for_uuid(const unsigned char *p_uuid); + XRPositionalTracker *get_anchor_for_uuid(const unsigned char *p_uuid); void remove_anchor_for_uuid(const unsigned char *p_uuid); void remove_all_anchors(); @@ -108,9 +108,9 @@ public: virtual Size2 get_render_targetsize(); virtual bool is_stereo(); - virtual Transform get_transform_for_eye(ARVRInterface::Eyes p_eye, const Transform &p_cam_transform); - virtual CameraMatrix get_projection_for_eye(ARVRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far); - virtual void commit_for_eye(ARVRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect); + virtual Transform get_transform_for_eye(XRInterface::Eyes p_eye, const Transform &p_cam_transform); + virtual CameraMatrix get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far); + virtual void commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect); virtual void process(); diff --git a/modules/arkit/arkit_interface.mm b/modules/arkit/arkit_interface.mm index 031e1e115e..79f09e2a7e 100644 --- a/modules/arkit/arkit_interface.mm +++ b/modules/arkit/arkit_interface.mm @@ -158,7 +158,7 @@ StringName ARKitInterface::get_name() const { } int ARKitInterface::get_capabilities() const { - return ARKitInterface::ARVR_MONO + ARKitInterface::ARVR_AR; + return ARKitInterface::XR_MONO + ARKitInterface::XR_AR; } Array ARKitInterface::raycast(Vector2 p_screen_coord) { @@ -218,8 +218,8 @@ bool ARKitInterface::is_initialized() const { } bool ARKitInterface::initialize() { - ARVRServer *arvr_server = ARVRServer::get_singleton(); - ERR_FAIL_NULL_V(arvr_server, false); + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL_V(xr_server, false); if (!initialized) { print_line("initializing ARKit"); @@ -244,7 +244,7 @@ bool ARKitInterface::initialize() { transform = Transform(); // make this our primary interface - arvr_server->set_primary_interface(this); + xr_server->set_primary_interface(this); // make sure we have our feed setup if (feed.is_null()) { @@ -270,10 +270,10 @@ bool ARKitInterface::initialize() { void ARKitInterface::uninitialize() { if (initialized) { - ARVRServer *arvr_server = ARVRServer::get_singleton(); - if (arvr_server != NULL) { + XRServer *xr_server = XRServer::get_singleton(); + if (xr_server != NULL) { // no longer our primary interface - arvr_server->clear_primary_interface_if(this); + xr_server->clear_primary_interface_if(this); } if (feed.is_valid()) { @@ -303,22 +303,22 @@ Size2 ARKitInterface::get_render_targetsize() { return target_size; } -Transform ARKitInterface::get_transform_for_eye(ARVRInterface::Eyes p_eye, const Transform &p_cam_transform) { +Transform ARKitInterface::get_transform_for_eye(XRInterface::Eyes p_eye, const Transform &p_cam_transform) { _THREAD_SAFE_METHOD_ Transform transform_for_eye; - ARVRServer *arvr_server = ARVRServer::get_singleton(); - ERR_FAIL_NULL_V(arvr_server, transform_for_eye); + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL_V(xr_server, transform_for_eye); if (initialized) { - float world_scale = arvr_server->get_world_scale(); + float world_scale = xr_server->get_world_scale(); // just scale our origin point of our transform, note that we really shouldn't be using world_scale in ARKit but.... transform_for_eye = transform; transform_for_eye.origin *= world_scale; - transform_for_eye = p_cam_transform * arvr_server->get_reference_frame() * transform_for_eye; + transform_for_eye = p_cam_transform * xr_server->get_reference_frame() * transform_for_eye; } else { // huh? well just return what we got.... transform_for_eye = p_cam_transform; @@ -327,7 +327,7 @@ Transform ARKitInterface::get_transform_for_eye(ARVRInterface::Eyes p_eye, const return transform_for_eye; } -CameraMatrix ARKitInterface::get_projection_for_eye(ARVRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) { +CameraMatrix ARKitInterface::get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) { // Remember our near and far, it will be used in process when we obtain our projection from our ARKit session. z_near = p_z_near; z_far = p_z_far; @@ -335,7 +335,7 @@ CameraMatrix ARKitInterface::get_projection_for_eye(ARVRInterface::Eyes p_eye, r return projection; } -void ARKitInterface::commit_for_eye(ARVRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) { +void ARKitInterface::commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) { _THREAD_SAFE_METHOD_ // We must have a valid render target @@ -356,7 +356,7 @@ void ARKitInterface::commit_for_eye(ARVRInterface::Eyes p_eye, RID p_render_targ VSG::rasterizer->blit_render_target_to_screen(p_render_target, screen_rect, 0); } -ARVRPositionalTracker *ARKitInterface::get_anchor_for_uuid(const unsigned char *p_uuid) { +XRPositionalTracker *ARKitInterface::get_anchor_for_uuid(const unsigned char *p_uuid) { if (anchors == NULL) { num_anchors = 0; max_anchors = 10; @@ -377,8 +377,8 @@ ARVRPositionalTracker *ARKitInterface::get_anchor_for_uuid(const unsigned char * ERR_FAIL_NULL_V(anchors, NULL); } - ARVRPositionalTracker *new_tracker = memnew(ARVRPositionalTracker); - new_tracker->set_type(ARVRServer::TRACKER_ANCHOR); + XRPositionalTracker *new_tracker = memnew(XRPositionalTracker); + new_tracker->set_type(XRServer::TRACKER_ANCHOR); char tracker_name[256]; sprintf(tracker_name, "Anchor %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", p_uuid[0], p_uuid[1], p_uuid[2], p_uuid[3], p_uuid[4], p_uuid[5], p_uuid[6], p_uuid[7], p_uuid[8], p_uuid[9], p_uuid[10], p_uuid[11], p_uuid[12], p_uuid[13], p_uuid[14], p_uuid[15]); @@ -388,7 +388,7 @@ ARVRPositionalTracker *ARKitInterface::get_anchor_for_uuid(const unsigned char * new_tracker->set_name(name); // add our tracker - ARVRServer::get_singleton()->add_tracker(new_tracker); + XRServer::get_singleton()->add_tracker(new_tracker); anchors[num_anchors].tracker = new_tracker; memcpy(anchors[num_anchors].uuid, p_uuid, 16); num_anchors++; @@ -401,7 +401,7 @@ void ARKitInterface::remove_anchor_for_uuid(const unsigned char *p_uuid) { for (unsigned int i = 0; i < num_anchors; i++) { if (memcmp(anchors[i].uuid, p_uuid, 16) == 0) { // remove our tracker - ARVRServer::get_singleton()->remove_tracker(anchors[i].tracker); + XRServer::get_singleton()->remove_tracker(anchors[i].tracker); memdelete(anchors[i].tracker); // bring remaining forward @@ -421,7 +421,7 @@ void ARKitInterface::remove_all_anchors() { if (anchors != NULL) { for (unsigned int i = 0; i < num_anchors; i++) { // remove our tracker - ARVRServer::get_singleton()->remove_tracker(anchors[i].tracker); + XRServer::get_singleton()->remove_tracker(anchors[i].tracker); memdelete(anchors[i].tracker); }; @@ -582,16 +582,16 @@ void ARKitInterface::process() { // strangely enough we have to states, rolling them up into one if (camera.trackingState == ARTrackingStateNotAvailable) { // no tracking, would be good if we black out the screen or something... - tracking_state = ARVRInterface::ARVR_NOT_TRACKING; + tracking_state = XRInterface::XR_NOT_TRACKING; } else { if (camera.trackingState == ARTrackingStateNormal) { - tracking_state = ARVRInterface::ARVR_NORMAL_TRACKING; + tracking_state = XRInterface::XR_NORMAL_TRACKING; } else if (camera.trackingStateReason == ARTrackingStateReasonExcessiveMotion) { - tracking_state = ARVRInterface::ARVR_EXCESSIVE_MOTION; + tracking_state = XRInterface::XR_EXCESSIVE_MOTION; } else if (camera.trackingStateReason == ARTrackingStateReasonInsufficientFeatures) { - tracking_state = ARVRInterface::ARVR_INSUFFICIENT_FEATURES; + tracking_state = XRInterface::XR_INSUFFICIENT_FEATURES; } else { - tracking_state = ARVRInterface::ARVR_UNKNOWN_TRACKING; + tracking_state = XRInterface::XR_UNKNOWN_TRACKING; } // copy our current frame transform @@ -665,7 +665,7 @@ void ARKitInterface::_add_or_update_anchor(void *p_anchor) { unsigned char uuid[16]; [anchor.identifier getUUIDBytes:uuid]; - ARVRPositionalTracker *tracker = get_anchor_for_uuid(uuid); + XRPositionalTracker *tracker = get_anchor_for_uuid(uuid); if (tracker != NULL) { // lets update our mesh! (using Arjens code as is for now) // we should also probably limit how often we do this... @@ -695,7 +695,7 @@ void ARKitInterface::_add_or_update_anchor(void *p_anchor) { } // Note, this also contains a scale factor which gives us an idea of the size of the anchor - // We may extract that in our ARVRAnchor class + // We may extract that in our XRAnchor class Basis b; matrix_float4x4 m44 = anchor.transform; b.elements[0].x = m44.columns[0][0]; diff --git a/modules/arkit/register_types.cpp b/modules/arkit/register_types.cpp index c78b35529b..91069ab364 100644 --- a/modules/arkit/register_types.cpp +++ b/modules/arkit/register_types.cpp @@ -37,7 +37,7 @@ void register_arkit_types() { Ref arkit_interface; arkit_interface.instance(); - ARVRServer::get_singleton()->add_interface(arkit_interface); + XRServer::get_singleton()->add_interface(arkit_interface); } void unregister_arkit_types() { diff --git a/modules/gdnative/SCsub b/modules/gdnative/SCsub index a788175b07..cab05549d2 100644 --- a/modules/gdnative/SCsub +++ b/modules/gdnative/SCsub @@ -17,7 +17,7 @@ env_gdnative.Prepend(CPPPATH=["#modules/gdnative/include/"]) Export("env_gdnative") SConscript("net/SCsub") -SConscript("arvr/SCsub") +SConscript("xr/SCsub") SConscript("pluginscript/SCsub") SConscript("videodecoder/SCsub") diff --git a/modules/gdnative/arvr/SCsub b/modules/gdnative/arvr/SCsub deleted file mode 100644 index 0b2db3b504..0000000000 --- a/modules/gdnative/arvr/SCsub +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env python - -Import("env") -Import("env_gdnative") - -env_gdnative.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/gdnative/arvr/arvr_interface_gdnative.cpp b/modules/gdnative/arvr/arvr_interface_gdnative.cpp deleted file mode 100644 index f14691027a..0000000000 --- a/modules/gdnative/arvr/arvr_interface_gdnative.cpp +++ /dev/null @@ -1,439 +0,0 @@ -/*************************************************************************/ -/* arvr_interface_gdnative.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.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 "arvr_interface_gdnative.h" -#include "core/input/input_filter.h" -#include "servers/arvr/arvr_positional_tracker.h" -#include "servers/rendering/rendering_server_globals.h" - -void ARVRInterfaceGDNative::_bind_methods() { - ADD_PROPERTY_DEFAULT("interface_is_initialized", false); - ADD_PROPERTY_DEFAULT("ar_is_anchor_detection_enabled", false); -} - -ARVRInterfaceGDNative::ARVRInterfaceGDNative() { - print_verbose("Construct gdnative interface\n"); - - // we won't have our data pointer until our library gets set - data = nullptr; - - interface = nullptr; -} - -ARVRInterfaceGDNative::~ARVRInterfaceGDNative() { - print_verbose("Destruct gdnative interface\n"); - - if (interface != nullptr && is_initialized()) { - uninitialize(); - }; - - // cleanup after ourselves - cleanup(); -} - -void ARVRInterfaceGDNative::cleanup() { - if (interface != nullptr) { - interface->destructor(data); - data = nullptr; - interface = nullptr; - } -} - -void ARVRInterfaceGDNative::set_interface(const godot_arvr_interface_gdnative *p_interface) { - // this should only be called once, just being paranoid.. - if (interface) { - cleanup(); - } - - // bind to our interface - interface = p_interface; - - // Now we do our constructing... - data = interface->constructor((godot_object *)this); -} - -StringName ARVRInterfaceGDNative::get_name() const { - - ERR_FAIL_COND_V(interface == nullptr, StringName()); - - godot_string result = interface->get_name(data); - - StringName name = *(String *)&result; - - godot_string_destroy(&result); - - return name; -} - -int ARVRInterfaceGDNative::get_capabilities() const { - int capabilities; - - ERR_FAIL_COND_V(interface == nullptr, 0); // 0 = None - - capabilities = interface->get_capabilities(data); - - return capabilities; -} - -bool ARVRInterfaceGDNative::get_anchor_detection_is_enabled() const { - - ERR_FAIL_COND_V(interface == nullptr, false); - - return interface->get_anchor_detection_is_enabled(data); -} - -void ARVRInterfaceGDNative::set_anchor_detection_is_enabled(bool p_enable) { - - ERR_FAIL_COND(interface == nullptr); - - interface->set_anchor_detection_is_enabled(data, p_enable); -} - -int ARVRInterfaceGDNative::get_camera_feed_id() { - - ERR_FAIL_COND_V(interface == nullptr, 0); - - if ((interface->version.major > 1) || ((interface->version.major) == 1 && (interface->version.minor >= 1))) { - return (unsigned int)interface->get_camera_feed_id(data); - } else { - return 0; - } -} - -bool ARVRInterfaceGDNative::is_stereo() { - bool stereo; - - ERR_FAIL_COND_V(interface == nullptr, false); - - stereo = interface->is_stereo(data); - - return stereo; -} - -bool ARVRInterfaceGDNative::is_initialized() const { - - ERR_FAIL_COND_V(interface == nullptr, false); - - return interface->is_initialized(data); -} - -bool ARVRInterfaceGDNative::initialize() { - ERR_FAIL_COND_V(interface == nullptr, false); - - bool initialized = interface->initialize(data); - - if (initialized) { - // if we successfully initialize our interface and we don't have a primary interface yet, this becomes our primary interface - - ARVRServer *arvr_server = ARVRServer::get_singleton(); - if ((arvr_server != nullptr) && (arvr_server->get_primary_interface() == nullptr)) { - arvr_server->set_primary_interface(this); - }; - }; - - return initialized; -} - -void ARVRInterfaceGDNative::uninitialize() { - ERR_FAIL_COND(interface == nullptr); - - ARVRServer *arvr_server = ARVRServer::get_singleton(); - if (arvr_server != nullptr) { - // Whatever happens, make sure this is no longer our primary interface - arvr_server->clear_primary_interface_if(this); - } - - interface->uninitialize(data); -} - -Size2 ARVRInterfaceGDNative::get_render_targetsize() { - - ERR_FAIL_COND_V(interface == nullptr, Size2()); - - godot_vector2 result = interface->get_render_targetsize(data); - Vector2 *vec = (Vector2 *)&result; - - return *vec; -} - -Transform ARVRInterfaceGDNative::get_transform_for_eye(ARVRInterface::Eyes p_eye, const Transform &p_cam_transform) { - Transform *ret; - - ERR_FAIL_COND_V(interface == nullptr, Transform()); - - godot_transform t = interface->get_transform_for_eye(data, (int)p_eye, (godot_transform *)&p_cam_transform); - - ret = (Transform *)&t; - - return *ret; -} - -CameraMatrix ARVRInterfaceGDNative::get_projection_for_eye(ARVRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) { - CameraMatrix cm; - - ERR_FAIL_COND_V(interface == nullptr, CameraMatrix()); - - interface->fill_projection_for_eye(data, (godot_real *)cm.matrix, (godot_int)p_eye, p_aspect, p_z_near, p_z_far); - - return cm; -} - -unsigned int ARVRInterfaceGDNative::get_external_texture_for_eye(ARVRInterface::Eyes p_eye) { - - ERR_FAIL_COND_V(interface == nullptr, 0); - - if ((interface->version.major > 1) || ((interface->version.major) == 1 && (interface->version.minor >= 1))) { - return (unsigned int)interface->get_external_texture_for_eye(data, (godot_int)p_eye); - } else { - return 0; - } -} - -void ARVRInterfaceGDNative::commit_for_eye(ARVRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) { - - ERR_FAIL_COND(interface == nullptr); - - interface->commit_for_eye(data, (godot_int)p_eye, (godot_rid *)&p_render_target, (godot_rect2 *)&p_screen_rect); -} - -void ARVRInterfaceGDNative::process() { - ERR_FAIL_COND(interface == nullptr); - - interface->process(data); -} - -void ARVRInterfaceGDNative::notification(int p_what) { - ERR_FAIL_COND(interface == nullptr); - - // this is only available in interfaces that implement 1.1 or later - if ((interface->version.major > 1) || ((interface->version.major == 1) && (interface->version.minor > 0))) { - interface->notification(data, p_what); - } -} - -///////////////////////////////////////////////////////////////////////////////////// -// some helper callbacks - -extern "C" { - -void GDAPI godot_arvr_register_interface(const godot_arvr_interface_gdnative *p_interface) { - // If our major version is 0 or bigger then 10, we're likely looking at our constructor pointer from an older plugin - ERR_FAIL_COND_MSG((p_interface->version.major == 0) || (p_interface->version.major > 10), "GDNative ARVR interfaces build for Godot 3.0 are not supported."); - - Ref new_interface; - new_interface.instance(); - new_interface->set_interface((const godot_arvr_interface_gdnative *)p_interface); - ARVRServer::get_singleton()->add_interface(new_interface); -} - -godot_real GDAPI godot_arvr_get_worldscale() { - ARVRServer *arvr_server = ARVRServer::get_singleton(); - ERR_FAIL_NULL_V(arvr_server, 1.0); - - return arvr_server->get_world_scale(); -} - -godot_transform GDAPI godot_arvr_get_reference_frame() { - godot_transform reference_frame; - Transform *reference_frame_ptr = (Transform *)&reference_frame; - - ARVRServer *arvr_server = ARVRServer::get_singleton(); - if (arvr_server != nullptr) { - *reference_frame_ptr = arvr_server->get_reference_frame(); - } else { - godot_transform_new_identity(&reference_frame); - } - - return reference_frame; -} - -void GDAPI godot_arvr_blit(godot_int p_eye, godot_rid *p_render_target, godot_rect2 *p_rect) { - // blits out our texture as is, handy for preview display of one of the eyes that is already rendered with lens distortion on an external HMD - ARVRInterface::Eyes eye = (ARVRInterface::Eyes)p_eye; -#if 0 - RID *render_target = (RID *)p_render_target; -#endif - Rect2 screen_rect = *(Rect2 *)p_rect; - - if (eye == ARVRInterface::EYE_LEFT) { - screen_rect.size.x /= 2.0; - } else if (p_eye == ARVRInterface::EYE_RIGHT) { - screen_rect.size.x /= 2.0; - screen_rect.position.x += screen_rect.size.x; - } -#ifndef _MSC_VER -#warning this needs to be redone -#endif -#if 0 - RSG::rasterizer->blit_render_target_to_screen(*render_target, screen_rect, 0); -#endif -} - -godot_int GDAPI godot_arvr_get_texid(godot_rid *p_render_target) { - // In order to send off our textures to display on our hardware we need the opengl texture ID instead of the render target RID - // This is a handy function to expose that. -#if 0 - RID *render_target = (RID *)p_render_target; - - RID eye_texture = RSG::storage->render_target_get_texture(*render_target); -#endif - -#ifndef _MSC_VER -#warning need to obtain this ID again -#endif - uint32_t texid = 0; //RS::get_singleton()->texture_get_texid(eye_texture); - - return texid; -} - -godot_int GDAPI godot_arvr_add_controller(char *p_device_name, godot_int p_hand, godot_bool p_tracks_orientation, godot_bool p_tracks_position) { - ARVRServer *arvr_server = ARVRServer::get_singleton(); - ERR_FAIL_NULL_V(arvr_server, 0); - - InputFilter *input = InputFilter::get_singleton(); - ERR_FAIL_NULL_V(input, 0); - - ARVRPositionalTracker *new_tracker = memnew(ARVRPositionalTracker); - new_tracker->set_name(p_device_name); - new_tracker->set_type(ARVRServer::TRACKER_CONTROLLER); - if (p_hand == 1) { - new_tracker->set_hand(ARVRPositionalTracker::TRACKER_LEFT_HAND); - } else if (p_hand == 2) { - new_tracker->set_hand(ARVRPositionalTracker::TRACKER_RIGHT_HAND); - } - - // also register as joystick... - int joyid = input->get_unused_joy_id(); - if (joyid != -1) { - new_tracker->set_joy_id(joyid); - input->joy_connection_changed(joyid, true, p_device_name, ""); - } - - if (p_tracks_orientation) { - Basis orientation; - new_tracker->set_orientation(orientation); - } - if (p_tracks_position) { - Vector3 position; - new_tracker->set_position(position); - } - - // add our tracker to our server and remember its pointer - arvr_server->add_tracker(new_tracker); - - // note, this ID is only unique within controllers! - return new_tracker->get_tracker_id(); -} - -void GDAPI godot_arvr_remove_controller(godot_int p_controller_id) { - ARVRServer *arvr_server = ARVRServer::get_singleton(); - ERR_FAIL_NULL(arvr_server); - - InputFilter *input = InputFilter::get_singleton(); - ERR_FAIL_NULL(input); - - ARVRPositionalTracker *remove_tracker = arvr_server->find_by_type_and_id(ARVRServer::TRACKER_CONTROLLER, p_controller_id); - if (remove_tracker != nullptr) { - // unset our joystick if applicable - int joyid = remove_tracker->get_joy_id(); - if (joyid != -1) { - input->joy_connection_changed(joyid, false, "", ""); - remove_tracker->set_joy_id(-1); - } - - // remove our tracker from our server - arvr_server->remove_tracker(remove_tracker); - memdelete(remove_tracker); - } -} - -void GDAPI godot_arvr_set_controller_transform(godot_int p_controller_id, godot_transform *p_transform, godot_bool p_tracks_orientation, godot_bool p_tracks_position) { - ARVRServer *arvr_server = ARVRServer::get_singleton(); - ERR_FAIL_NULL(arvr_server); - - ARVRPositionalTracker *tracker = arvr_server->find_by_type_and_id(ARVRServer::TRACKER_CONTROLLER, p_controller_id); - if (tracker != nullptr) { - Transform *transform = (Transform *)p_transform; - if (p_tracks_orientation) { - tracker->set_orientation(transform->basis); - } - if (p_tracks_position) { - tracker->set_rw_position(transform->origin); - } - } -} - -void GDAPI godot_arvr_set_controller_button(godot_int p_controller_id, godot_int p_button, godot_bool p_is_pressed) { - ARVRServer *arvr_server = ARVRServer::get_singleton(); - ERR_FAIL_NULL(arvr_server); - - InputFilter *input = InputFilter::get_singleton(); - ERR_FAIL_NULL(input); - - ARVRPositionalTracker *tracker = arvr_server->find_by_type_and_id(ARVRServer::TRACKER_CONTROLLER, p_controller_id); - if (tracker != nullptr) { - int joyid = tracker->get_joy_id(); - if (joyid != -1) { - input->joy_button(joyid, p_button, p_is_pressed); - } - } -} - -void GDAPI godot_arvr_set_controller_axis(godot_int p_controller_id, godot_int p_axis, godot_real p_value, godot_bool p_can_be_negative) { - ARVRServer *arvr_server = ARVRServer::get_singleton(); - ERR_FAIL_NULL(arvr_server); - - InputFilter *input = InputFilter::get_singleton(); - ERR_FAIL_NULL(input); - - ARVRPositionalTracker *tracker = arvr_server->find_by_type_and_id(ARVRServer::TRACKER_CONTROLLER, p_controller_id); - if (tracker != nullptr) { - int joyid = tracker->get_joy_id(); - if (joyid != -1) { - InputFilter::JoyAxis jx; - jx.min = p_can_be_negative ? -1 : 0; - jx.value = p_value; - input->joy_axis(joyid, p_axis, jx); - } - } -} - -godot_real GDAPI godot_arvr_get_controller_rumble(godot_int p_controller_id) { - ARVRServer *arvr_server = ARVRServer::get_singleton(); - ERR_FAIL_NULL_V(arvr_server, 0.0); - - ARVRPositionalTracker *tracker = arvr_server->find_by_type_and_id(ARVRServer::TRACKER_CONTROLLER, p_controller_id); - if (tracker != nullptr) { - return tracker->get_rumble(); - } - - return 0.0; -} -} diff --git a/modules/gdnative/arvr/arvr_interface_gdnative.h b/modules/gdnative/arvr/arvr_interface_gdnative.h deleted file mode 100644 index e38eb435c6..0000000000 --- a/modules/gdnative/arvr/arvr_interface_gdnative.h +++ /dev/null @@ -1,91 +0,0 @@ -/*************************************************************************/ -/* arvr_interface_gdnative.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.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. */ -/*************************************************************************/ - -#ifndef ARVR_INTERFACE_GDNATIVE_H -#define ARVR_INTERFACE_GDNATIVE_H - -#include "modules/gdnative/gdnative.h" -#include "servers/arvr/arvr_interface.h" - -/** - @authors Hinsbart & Karroffel & Mux213 - - This subclass of our AR/VR interface forms a bridge to GDNative. -*/ - -class ARVRInterfaceGDNative : public ARVRInterface { - GDCLASS(ARVRInterfaceGDNative, ARVRInterface); - - void cleanup(); - -protected: - const godot_arvr_interface_gdnative *interface; - void *data; - - static void _bind_methods(); - -public: - /** general interface information **/ - ARVRInterfaceGDNative(); - ~ARVRInterfaceGDNative(); - - void set_interface(const godot_arvr_interface_gdnative *p_interface); - - virtual StringName get_name() const; - virtual int get_capabilities() const; - - virtual bool is_initialized() const; - virtual bool initialize(); - virtual void uninitialize(); - - /** specific to AR **/ - virtual bool get_anchor_detection_is_enabled() const; - virtual void set_anchor_detection_is_enabled(bool p_enable); - virtual int get_camera_feed_id(); - - /** rendering and internal **/ - virtual Size2 get_render_targetsize(); - virtual bool is_stereo(); - virtual Transform get_transform_for_eye(ARVRInterface::Eyes p_eye, const Transform &p_cam_transform); - - // we expose a Vector version of this function to GDNative - Vector _get_projection_for_eye(ARVRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far); - - // and a CameraMatrix version to ARVRServer - virtual CameraMatrix get_projection_for_eye(ARVRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far); - - virtual unsigned int get_external_texture_for_eye(ARVRInterface::Eyes p_eye); - virtual void commit_for_eye(ARVRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect); - - virtual void process(); - virtual void notification(int p_what); -}; - -#endif // ARVR_INTERFACE_GDNATIVE_H diff --git a/modules/gdnative/arvr/config.py b/modules/gdnative/arvr/config.py deleted file mode 100644 index d22f9454ed..0000000000 --- a/modules/gdnative/arvr/config.py +++ /dev/null @@ -1,6 +0,0 @@ -def can_build(env, platform): - return True - - -def configure(env): - pass diff --git a/modules/gdnative/arvr/register_types.cpp b/modules/gdnative/arvr/register_types.cpp deleted file mode 100644 index 0f6e2bca1a..0000000000 --- a/modules/gdnative/arvr/register_types.cpp +++ /dev/null @@ -1,39 +0,0 @@ -/*************************************************************************/ -/* register_types.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.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 "register_types.h" -#include "arvr_interface_gdnative.h" - -void register_arvr_types() { - ClassDB::register_class(); -} - -void unregister_arvr_types() { -} diff --git a/modules/gdnative/arvr/register_types.h b/modules/gdnative/arvr/register_types.h deleted file mode 100644 index b0de6f7c14..0000000000 --- a/modules/gdnative/arvr/register_types.h +++ /dev/null @@ -1,37 +0,0 @@ -/*************************************************************************/ -/* register_types.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.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. */ -/*************************************************************************/ - -#ifndef ARVR_REGISTER_TYPES_H -#define ARVR_REGISTER_TYPES_H - -void register_arvr_types(); -void unregister_arvr_types(); - -#endif // ARVR_REGISTER_TYPES_H diff --git a/modules/gdnative/config.py b/modules/gdnative/config.py index 37e25a46d4..4b997e4bfe 100644 --- a/modules/gdnative/config.py +++ b/modules/gdnative/config.py @@ -9,7 +9,7 @@ def configure(env): def get_doc_classes(): return [ "@NativeScript", - "ARVRInterfaceGDNative", + "XRInterfaceGDNative", "GDNative", "GDNativeLibrary", "MultiplayerPeerGDNative", diff --git a/modules/gdnative/doc_classes/ARVRInterfaceGDNative.xml b/modules/gdnative/doc_classes/ARVRInterfaceGDNative.xml deleted file mode 100644 index e8405b64a3..0000000000 --- a/modules/gdnative/doc_classes/ARVRInterfaceGDNative.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - GDNative wrapper for an ARVR interface. - - - This is a wrapper class for GDNative implementations of the ARVR interface. To use a GDNative ARVR interface, simply instantiate this object and set your GDNative library containing the ARVR interface implementation. - - - - - - - - diff --git a/modules/gdnative/doc_classes/GDNativeLibrary.xml b/modules/gdnative/doc_classes/GDNativeLibrary.xml index 601e132d42..1aab864102 100644 --- a/modules/gdnative/doc_classes/GDNativeLibrary.xml +++ b/modules/gdnative/doc_classes/GDNativeLibrary.xml @@ -4,7 +4,7 @@ An external library containing functions or script classes to use in Godot. - A GDNative library can implement [NativeScript]s, global functions to call with the [GDNative] class, or low-level engine extensions through interfaces such as [ARVRInterfaceGDNative]. The library must be compiled for each platform and architecture that the project will run on. + A GDNative library can implement [NativeScript]s, global functions to call with the [GDNative] class, or low-level engine extensions through interfaces such as [XRInterfaceGDNative]. The library must be compiled for each platform and architecture that the project will run on. https://docs.godotengine.org/en/latest/tutorials/plugins/gdnative/gdnative-c-example.html diff --git a/modules/gdnative/doc_classes/XRInterfaceGDNative.xml b/modules/gdnative/doc_classes/XRInterfaceGDNative.xml new file mode 100644 index 0000000000..13de815793 --- /dev/null +++ b/modules/gdnative/doc_classes/XRInterfaceGDNative.xml @@ -0,0 +1,15 @@ + + + + GDNative wrapper for an XR interface. + + + This is a wrapper class for GDNative implementations of the XR interface. To use a GDNative XR interface, simply instantiate this object and set your GDNative library containing the XR interface implementation. + + + + + + + + diff --git a/modules/gdnative/gdnative_api.json b/modules/gdnative/gdnative_api.json index e1d6c0c867..9473a3d419 100644 --- a/modules/gdnative/gdnative_api.json +++ b/modules/gdnative/gdnative_api.json @@ -5935,8 +5935,8 @@ ] }, { - "name": "arvr", - "type": "ARVR", + "name": "xr", + "type": "XR", "version": { "major": 1, "minor": 1 @@ -5944,24 +5944,24 @@ "next": null, "api": [ { - "name": "godot_arvr_register_interface", + "name": "godot_xr_register_interface", "return_type": "void", "arguments": [ - ["const godot_arvr_interface_gdnative *", "p_interface"] + ["const godot_xr_interface_gdnative *", "p_interface"] ] }, { - "name": "godot_arvr_get_worldscale", + "name": "godot_xr_get_worldscale", "return_type": "godot_real", "arguments": [] }, { - "name": "godot_arvr_get_reference_frame", + "name": "godot_xr_get_reference_frame", "return_type": "godot_transform", "arguments": [] }, { - "name": "godot_arvr_blit", + "name": "godot_xr_blit", "return_type": "void", "arguments": [ ["godot_int", "p_eye"], @@ -5970,14 +5970,14 @@ ] }, { - "name": "godot_arvr_get_texid", + "name": "godot_xr_get_texid", "return_type": "godot_int", "arguments": [ ["godot_rid *", "p_render_target"] ] }, { - "name": "godot_arvr_add_controller", + "name": "godot_xr_add_controller", "return_type": "godot_int", "arguments": [ ["char *", "p_device_name"], @@ -5987,14 +5987,14 @@ ] }, { - "name": "godot_arvr_remove_controller", + "name": "godot_xr_remove_controller", "return_type": "void", "arguments": [ ["godot_int", "p_controller_id"] ] }, { - "name": "godot_arvr_set_controller_transform", + "name": "godot_xr_set_controller_transform", "return_type": "void", "arguments": [ ["godot_int", "p_controller_id"], @@ -6004,7 +6004,7 @@ ] }, { - "name": "godot_arvr_set_controller_button", + "name": "godot_xr_set_controller_button", "return_type": "void", "arguments": [ ["godot_int", "p_controller_id"], @@ -6013,7 +6013,7 @@ ] }, { - "name": "godot_arvr_set_controller_axis", + "name": "godot_xr_set_controller_axis", "return_type": "void", "arguments": [ ["godot_int", "p_controller_id"], @@ -6023,7 +6023,7 @@ ] }, { - "name": "godot_arvr_get_controller_rumble", + "name": "godot_xr_get_controller_rumble", "return_type": "godot_real", "arguments": [ ["godot_int", "p_controller_id"] diff --git a/modules/gdnative/gdnative_builders.py b/modules/gdnative/gdnative_builders.py index 2d84f93d87..620935795f 100644 --- a/modules/gdnative/gdnative_builders.py +++ b/modules/gdnative/gdnative_builders.py @@ -19,7 +19,7 @@ def _build_gdnative_api_struct_header(api): "", "#include ", "#include ", - "#include ", + "#include ", "#include ", "#include ", "#include ", diff --git a/modules/gdnative/include/arvr/godot_arvr.h b/modules/gdnative/include/arvr/godot_arvr.h deleted file mode 100644 index aaef31a855..0000000000 --- a/modules/gdnative/include/arvr/godot_arvr.h +++ /dev/null @@ -1,92 +0,0 @@ -/*************************************************************************/ -/* godot_arvr.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.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. */ -/*************************************************************************/ - -#ifndef GODOT_NATIVEARVR_H -#define GODOT_NATIVEARVR_H - -#include - -#ifdef __cplusplus -extern "C" { -#endif - -// For future versions of the API we should only add new functions at the end of the structure and use the -// version info to detect whether a call is available - -// Use these to populate version in your plugin -#define GODOTVR_API_MAJOR 1 -#define GODOTVR_API_MINOR 1 - -typedef struct { - godot_gdnative_api_version version; /* version of our API */ - void *(*constructor)(godot_object *); - void (*destructor)(void *); - godot_string (*get_name)(const void *); - godot_int (*get_capabilities)(const void *); - godot_bool (*get_anchor_detection_is_enabled)(const void *); - void (*set_anchor_detection_is_enabled)(void *, godot_bool); - godot_bool (*is_stereo)(const void *); - godot_bool (*is_initialized)(const void *); - godot_bool (*initialize)(void *); - void (*uninitialize)(void *); - godot_vector2 (*get_render_targetsize)(const void *); - godot_transform (*get_transform_for_eye)(void *, godot_int, godot_transform *); - void (*fill_projection_for_eye)(void *, godot_real *, godot_int, godot_real, godot_real, godot_real); - void (*commit_for_eye)(void *, godot_int, godot_rid *, godot_rect2 *); - void (*process)(void *); - // only in 1.1 onwards - godot_int (*get_external_texture_for_eye)(void *, godot_int); - void (*notification)(void *, godot_int); - godot_int (*get_camera_feed_id)(void *); -} godot_arvr_interface_gdnative; - -void GDAPI godot_arvr_register_interface(const godot_arvr_interface_gdnative *p_interface); - -// helper functions to access ARVRServer data -godot_real GDAPI godot_arvr_get_worldscale(); -godot_transform GDAPI godot_arvr_get_reference_frame(); - -// helper functions for rendering -void GDAPI godot_arvr_blit(godot_int p_eye, godot_rid *p_render_target, godot_rect2 *p_rect); -godot_int GDAPI godot_arvr_get_texid(godot_rid *p_render_target); - -// helper functions for updating ARVR controllers -godot_int GDAPI godot_arvr_add_controller(char *p_device_name, godot_int p_hand, godot_bool p_tracks_orientation, godot_bool p_tracks_position); -void GDAPI godot_arvr_remove_controller(godot_int p_controller_id); -void GDAPI godot_arvr_set_controller_transform(godot_int p_controller_id, godot_transform *p_transform, godot_bool p_tracks_orientation, godot_bool p_tracks_position); -void GDAPI godot_arvr_set_controller_button(godot_int p_controller_id, godot_int p_button, godot_bool p_is_pressed); -void GDAPI godot_arvr_set_controller_axis(godot_int p_controller_id, godot_int p_axis, godot_real p_value, godot_bool p_can_be_negative); -godot_real GDAPI godot_arvr_get_controller_rumble(godot_int p_controller_id); - -#ifdef __cplusplus -} -#endif - -#endif /* !GODOT_NATIVEARVR_H */ diff --git a/modules/gdnative/include/xr/godot_xr.h b/modules/gdnative/include/xr/godot_xr.h new file mode 100644 index 0000000000..22f7f021c4 --- /dev/null +++ b/modules/gdnative/include/xr/godot_xr.h @@ -0,0 +1,91 @@ +/*************************************************************************/ +/* godot_xr.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.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. */ +/*************************************************************************/ + +#ifndef GODOT_NATIVEXR_H +#define GODOT_NATIVEXR_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +// For future versions of the API we should only add new functions at the end of the structure and use the +// version info to detect whether a call is available + +// Use these to populate version in your plugin +#define GODOTVR_API_MAJOR 1 +#define GODOTVR_API_MINOR 1 + +typedef struct { + godot_gdnative_api_version version; /* version of our API */ + void *(*constructor)(godot_object *); + void (*destructor)(void *); + godot_string (*get_name)(const void *); + godot_int (*get_capabilities)(const void *); + godot_bool (*get_anchor_detection_is_enabled)(const void *); + void (*set_anchor_detection_is_enabled)(void *, godot_bool); + godot_bool (*is_stereo)(const void *); + godot_bool (*is_initialized)(const void *); + godot_bool (*initialize)(void *); + void (*uninitialize)(void *); + godot_vector2 (*get_render_targetsize)(const void *); + godot_transform (*get_transform_for_eye)(void *, godot_int, godot_transform *); + void (*fill_projection_for_eye)(void *, godot_real *, godot_int, godot_real, godot_real, godot_real); + void (*commit_for_eye)(void *, godot_int, godot_rid *, godot_rect2 *); + void (*process)(void *); + godot_int (*get_external_texture_for_eye)(void *, godot_int); + void (*notification)(void *, godot_int); + godot_int (*get_camera_feed_id)(void *); +} godot_xr_interface_gdnative; + +void GDAPI godot_xr_register_interface(const godot_xr_interface_gdnative *p_interface); + +// helper functions to access XRServer data +godot_real GDAPI godot_xr_get_worldscale(); +godot_transform GDAPI godot_xr_get_reference_frame(); + +// helper functions for rendering +void GDAPI godot_xr_blit(godot_int p_eye, godot_rid *p_render_target, godot_rect2 *p_rect); +godot_int GDAPI godot_xr_get_texid(godot_rid *p_render_target); + +// helper functions for updating XR controllers +godot_int GDAPI godot_xr_add_controller(char *p_device_name, godot_int p_hand, godot_bool p_tracks_orientation, godot_bool p_tracks_position); +void GDAPI godot_xr_remove_controller(godot_int p_controller_id); +void GDAPI godot_xr_set_controller_transform(godot_int p_controller_id, godot_transform *p_transform, godot_bool p_tracks_orientation, godot_bool p_tracks_position); +void GDAPI godot_xr_set_controller_button(godot_int p_controller_id, godot_int p_button, godot_bool p_is_pressed); +void GDAPI godot_xr_set_controller_axis(godot_int p_controller_id, godot_int p_axis, godot_real p_value, godot_bool p_can_be_negative); +godot_real GDAPI godot_xr_get_controller_rumble(godot_int p_controller_id); + +#ifdef __cplusplus +} +#endif + +#endif /* !GODOT_NATIVEXR_H */ diff --git a/modules/gdnative/register_types.cpp b/modules/gdnative/register_types.cpp index 397a020689..67a286ee2e 100644 --- a/modules/gdnative/register_types.cpp +++ b/modules/gdnative/register_types.cpp @@ -34,11 +34,11 @@ #include "gdnative.h" -#include "arvr/register_types.h" #include "nativescript/register_types.h" #include "net/register_types.h" #include "pluginscript/register_types.h" #include "videodecoder/register_types.h" +#include "xr/register_types.h" #include "core/engine.h" #include "core/io/resource_loader.h" @@ -240,7 +240,7 @@ void register_gdnative_types() { GDNativeCallRegistry::singleton->register_native_call_type("standard_varcall", cb_standard_varcall); register_net_types(); - register_arvr_types(); + register_xr_types(); register_nativescript_types(); register_pluginscript_types(); register_videodecoder_types(); @@ -305,7 +305,7 @@ void unregister_gdnative_types() { unregister_videodecoder_types(); unregister_pluginscript_types(); unregister_nativescript_types(); - unregister_arvr_types(); + unregister_xr_types(); unregister_net_types(); memdelete(GDNativeCallRegistry::singleton); diff --git a/modules/gdnative/xr/SCsub b/modules/gdnative/xr/SCsub new file mode 100644 index 0000000000..0b2db3b504 --- /dev/null +++ b/modules/gdnative/xr/SCsub @@ -0,0 +1,6 @@ +#!/usr/bin/env python + +Import("env") +Import("env_gdnative") + +env_gdnative.add_source_files(env.modules_sources, "*.cpp") diff --git a/modules/gdnative/xr/config.py b/modules/gdnative/xr/config.py new file mode 100644 index 0000000000..d22f9454ed --- /dev/null +++ b/modules/gdnative/xr/config.py @@ -0,0 +1,6 @@ +def can_build(env, platform): + return True + + +def configure(env): + pass diff --git a/modules/gdnative/xr/register_types.cpp b/modules/gdnative/xr/register_types.cpp new file mode 100644 index 0000000000..da3a7dc4b8 --- /dev/null +++ b/modules/gdnative/xr/register_types.cpp @@ -0,0 +1,40 @@ +/*************************************************************************/ +/* register_types.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.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 "register_types.h" +#include "xr_interface_gdnative.h" + +void register_xr_types() { + ClassDB::register_class(); + ClassDB::add_compatibility_class("ARVRInterfaceGDNative", "XRInterfaceGDNative"); +} + +void unregister_xr_types() { +} diff --git a/modules/gdnative/xr/register_types.h b/modules/gdnative/xr/register_types.h new file mode 100644 index 0000000000..2501d28651 --- /dev/null +++ b/modules/gdnative/xr/register_types.h @@ -0,0 +1,37 @@ +/*************************************************************************/ +/* register_types.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.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. */ +/*************************************************************************/ + +#ifndef XR_REGISTER_TYPES_H +#define XR_REGISTER_TYPES_H + +void register_xr_types(); +void unregister_xr_types(); + +#endif // XR_REGISTER_TYPES_H diff --git a/modules/gdnative/xr/xr_interface_gdnative.cpp b/modules/gdnative/xr/xr_interface_gdnative.cpp new file mode 100644 index 0000000000..0451945139 --- /dev/null +++ b/modules/gdnative/xr/xr_interface_gdnative.cpp @@ -0,0 +1,428 @@ +/*************************************************************************/ +/* xr_interface_gdnative.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.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 "xr_interface_gdnative.h" +#include "core/input/input_filter.h" +#include "servers/rendering/rendering_server_globals.h" +#include "servers/xr/xr_positional_tracker.h" + +void XRInterfaceGDNative::_bind_methods() { + ADD_PROPERTY_DEFAULT("interface_is_initialized", false); + ADD_PROPERTY_DEFAULT("ar_is_anchor_detection_enabled", false); +} + +XRInterfaceGDNative::XRInterfaceGDNative() { + print_verbose("Construct gdnative interface\n"); + + // we won't have our data pointer until our library gets set + data = nullptr; + + interface = nullptr; +} + +XRInterfaceGDNative::~XRInterfaceGDNative() { + print_verbose("Destruct gdnative interface\n"); + + if (interface != nullptr && is_initialized()) { + uninitialize(); + }; + + // cleanup after ourselves + cleanup(); +} + +void XRInterfaceGDNative::cleanup() { + if (interface != nullptr) { + interface->destructor(data); + data = nullptr; + interface = nullptr; + } +} + +void XRInterfaceGDNative::set_interface(const godot_xr_interface_gdnative *p_interface) { + // this should only be called once, just being paranoid.. + if (interface) { + cleanup(); + } + + // bind to our interface + interface = p_interface; + + // Now we do our constructing... + data = interface->constructor((godot_object *)this); +} + +StringName XRInterfaceGDNative::get_name() const { + + ERR_FAIL_COND_V(interface == nullptr, StringName()); + + godot_string result = interface->get_name(data); + + StringName name = *(String *)&result; + + godot_string_destroy(&result); + + return name; +} + +int XRInterfaceGDNative::get_capabilities() const { + int capabilities; + + ERR_FAIL_COND_V(interface == nullptr, 0); // 0 = None + + capabilities = interface->get_capabilities(data); + + return capabilities; +} + +bool XRInterfaceGDNative::get_anchor_detection_is_enabled() const { + + ERR_FAIL_COND_V(interface == nullptr, false); + + return interface->get_anchor_detection_is_enabled(data); +} + +void XRInterfaceGDNative::set_anchor_detection_is_enabled(bool p_enable) { + + ERR_FAIL_COND(interface == nullptr); + + interface->set_anchor_detection_is_enabled(data, p_enable); +} + +int XRInterfaceGDNative::get_camera_feed_id() { + + ERR_FAIL_COND_V(interface == nullptr, 0); + + return (unsigned int)interface->get_camera_feed_id(data); +} + +bool XRInterfaceGDNative::is_stereo() { + bool stereo; + + ERR_FAIL_COND_V(interface == nullptr, false); + + stereo = interface->is_stereo(data); + + return stereo; +} + +bool XRInterfaceGDNative::is_initialized() const { + + ERR_FAIL_COND_V(interface == nullptr, false); + + return interface->is_initialized(data); +} + +bool XRInterfaceGDNative::initialize() { + ERR_FAIL_COND_V(interface == nullptr, false); + + bool initialized = interface->initialize(data); + + if (initialized) { + // if we successfully initialize our interface and we don't have a primary interface yet, this becomes our primary interface + + XRServer *xr_server = XRServer::get_singleton(); + if ((xr_server != nullptr) && (xr_server->get_primary_interface() == nullptr)) { + xr_server->set_primary_interface(this); + }; + }; + + return initialized; +} + +void XRInterfaceGDNative::uninitialize() { + ERR_FAIL_COND(interface == nullptr); + + XRServer *xr_server = XRServer::get_singleton(); + if (xr_server != nullptr) { + // Whatever happens, make sure this is no longer our primary interface + xr_server->clear_primary_interface_if(this); + } + + interface->uninitialize(data); +} + +Size2 XRInterfaceGDNative::get_render_targetsize() { + + ERR_FAIL_COND_V(interface == nullptr, Size2()); + + godot_vector2 result = interface->get_render_targetsize(data); + Vector2 *vec = (Vector2 *)&result; + + return *vec; +} + +Transform XRInterfaceGDNative::get_transform_for_eye(XRInterface::Eyes p_eye, const Transform &p_cam_transform) { + Transform *ret; + + ERR_FAIL_COND_V(interface == nullptr, Transform()); + + godot_transform t = interface->get_transform_for_eye(data, (int)p_eye, (godot_transform *)&p_cam_transform); + + ret = (Transform *)&t; + + return *ret; +} + +CameraMatrix XRInterfaceGDNative::get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) { + CameraMatrix cm; + + ERR_FAIL_COND_V(interface == nullptr, CameraMatrix()); + + interface->fill_projection_for_eye(data, (godot_real *)cm.matrix, (godot_int)p_eye, p_aspect, p_z_near, p_z_far); + + return cm; +} + +unsigned int XRInterfaceGDNative::get_external_texture_for_eye(XRInterface::Eyes p_eye) { + + ERR_FAIL_COND_V(interface == nullptr, 0); + + return (unsigned int)interface->get_external_texture_for_eye(data, (godot_int)p_eye); +} + +void XRInterfaceGDNative::commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) { + + ERR_FAIL_COND(interface == nullptr); + + interface->commit_for_eye(data, (godot_int)p_eye, (godot_rid *)&p_render_target, (godot_rect2 *)&p_screen_rect); +} + +void XRInterfaceGDNative::process() { + ERR_FAIL_COND(interface == nullptr); + + interface->process(data); +} + +void XRInterfaceGDNative::notification(int p_what) { + ERR_FAIL_COND(interface == nullptr); + + interface->notification(data, p_what); +} + +///////////////////////////////////////////////////////////////////////////////////// +// some helper callbacks + +extern "C" { + +void GDAPI godot_xr_register_interface(const godot_xr_interface_gdnative *p_interface) { + // Must be on a version 4 plugin + ERR_FAIL_COND_MSG(p_interface->version.major < 4, "GDNative XR interfaces build for Godot 3.x are not supported."); + + Ref new_interface; + new_interface.instance(); + new_interface->set_interface((const godot_xr_interface_gdnative *)p_interface); + XRServer::get_singleton()->add_interface(new_interface); +} + +godot_real GDAPI godot_xr_get_worldscale() { + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL_V(xr_server, 1.0); + + return xr_server->get_world_scale(); +} + +godot_transform GDAPI godot_xr_get_reference_frame() { + godot_transform reference_frame; + Transform *reference_frame_ptr = (Transform *)&reference_frame; + + XRServer *xr_server = XRServer::get_singleton(); + if (xr_server != nullptr) { + *reference_frame_ptr = xr_server->get_reference_frame(); + } else { + godot_transform_new_identity(&reference_frame); + } + + return reference_frame; +} + +void GDAPI godot_xr_blit(godot_int p_eye, godot_rid *p_render_target, godot_rect2 *p_rect) { + // blits out our texture as is, handy for preview display of one of the eyes that is already rendered with lens distortion on an external HMD + XRInterface::Eyes eye = (XRInterface::Eyes)p_eye; +#if 0 + RID *render_target = (RID *)p_render_target; +#endif + Rect2 screen_rect = *(Rect2 *)p_rect; + + if (eye == XRInterface::EYE_LEFT) { + screen_rect.size.x /= 2.0; + } else if (p_eye == XRInterface::EYE_RIGHT) { + screen_rect.size.x /= 2.0; + screen_rect.position.x += screen_rect.size.x; + } +#ifndef _MSC_VER +#warning this needs to be redone +#endif +#if 0 + RSG::rasterizer->blit_render_target_to_screen(*render_target, screen_rect, 0); +#endif +} + +godot_int GDAPI godot_xr_get_texid(godot_rid *p_render_target) { + // In order to send off our textures to display on our hardware we need the opengl texture ID instead of the render target RID + // This is a handy function to expose that. +#if 0 + RID *render_target = (RID *)p_render_target; + + RID eye_texture = RSG::storage->render_target_get_texture(*render_target); +#endif + +#ifndef _MSC_VER +#warning need to obtain this ID again +#endif + uint32_t texid = 0; //RS::get_singleton()->texture_get_texid(eye_texture); + + return texid; +} + +godot_int GDAPI godot_xr_add_controller(char *p_device_name, godot_int p_hand, godot_bool p_tracks_orientation, godot_bool p_tracks_position) { + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL_V(xr_server, 0); + + InputFilter *input = InputFilter::get_singleton(); + ERR_FAIL_NULL_V(input, 0); + + XRPositionalTracker *new_tracker = memnew(XRPositionalTracker); + new_tracker->set_name(p_device_name); + new_tracker->set_type(XRServer::TRACKER_CONTROLLER); + if (p_hand == 1) { + new_tracker->set_hand(XRPositionalTracker::TRACKER_LEFT_HAND); + } else if (p_hand == 2) { + new_tracker->set_hand(XRPositionalTracker::TRACKER_RIGHT_HAND); + } + + // also register as joystick... + int joyid = input->get_unused_joy_id(); + if (joyid != -1) { + new_tracker->set_joy_id(joyid); + input->joy_connection_changed(joyid, true, p_device_name, ""); + } + + if (p_tracks_orientation) { + Basis orientation; + new_tracker->set_orientation(orientation); + } + if (p_tracks_position) { + Vector3 position; + new_tracker->set_position(position); + } + + // add our tracker to our server and remember its pointer + xr_server->add_tracker(new_tracker); + + // note, this ID is only unique within controllers! + return new_tracker->get_tracker_id(); +} + +void GDAPI godot_xr_remove_controller(godot_int p_controller_id) { + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL(xr_server); + + InputFilter *input = InputFilter::get_singleton(); + ERR_FAIL_NULL(input); + + XRPositionalTracker *remove_tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, p_controller_id); + if (remove_tracker != nullptr) { + // unset our joystick if applicable + int joyid = remove_tracker->get_joy_id(); + if (joyid != -1) { + input->joy_connection_changed(joyid, false, "", ""); + remove_tracker->set_joy_id(-1); + } + + // remove our tracker from our server + xr_server->remove_tracker(remove_tracker); + memdelete(remove_tracker); + } +} + +void GDAPI godot_xr_set_controller_transform(godot_int p_controller_id, godot_transform *p_transform, godot_bool p_tracks_orientation, godot_bool p_tracks_position) { + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL(xr_server); + + XRPositionalTracker *tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, p_controller_id); + if (tracker != nullptr) { + Transform *transform = (Transform *)p_transform; + if (p_tracks_orientation) { + tracker->set_orientation(transform->basis); + } + if (p_tracks_position) { + tracker->set_rw_position(transform->origin); + } + } +} + +void GDAPI godot_xr_set_controller_button(godot_int p_controller_id, godot_int p_button, godot_bool p_is_pressed) { + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL(xr_server); + + InputFilter *input = InputFilter::get_singleton(); + ERR_FAIL_NULL(input); + + XRPositionalTracker *tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, p_controller_id); + if (tracker != nullptr) { + int joyid = tracker->get_joy_id(); + if (joyid != -1) { + input->joy_button(joyid, p_button, p_is_pressed); + } + } +} + +void GDAPI godot_xr_set_controller_axis(godot_int p_controller_id, godot_int p_axis, godot_real p_value, godot_bool p_can_be_negative) { + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL(xr_server); + + InputFilter *input = InputFilter::get_singleton(); + ERR_FAIL_NULL(input); + + XRPositionalTracker *tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, p_controller_id); + if (tracker != nullptr) { + int joyid = tracker->get_joy_id(); + if (joyid != -1) { + InputFilter::JoyAxis jx; + jx.min = p_can_be_negative ? -1 : 0; + jx.value = p_value; + input->joy_axis(joyid, p_axis, jx); + } + } +} + +godot_real GDAPI godot_xr_get_controller_rumble(godot_int p_controller_id) { + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL_V(xr_server, 0.0); + + XRPositionalTracker *tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, p_controller_id); + if (tracker != nullptr) { + return tracker->get_rumble(); + } + + return 0.0; +} +} diff --git a/modules/gdnative/xr/xr_interface_gdnative.h b/modules/gdnative/xr/xr_interface_gdnative.h new file mode 100644 index 0000000000..64f1282a7e --- /dev/null +++ b/modules/gdnative/xr/xr_interface_gdnative.h @@ -0,0 +1,91 @@ +/*************************************************************************/ +/* xr_interface_gdnative.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.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. */ +/*************************************************************************/ + +#ifndef XR_INTERFACE_GDNATIVE_H +#define XR_INTERFACE_GDNATIVE_H + +#include "modules/gdnative/gdnative.h" +#include "servers/xr/xr_interface.h" + +/** + @authors Hinsbart & Karroffel & Mux213 + + This subclass of our AR/VR interface forms a bridge to GDNative. +*/ + +class XRInterfaceGDNative : public XRInterface { + GDCLASS(XRInterfaceGDNative, XRInterface); + + void cleanup(); + +protected: + const godot_xr_interface_gdnative *interface; + void *data; + + static void _bind_methods(); + +public: + /** general interface information **/ + XRInterfaceGDNative(); + ~XRInterfaceGDNative(); + + void set_interface(const godot_xr_interface_gdnative *p_interface); + + virtual StringName get_name() const; + virtual int get_capabilities() const; + + virtual bool is_initialized() const; + virtual bool initialize(); + virtual void uninitialize(); + + /** specific to AR **/ + virtual bool get_anchor_detection_is_enabled() const; + virtual void set_anchor_detection_is_enabled(bool p_enable); + virtual int get_camera_feed_id(); + + /** rendering and internal **/ + virtual Size2 get_render_targetsize(); + virtual bool is_stereo(); + virtual Transform get_transform_for_eye(XRInterface::Eyes p_eye, const Transform &p_cam_transform); + + // we expose a Vector version of this function to GDNative + Vector _get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far); + + // and a CameraMatrix version to XRServer + virtual CameraMatrix get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far); + + virtual unsigned int get_external_texture_for_eye(XRInterface::Eyes p_eye); + virtual void commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect); + + virtual void process(); + virtual void notification(int p_what); +}; + +#endif // XR_INTERFACE_GDNATIVE_H diff --git a/modules/mobile_vr/doc_classes/MobileVRInterface.xml b/modules/mobile_vr/doc_classes/MobileVRInterface.xml index 7552abe61d..120535bd41 100644 --- a/modules/mobile_vr/doc_classes/MobileVRInterface.xml +++ b/modules/mobile_vr/doc_classes/MobileVRInterface.xml @@ -1,5 +1,5 @@ - + Generic mobile VR implementation. @@ -8,9 +8,9 @@ Note that even though there is no positional tracking, the camera will assume the headset is at a height of 1.85 meters. You can change this by setting [member eye_height]. You can initialise this interface as follows: [codeblock] - var interface = ARVRServer.find_interface("Native mobile") + var interface = XRServer.find_interface("Native mobile") if interface and interface.initialize(): - get_viewport().arvr = true + get_viewport().xr = true [/codeblock] @@ -25,7 +25,7 @@ The width of the display in centimeters. - The height at which the camera is placed in relation to the ground (i.e. [ARVROrigin] node). + The height at which the camera is placed in relation to the ground (i.e. [XROrigin3D] node). The interocular distance, also known as the interpupillary distance. The distance between the pupils of the left and right eye. diff --git a/modules/mobile_vr/mobile_vr_interface.cpp b/modules/mobile_vr/mobile_vr_interface.cpp index 6b5a70435d..2f0a15f20b 100644 --- a/modules/mobile_vr/mobile_vr_interface.cpp +++ b/modules/mobile_vr/mobile_vr_interface.cpp @@ -39,7 +39,7 @@ StringName MobileVRInterface::get_name() const { }; int MobileVRInterface::get_capabilities() const { - return ARVRInterface::ARVR_STEREO; + return XRInterface::XR_STEREO; }; Vector3 MobileVRInterface::scale_magneto(const Vector3 &p_magnetometer) { @@ -165,7 +165,7 @@ void MobileVRInterface::set_position_from_sensors() { rotate.rotate(orientation.get_axis(2), gyro.z * delta_time); orientation = rotate * orientation; - tracking_state = ARVRInterface::ARVR_NORMAL_TRACKING; + tracking_state = XRInterface::XR_NORMAL_TRACKING; }; ///@TODO improve this, the magnetometer is very fidgity sometimes flipping the axis for no apparent reason (probably a bug on my part) @@ -177,7 +177,7 @@ void MobileVRInterface::set_position_from_sensors() { transform_quat = transform_quat.slerp(acc_mag_quat, 0.1); orientation = Basis(transform_quat); - tracking_state = ARVRInterface::ARVR_NORMAL_TRACKING; + tracking_state = XRInterface::XR_NORMAL_TRACKING; } else if (has_grav) { // use gravity vector to make sure down is down... // transform gravity into our world space @@ -297,8 +297,8 @@ bool MobileVRInterface::is_initialized() const { }; bool MobileVRInterface::initialize() { - ARVRServer *arvr_server = ARVRServer::get_singleton(); - ERR_FAIL_NULL_V(arvr_server, false); + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL_V(xr_server, false); if (!initialized) { // reset our sensor data and orientation @@ -314,7 +314,7 @@ bool MobileVRInterface::initialize() { orientation = Basis(); // make this our primary interface - arvr_server->set_primary_interface(this); + xr_server->set_primary_interface(this); last_ticks = OS::get_singleton()->get_ticks_usec(); @@ -326,10 +326,10 @@ bool MobileVRInterface::initialize() { void MobileVRInterface::uninitialize() { if (initialized) { - ARVRServer *arvr_server = ARVRServer::get_singleton(); - if (arvr_server != nullptr) { + XRServer *xr_server = XRServer::get_singleton(); + if (xr_server != nullptr) { // no longer our primary interface - arvr_server->clear_primary_interface_if(this); + xr_server->clear_primary_interface_if(this); } initialized = false; @@ -348,22 +348,22 @@ Size2 MobileVRInterface::get_render_targetsize() { return target_size; }; -Transform MobileVRInterface::get_transform_for_eye(ARVRInterface::Eyes p_eye, const Transform &p_cam_transform) { +Transform MobileVRInterface::get_transform_for_eye(XRInterface::Eyes p_eye, const Transform &p_cam_transform) { _THREAD_SAFE_METHOD_ Transform transform_for_eye; - ARVRServer *arvr_server = ARVRServer::get_singleton(); - ERR_FAIL_NULL_V(arvr_server, transform_for_eye); + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL_V(xr_server, transform_for_eye); if (initialized) { - float world_scale = arvr_server->get_world_scale(); + float world_scale = xr_server->get_world_scale(); // we don't need to check for the existence of our HMD, doesn't effect our values... // note * 0.01 to convert cm to m and * 0.5 as we're moving half in each direction... - if (p_eye == ARVRInterface::EYE_LEFT) { + if (p_eye == XRInterface::EYE_LEFT) { transform_for_eye.origin.x = -(intraocular_dist * 0.01 * 0.5 * world_scale); - } else if (p_eye == ARVRInterface::EYE_RIGHT) { + } else if (p_eye == XRInterface::EYE_RIGHT) { transform_for_eye.origin.x = intraocular_dist * 0.01 * 0.5 * world_scale; } else { // for mono we don't reposition, we want our center position. @@ -374,7 +374,7 @@ Transform MobileVRInterface::get_transform_for_eye(ARVRInterface::Eyes p_eye, co hmd_transform.basis = orientation; hmd_transform.origin = Vector3(0.0, eye_height * world_scale, 0.0); - transform_for_eye = p_cam_transform * (arvr_server->get_reference_frame()) * hmd_transform * transform_for_eye; + transform_for_eye = p_cam_transform * (xr_server->get_reference_frame()) * hmd_transform * transform_for_eye; } else { // huh? well just return what we got.... transform_for_eye = p_cam_transform; @@ -383,12 +383,12 @@ Transform MobileVRInterface::get_transform_for_eye(ARVRInterface::Eyes p_eye, co return transform_for_eye; }; -CameraMatrix MobileVRInterface::get_projection_for_eye(ARVRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) { +CameraMatrix MobileVRInterface::get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) { _THREAD_SAFE_METHOD_ CameraMatrix eye; - if (p_eye == ARVRInterface::EYE_MONO) { + if (p_eye == XRInterface::EYE_MONO) { ///@TODO for now hardcode some of this, what is really needed here is that this needs to be in sync with the real cameras properties // which probably means implementing a specific class for iOS and Android. For now this is purely here as an example. // Note also that if you use a normal viewport with AR/VR turned off you can still use the tracker output of this interface @@ -396,13 +396,13 @@ CameraMatrix MobileVRInterface::get_projection_for_eye(ARVRInterface::Eyes p_eye // This will make more sense when we implement ARkit on iOS (probably a separate interface). eye.set_perspective(60.0, p_aspect, p_z_near, p_z_far, false); } else { - eye.set_for_hmd(p_eye == ARVRInterface::EYE_LEFT ? 1 : 2, p_aspect, intraocular_dist, display_width, display_to_lens, oversample, p_z_near, p_z_far); + eye.set_for_hmd(p_eye == XRInterface::EYE_LEFT ? 1 : 2, p_aspect, intraocular_dist, display_width, display_to_lens, oversample, p_z_near, p_z_far); }; return eye; }; -void MobileVRInterface::commit_for_eye(ARVRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) { +void MobileVRInterface::commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) { _THREAD_SAFE_METHOD_ // We must have a valid render target @@ -417,9 +417,9 @@ void MobileVRInterface::commit_for_eye(ARVRInterface::Eyes p_eye, RID p_render_t // we output half a screen dest.size.x *= 0.5; - if (p_eye == ARVRInterface::EYE_LEFT) { + if (p_eye == XRInterface::EYE_LEFT) { eye_center.x = ((-intraocular_dist / 2.0) + (display_width / 4.0)) / (display_width / 2.0); - } else if (p_eye == ARVRInterface::EYE_RIGHT) { + } else if (p_eye == XRInterface::EYE_RIGHT) { dest.position.x = dest.size.x; eye_center.x = ((intraocular_dist / 2.0) - (display_width / 4.0)) / (display_width / 2.0); } diff --git a/modules/mobile_vr/mobile_vr_interface.h b/modules/mobile_vr/mobile_vr_interface.h index c762c9b799..3a9ed1314a 100644 --- a/modules/mobile_vr/mobile_vr_interface.h +++ b/modules/mobile_vr/mobile_vr_interface.h @@ -31,8 +31,8 @@ #ifndef MOBILE_VR_INTERFACE_H #define MOBILE_VR_INTERFACE_H -#include "servers/arvr/arvr_interface.h" -#include "servers/arvr/arvr_positional_tracker.h" +#include "servers/xr/xr_interface.h" +#include "servers/xr/xr_positional_tracker.h" /** @author Bastiaan Olij @@ -47,8 +47,8 @@ more advanced interfaces. */ -class MobileVRInterface : public ARVRInterface { - GDCLASS(MobileVRInterface, ARVRInterface); +class MobileVRInterface : public XRInterface { + GDCLASS(MobileVRInterface, XRInterface); private: bool initialized; @@ -137,9 +137,9 @@ public: virtual Size2 get_render_targetsize(); virtual bool is_stereo(); - virtual Transform get_transform_for_eye(ARVRInterface::Eyes p_eye, const Transform &p_cam_transform); - virtual CameraMatrix get_projection_for_eye(ARVRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far); - virtual void commit_for_eye(ARVRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect); + virtual Transform get_transform_for_eye(XRInterface::Eyes p_eye, const Transform &p_cam_transform); + virtual CameraMatrix get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far); + virtual void commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect); virtual void process(); virtual void notification(int p_what); diff --git a/modules/mobile_vr/register_types.cpp b/modules/mobile_vr/register_types.cpp index faf6c3b151..75638d47c4 100644 --- a/modules/mobile_vr/register_types.cpp +++ b/modules/mobile_vr/register_types.cpp @@ -37,7 +37,7 @@ void register_mobile_vr_types() { Ref mobile_vr; mobile_vr.instance(); - ARVRServer::get_singleton()->add_interface(mobile_vr); + XRServer::get_singleton()->add_interface(mobile_vr); } void unregister_mobile_vr_types() { diff --git a/scene/3d/arvr_nodes.cpp b/scene/3d/arvr_nodes.cpp deleted file mode 100644 index 537c094ceb..0000000000 --- a/scene/3d/arvr_nodes.cpp +++ /dev/null @@ -1,621 +0,0 @@ -/*************************************************************************/ -/* arvr_nodes.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.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 "arvr_nodes.h" -#include "core/input/input_filter.h" -#include "servers/arvr/arvr_interface.h" -#include "servers/arvr_server.h" - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -void ARVRCamera::_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_ENTER_TREE: { - // need to find our ARVROrigin parent and let it know we're its camera! - ARVROrigin *origin = Object::cast_to(get_parent()); - if (origin != nullptr) { - origin->set_tracked_camera(this); - } - }; break; - case NOTIFICATION_EXIT_TREE: { - // need to find our ARVROrigin parent and let it know we're no longer its camera! - ARVROrigin *origin = Object::cast_to(get_parent()); - if (origin != nullptr) { - origin->clear_tracked_camera_if(this); - } - }; break; - }; -}; - -String ARVRCamera::get_configuration_warning() const { - if (!is_visible() || !is_inside_tree()) - return String(); - - // must be child node of ARVROrigin! - ARVROrigin *origin = Object::cast_to(get_parent()); - if (origin == nullptr) { - return TTR("ARVRCamera must have an ARVROrigin node as its parent."); - }; - - return String(); -}; - -Vector3 ARVRCamera::project_local_ray_normal(const Point2 &p_pos) const { - // get our ARVRServer - ARVRServer *arvr_server = ARVRServer::get_singleton(); - ERR_FAIL_NULL_V(arvr_server, Vector3()); - - Ref arvr_interface = arvr_server->get_primary_interface(); - if (arvr_interface.is_null()) { - // we might be in the editor or have VR turned off, just call superclass - return Camera3D::project_local_ray_normal(p_pos); - } - - ERR_FAIL_COND_V_MSG(!is_inside_tree(), Vector3(), "Camera is not inside scene."); - - Size2 viewport_size = get_viewport()->get_camera_rect_size(); - Vector2 cpos = get_viewport()->get_camera_coords(p_pos); - Vector3 ray; - - CameraMatrix cm = arvr_interface->get_projection_for_eye(ARVRInterface::EYE_MONO, viewport_size.aspect(), get_znear(), get_zfar()); - Vector2 screen_he = cm.get_viewport_half_extents(); - ray = Vector3(((cpos.x / viewport_size.width) * 2.0 - 1.0) * screen_he.x, ((1.0 - (cpos.y / viewport_size.height)) * 2.0 - 1.0) * screen_he.y, -get_znear()).normalized(); - - return ray; -}; - -Point2 ARVRCamera::unproject_position(const Vector3 &p_pos) const { - // get our ARVRServer - ARVRServer *arvr_server = ARVRServer::get_singleton(); - ERR_FAIL_NULL_V(arvr_server, Vector2()); - - Ref arvr_interface = arvr_server->get_primary_interface(); - if (arvr_interface.is_null()) { - // we might be in the editor or have VR turned off, just call superclass - return Camera3D::unproject_position(p_pos); - } - - ERR_FAIL_COND_V_MSG(!is_inside_tree(), Vector2(), "Camera is not inside scene."); - - Size2 viewport_size = get_viewport()->get_visible_rect().size; - - CameraMatrix cm = arvr_interface->get_projection_for_eye(ARVRInterface::EYE_MONO, viewport_size.aspect(), get_znear(), get_zfar()); - - Plane p(get_camera_transform().xform_inv(p_pos), 1.0); - - p = cm.xform4(p); - p.normal /= p.d; - - Point2 res; - res.x = (p.normal.x * 0.5 + 0.5) * viewport_size.x; - res.y = (-p.normal.y * 0.5 + 0.5) * viewport_size.y; - - return res; -}; - -Vector3 ARVRCamera::project_position(const Point2 &p_point, float p_z_depth) const { - // get our ARVRServer - ARVRServer *arvr_server = ARVRServer::get_singleton(); - ERR_FAIL_NULL_V(arvr_server, Vector3()); - - Ref arvr_interface = arvr_server->get_primary_interface(); - if (arvr_interface.is_null()) { - // we might be in the editor or have VR turned off, just call superclass - return Camera3D::project_position(p_point, p_z_depth); - } - - ERR_FAIL_COND_V_MSG(!is_inside_tree(), Vector3(), "Camera is not inside scene."); - - Size2 viewport_size = get_viewport()->get_visible_rect().size; - - CameraMatrix cm = arvr_interface->get_projection_for_eye(ARVRInterface::EYE_MONO, viewport_size.aspect(), get_znear(), get_zfar()); - - Vector2 vp_he = cm.get_viewport_half_extents(); - - Vector2 point; - point.x = (p_point.x / viewport_size.x) * 2.0 - 1.0; - point.y = (1.0 - (p_point.y / viewport_size.y)) * 2.0 - 1.0; - point *= vp_he; - - Vector3 p(point.x, point.y, -p_z_depth); - - return get_camera_transform().xform(p); -}; - -Vector ARVRCamera::get_frustum() const { - // get our ARVRServer - ARVRServer *arvr_server = ARVRServer::get_singleton(); - ERR_FAIL_NULL_V(arvr_server, Vector()); - - Ref arvr_interface = arvr_server->get_primary_interface(); - if (arvr_interface.is_null()) { - // we might be in the editor or have VR turned off, just call superclass - return Camera3D::get_frustum(); - } - - ERR_FAIL_COND_V(!is_inside_world(), Vector()); - - Size2 viewport_size = get_viewport()->get_visible_rect().size; - CameraMatrix cm = arvr_interface->get_projection_for_eye(ARVRInterface::EYE_MONO, viewport_size.aspect(), get_znear(), get_zfar()); - return cm.get_projection_planes(get_camera_transform()); -}; - -ARVRCamera::ARVRCamera(){ - // nothing to do here yet for now.. -}; - -ARVRCamera::~ARVRCamera(){ - // nothing to do here yet for now.. -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -void ARVRController::_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_ENTER_TREE: { - set_process_internal(true); - }; break; - case NOTIFICATION_EXIT_TREE: { - set_process_internal(false); - }; break; - case NOTIFICATION_INTERNAL_PROCESS: { - // get our ARVRServer - ARVRServer *arvr_server = ARVRServer::get_singleton(); - ERR_FAIL_NULL(arvr_server); - - // find the tracker for our controller - ARVRPositionalTracker *tracker = arvr_server->find_by_type_and_id(ARVRServer::TRACKER_CONTROLLER, controller_id); - if (tracker == nullptr) { - // this controller is currently turned off - is_active = false; - button_states = 0; - } else { - is_active = true; - set_transform(tracker->get_transform(true)); - - int joy_id = tracker->get_joy_id(); - if (joy_id >= 0) { - int mask = 1; - // check button states - for (int i = 0; i < 16; i++) { - bool was_pressed = (button_states & mask) == mask; - bool is_pressed = InputFilter::get_singleton()->is_joy_button_pressed(joy_id, i); - - if (!was_pressed && is_pressed) { - emit_signal("button_pressed", i); - button_states += mask; - } else if (was_pressed && !is_pressed) { - emit_signal("button_release", i); - button_states -= mask; - }; - - mask = mask << 1; - }; - - } else { - button_states = 0; - }; - - // check for an updated mesh - Ref trackerMesh = tracker->get_mesh(); - if (mesh != trackerMesh) { - mesh = trackerMesh; - emit_signal("mesh_updated", mesh); - } - }; - }; break; - default: - break; - }; -}; - -void ARVRController::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_controller_id", "controller_id"), &ARVRController::set_controller_id); - ClassDB::bind_method(D_METHOD("get_controller_id"), &ARVRController::get_controller_id); - ADD_PROPERTY(PropertyInfo(Variant::INT, "controller_id", PROPERTY_HINT_RANGE, "0,32,1"), "set_controller_id", "get_controller_id"); - ClassDB::bind_method(D_METHOD("get_controller_name"), &ARVRController::get_controller_name); - - // passthroughs to information about our related joystick - ClassDB::bind_method(D_METHOD("get_joystick_id"), &ARVRController::get_joystick_id); - ClassDB::bind_method(D_METHOD("is_button_pressed", "button"), &ARVRController::is_button_pressed); - ClassDB::bind_method(D_METHOD("get_joystick_axis", "axis"), &ARVRController::get_joystick_axis); - - ClassDB::bind_method(D_METHOD("get_is_active"), &ARVRController::get_is_active); - ClassDB::bind_method(D_METHOD("get_hand"), &ARVRController::get_hand); - - ClassDB::bind_method(D_METHOD("get_rumble"), &ARVRController::get_rumble); - ClassDB::bind_method(D_METHOD("set_rumble", "rumble"), &ARVRController::set_rumble); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rumble", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_rumble", "get_rumble"); - ADD_PROPERTY_DEFAULT("rumble", 0.0); - - ClassDB::bind_method(D_METHOD("get_mesh"), &ARVRController::get_mesh); - - ADD_SIGNAL(MethodInfo("button_pressed", PropertyInfo(Variant::INT, "button"))); - ADD_SIGNAL(MethodInfo("button_release", PropertyInfo(Variant::INT, "button"))); - ADD_SIGNAL(MethodInfo("mesh_updated", PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"))); -}; - -void ARVRController::set_controller_id(int p_controller_id) { - // We don't check any bounds here, this controller may not yet be active and just be a place holder until it is. - // Note that setting this to 0 means this node is not bound to a controller yet. - controller_id = p_controller_id; - update_configuration_warning(); -}; - -int ARVRController::get_controller_id(void) const { - return controller_id; -}; - -String ARVRController::get_controller_name(void) const { - // get our ARVRServer - ARVRServer *arvr_server = ARVRServer::get_singleton(); - ERR_FAIL_NULL_V(arvr_server, String()); - - ARVRPositionalTracker *tracker = arvr_server->find_by_type_and_id(ARVRServer::TRACKER_CONTROLLER, controller_id); - if (tracker == nullptr) { - return String("Not connected"); - }; - - return tracker->get_name(); -}; - -int ARVRController::get_joystick_id() const { - // get our ARVRServer - ARVRServer *arvr_server = ARVRServer::get_singleton(); - ERR_FAIL_NULL_V(arvr_server, 0); - - ARVRPositionalTracker *tracker = arvr_server->find_by_type_and_id(ARVRServer::TRACKER_CONTROLLER, controller_id); - if (tracker == nullptr) { - // No tracker? no joystick id... (0 is our first joystick) - return -1; - }; - - return tracker->get_joy_id(); -}; - -bool ARVRController::is_button_pressed(int p_button) const { - int joy_id = get_joystick_id(); - if (joy_id == -1) { - return false; - }; - - return InputFilter::get_singleton()->is_joy_button_pressed(joy_id, p_button); -}; - -float ARVRController::get_joystick_axis(int p_axis) const { - int joy_id = get_joystick_id(); - if (joy_id == -1) { - return 0.0; - }; - - return InputFilter::get_singleton()->get_joy_axis(joy_id, p_axis); -}; - -real_t ARVRController::get_rumble() const { - // get our ARVRServer - ARVRServer *arvr_server = ARVRServer::get_singleton(); - ERR_FAIL_NULL_V(arvr_server, 0.0); - - ARVRPositionalTracker *tracker = arvr_server->find_by_type_and_id(ARVRServer::TRACKER_CONTROLLER, controller_id); - if (tracker == nullptr) { - return 0.0; - }; - - return tracker->get_rumble(); -}; - -void ARVRController::set_rumble(real_t p_rumble) { - // get our ARVRServer - ARVRServer *arvr_server = ARVRServer::get_singleton(); - ERR_FAIL_NULL(arvr_server); - - ARVRPositionalTracker *tracker = arvr_server->find_by_type_and_id(ARVRServer::TRACKER_CONTROLLER, controller_id); - if (tracker != nullptr) { - tracker->set_rumble(p_rumble); - }; -}; - -Ref ARVRController::get_mesh() const { - return mesh; -} - -bool ARVRController::get_is_active() const { - return is_active; -}; - -ARVRPositionalTracker::TrackerHand ARVRController::get_hand() const { - // get our ARVRServer - ARVRServer *arvr_server = ARVRServer::get_singleton(); - ERR_FAIL_NULL_V(arvr_server, ARVRPositionalTracker::TRACKER_HAND_UNKNOWN); - - ARVRPositionalTracker *tracker = arvr_server->find_by_type_and_id(ARVRServer::TRACKER_CONTROLLER, controller_id); - if (tracker == nullptr) { - return ARVRPositionalTracker::TRACKER_HAND_UNKNOWN; - }; - - return tracker->get_hand(); -}; - -String ARVRController::get_configuration_warning() const { - if (!is_visible() || !is_inside_tree()) - return String(); - - // must be child node of ARVROrigin! - ARVROrigin *origin = Object::cast_to(get_parent()); - if (origin == nullptr) { - return TTR("ARVRController must have an ARVROrigin node as its parent."); - }; - - if (controller_id == 0) { - return TTR("The controller ID must not be 0 or this controller won't be bound to an actual controller."); - }; - - return String(); -}; - -ARVRController::ARVRController() { - controller_id = 1; - is_active = true; - button_states = 0; -}; - -ARVRController::~ARVRController(){ - // nothing to do here yet for now.. -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -void ARVRAnchor::_notification(int p_what) { - switch (p_what) { - case NOTIFICATION_ENTER_TREE: { - set_process_internal(true); - }; break; - case NOTIFICATION_EXIT_TREE: { - set_process_internal(false); - }; break; - case NOTIFICATION_INTERNAL_PROCESS: { - // get our ARVRServer - ARVRServer *arvr_server = ARVRServer::get_singleton(); - ERR_FAIL_NULL(arvr_server); - - // find the tracker for our anchor - ARVRPositionalTracker *tracker = arvr_server->find_by_type_and_id(ARVRServer::TRACKER_ANCHOR, anchor_id); - if (tracker == nullptr) { - // this anchor is currently not available - is_active = false; - } else { - is_active = true; - Transform transform; - - // we'll need our world_scale - real_t world_scale = arvr_server->get_world_scale(); - - // get our info from our tracker - transform.basis = tracker->get_orientation(); - transform.origin = tracker->get_position(); // <-- already adjusted to world scale - - // our basis is scaled to the size of the plane the anchor is tracking - // extract the size from our basis and reset the scale - size = transform.basis.get_scale() * world_scale; - transform.basis.orthonormalize(); - - // apply our reference frame and set our transform - set_transform(arvr_server->get_reference_frame() * transform); - - // check for an updated mesh - Ref trackerMesh = tracker->get_mesh(); - if (mesh != trackerMesh) { - mesh = trackerMesh; - emit_signal("mesh_updated", mesh); - } - }; - }; break; - default: - break; - }; -}; - -void ARVRAnchor::_bind_methods() { - - ClassDB::bind_method(D_METHOD("set_anchor_id", "anchor_id"), &ARVRAnchor::set_anchor_id); - ClassDB::bind_method(D_METHOD("get_anchor_id"), &ARVRAnchor::get_anchor_id); - ADD_PROPERTY(PropertyInfo(Variant::INT, "anchor_id", PROPERTY_HINT_RANGE, "0,32,1"), "set_anchor_id", "get_anchor_id"); - ClassDB::bind_method(D_METHOD("get_anchor_name"), &ARVRAnchor::get_anchor_name); - - ClassDB::bind_method(D_METHOD("get_is_active"), &ARVRAnchor::get_is_active); - ClassDB::bind_method(D_METHOD("get_size"), &ARVRAnchor::get_size); - - ClassDB::bind_method(D_METHOD("get_plane"), &ARVRAnchor::get_plane); - - ClassDB::bind_method(D_METHOD("get_mesh"), &ARVRAnchor::get_mesh); - ADD_SIGNAL(MethodInfo("mesh_updated", PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"))); -}; - -void ARVRAnchor::set_anchor_id(int p_anchor_id) { - // We don't check any bounds here, this anchor may not yet be active and just be a place holder until it is. - // Note that setting this to 0 means this node is not bound to an anchor yet. - anchor_id = p_anchor_id; - update_configuration_warning(); -}; - -int ARVRAnchor::get_anchor_id(void) const { - return anchor_id; -}; - -Vector3 ARVRAnchor::get_size() const { - return size; -}; - -String ARVRAnchor::get_anchor_name(void) const { - // get our ARVRServer - ARVRServer *arvr_server = ARVRServer::get_singleton(); - ERR_FAIL_NULL_V(arvr_server, String()); - - ARVRPositionalTracker *tracker = arvr_server->find_by_type_and_id(ARVRServer::TRACKER_ANCHOR, anchor_id); - if (tracker == nullptr) { - return String("Not connected"); - }; - - return tracker->get_name(); -}; - -bool ARVRAnchor::get_is_active() const { - return is_active; -}; - -String ARVRAnchor::get_configuration_warning() const { - if (!is_visible() || !is_inside_tree()) - return String(); - - // must be child node of ARVROrigin! - ARVROrigin *origin = Object::cast_to(get_parent()); - if (origin == nullptr) { - return TTR("ARVRAnchor must have an ARVROrigin node as its parent."); - }; - - if (anchor_id == 0) { - return TTR("The anchor ID must not be 0 or this anchor won't be bound to an actual anchor."); - }; - - return String(); -}; - -Plane ARVRAnchor::get_plane() const { - Vector3 location = get_translation(); - Basis orientation = get_transform().basis; - - Plane plane(location, orientation.get_axis(1).normalized()); - - return plane; -}; - -Ref ARVRAnchor::get_mesh() const { - return mesh; -} - -ARVRAnchor::ARVRAnchor() { - anchor_id = 1; - is_active = true; -}; - -ARVRAnchor::~ARVRAnchor(){ - // nothing to do here yet for now.. -}; - -//////////////////////////////////////////////////////////////////////////////////////////////////// - -String ARVROrigin::get_configuration_warning() const { - if (!is_visible() || !is_inside_tree()) - return String(); - - if (tracked_camera == nullptr) - return TTR("ARVROrigin requires an ARVRCamera child node."); - - return String(); -}; - -void ARVROrigin::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_world_scale", "world_scale"), &ARVROrigin::set_world_scale); - ClassDB::bind_method(D_METHOD("get_world_scale"), &ARVROrigin::get_world_scale); - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "world_scale"), "set_world_scale", "get_world_scale"); -}; - -void ARVROrigin::set_tracked_camera(ARVRCamera *p_tracked_camera) { - tracked_camera = p_tracked_camera; -}; - -void ARVROrigin::clear_tracked_camera_if(ARVRCamera *p_tracked_camera) { - if (tracked_camera == p_tracked_camera) { - tracked_camera = nullptr; - }; -}; - -float ARVROrigin::get_world_scale() const { - // get our ARVRServer - ARVRServer *arvr_server = ARVRServer::get_singleton(); - ERR_FAIL_NULL_V(arvr_server, 1.0); - - return arvr_server->get_world_scale(); -}; - -void ARVROrigin::set_world_scale(float p_world_scale) { - // get our ARVRServer - ARVRServer *arvr_server = ARVRServer::get_singleton(); - ERR_FAIL_NULL(arvr_server); - - arvr_server->set_world_scale(p_world_scale); -}; - -void ARVROrigin::_notification(int p_what) { - // get our ARVRServer - ARVRServer *arvr_server = ARVRServer::get_singleton(); - ERR_FAIL_NULL(arvr_server); - - switch (p_what) { - case NOTIFICATION_ENTER_TREE: { - set_process_internal(true); - }; break; - case NOTIFICATION_EXIT_TREE: { - set_process_internal(false); - }; break; - case NOTIFICATION_INTERNAL_PROCESS: { - // set our world origin to our node transform - arvr_server->set_world_origin(get_global_transform()); - - // check if we have a primary interface - Ref arvr_interface = arvr_server->get_primary_interface(); - if (arvr_interface.is_valid() && tracked_camera != nullptr) { - // get our positioning transform for our headset - Transform t = arvr_interface->get_transform_for_eye(ARVRInterface::EYE_MONO, Transform()); - - // now apply this to our camera - tracked_camera->set_transform(t); - }; - }; break; - default: - break; - }; - - // send our notification to all active ARVR interfaces, they may need to react to it also - for (int i = 0; i < arvr_server->get_interface_count(); i++) { - Ref interface = arvr_server->get_interface(i); - if (interface.is_valid() && interface->is_initialized()) { - interface->notification(p_what); - } - } -}; - -ARVROrigin::ARVROrigin() { - tracked_camera = nullptr; -}; - -ARVROrigin::~ARVROrigin(){ - // nothing to do here yet for now.. -}; diff --git a/scene/3d/arvr_nodes.h b/scene/3d/arvr_nodes.h deleted file mode 100644 index bc5df2e174..0000000000 --- a/scene/3d/arvr_nodes.h +++ /dev/null @@ -1,176 +0,0 @@ -/*************************************************************************/ -/* arvr_nodes.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.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. */ -/*************************************************************************/ - -#ifndef ARVR_NODES_H -#define ARVR_NODES_H - -#include "scene/3d/camera_3d.h" -#include "scene/3d/node_3d.h" -#include "scene/resources/mesh.h" -#include "servers/arvr/arvr_positional_tracker.h" - -/** - @author Bastiaan Olij -**/ - -/* - ARVRCamera is a subclass of camera which will register itself with its parent ARVROrigin and as a result is automatically positioned -*/ -class ARVRCamera : public Camera3D { - - GDCLASS(ARVRCamera, Camera3D); - -protected: - void _notification(int p_what); - -public: - String get_configuration_warning() const; - - virtual Vector3 project_local_ray_normal(const Point2 &p_pos) const; - virtual Point2 unproject_position(const Vector3 &p_pos) const; - virtual Vector3 project_position(const Point2 &p_point, float p_z_depth) const; - virtual Vector get_frustum() const; - - ARVRCamera(); - ~ARVRCamera(); -}; - -/* - ARVRController is a helper node that automatically updates its position based on tracker data. - - It must be a child node of our ARVROrigin node -*/ - -class ARVRController : public Node3D { - - GDCLASS(ARVRController, Node3D); - -private: - int controller_id; - bool is_active; - int button_states; - Ref mesh; - -protected: - void _notification(int p_what); - static void _bind_methods(); - -public: - void set_controller_id(int p_controller_id); - int get_controller_id(void) const; - String get_controller_name(void) const; - - int get_joystick_id() const; - bool is_button_pressed(int p_button) const; - float get_joystick_axis(int p_axis) const; - - real_t get_rumble() const; - void set_rumble(real_t p_rumble); - - bool get_is_active() const; - ARVRPositionalTracker::TrackerHand get_hand() const; - - Ref get_mesh(void) const; - - String get_configuration_warning() const; - - ARVRController(); - ~ARVRController(); -}; - -/* - ARVRAnchor is a helper node that automatically updates its position based on anchor data, it represents a real world location. - It must be a child node of our ARVROrigin node -*/ - -class ARVRAnchor : public Node3D { - GDCLASS(ARVRAnchor, Node3D); - -private: - int anchor_id; - bool is_active; - Vector3 size; - Ref mesh; - -protected: - void _notification(int p_what); - static void _bind_methods(); - -public: - void set_anchor_id(int p_anchor_id); - int get_anchor_id(void) const; - String get_anchor_name(void) const; - - bool get_is_active() const; - Vector3 get_size() const; - - Plane get_plane() const; - - Ref get_mesh(void) const; - - String get_configuration_warning() const; - - ARVRAnchor(); - ~ARVRAnchor(); -}; - -/* - ARVROrigin is special spatial node that acts as our origin point mapping our real world center of our tracking volume into our virtual world. - - It is this point that you will move around the world as the player 'moves while standing still', i.e. the player moves through teleporting or controller inputs as opposed to physically moving. - - Our camera and controllers will always be child nodes and thus place relative to this origin point. - This node will automatically locate any camera child nodes and update its position while our ARVRController node will handle tracked controllers. -*/ -class ARVROrigin : public Node3D { - - GDCLASS(ARVROrigin, Node3D); - -private: - ARVRCamera *tracked_camera; - -protected: - void _notification(int p_what); - static void _bind_methods(); - -public: - String get_configuration_warning() const; - - void set_tracked_camera(ARVRCamera *p_tracked_camera); - void clear_tracked_camera_if(ARVRCamera *p_tracked_camera); - - float get_world_scale() const; - void set_world_scale(float p_world_scale); - - ARVROrigin(); - ~ARVROrigin(); -}; - -#endif /* ARVR_NODES_H */ diff --git a/scene/3d/xr_nodes.cpp b/scene/3d/xr_nodes.cpp new file mode 100644 index 0000000000..0373114e7d --- /dev/null +++ b/scene/3d/xr_nodes.cpp @@ -0,0 +1,621 @@ +/*************************************************************************/ +/* xr_nodes.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.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 "xr_nodes.h" +#include "core/input/input_filter.h" +#include "servers/xr/xr_interface.h" +#include "servers/xr_server.h" + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +void XRCamera3D::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + // need to find our XROrigin3D parent and let it know we're its camera! + XROrigin3D *origin = Object::cast_to(get_parent()); + if (origin != nullptr) { + origin->set_tracked_camera(this); + } + }; break; + case NOTIFICATION_EXIT_TREE: { + // need to find our XROrigin3D parent and let it know we're no longer its camera! + XROrigin3D *origin = Object::cast_to(get_parent()); + if (origin != nullptr) { + origin->clear_tracked_camera_if(this); + } + }; break; + }; +}; + +String XRCamera3D::get_configuration_warning() const { + if (!is_visible() || !is_inside_tree()) + return String(); + + // must be child node of XROrigin3D! + XROrigin3D *origin = Object::cast_to(get_parent()); + if (origin == nullptr) { + return TTR("XRCamera3D must have an XROrigin3D node as its parent."); + }; + + return String(); +}; + +Vector3 XRCamera3D::project_local_ray_normal(const Point2 &p_pos) const { + // get our XRServer + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL_V(xr_server, Vector3()); + + Ref xr_interface = xr_server->get_primary_interface(); + if (xr_interface.is_null()) { + // we might be in the editor or have VR turned off, just call superclass + return Camera3D::project_local_ray_normal(p_pos); + } + + ERR_FAIL_COND_V_MSG(!is_inside_tree(), Vector3(), "Camera is not inside scene."); + + Size2 viewport_size = get_viewport()->get_camera_rect_size(); + Vector2 cpos = get_viewport()->get_camera_coords(p_pos); + Vector3 ray; + + CameraMatrix cm = xr_interface->get_projection_for_eye(XRInterface::EYE_MONO, viewport_size.aspect(), get_znear(), get_zfar()); + Vector2 screen_he = cm.get_viewport_half_extents(); + ray = Vector3(((cpos.x / viewport_size.width) * 2.0 - 1.0) * screen_he.x, ((1.0 - (cpos.y / viewport_size.height)) * 2.0 - 1.0) * screen_he.y, -get_znear()).normalized(); + + return ray; +}; + +Point2 XRCamera3D::unproject_position(const Vector3 &p_pos) const { + // get our XRServer + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL_V(xr_server, Vector2()); + + Ref xr_interface = xr_server->get_primary_interface(); + if (xr_interface.is_null()) { + // we might be in the editor or have VR turned off, just call superclass + return Camera3D::unproject_position(p_pos); + } + + ERR_FAIL_COND_V_MSG(!is_inside_tree(), Vector2(), "Camera is not inside scene."); + + Size2 viewport_size = get_viewport()->get_visible_rect().size; + + CameraMatrix cm = xr_interface->get_projection_for_eye(XRInterface::EYE_MONO, viewport_size.aspect(), get_znear(), get_zfar()); + + Plane p(get_camera_transform().xform_inv(p_pos), 1.0); + + p = cm.xform4(p); + p.normal /= p.d; + + Point2 res; + res.x = (p.normal.x * 0.5 + 0.5) * viewport_size.x; + res.y = (-p.normal.y * 0.5 + 0.5) * viewport_size.y; + + return res; +}; + +Vector3 XRCamera3D::project_position(const Point2 &p_point, float p_z_depth) const { + // get our XRServer + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL_V(xr_server, Vector3()); + + Ref xr_interface = xr_server->get_primary_interface(); + if (xr_interface.is_null()) { + // we might be in the editor or have VR turned off, just call superclass + return Camera3D::project_position(p_point, p_z_depth); + } + + ERR_FAIL_COND_V_MSG(!is_inside_tree(), Vector3(), "Camera is not inside scene."); + + Size2 viewport_size = get_viewport()->get_visible_rect().size; + + CameraMatrix cm = xr_interface->get_projection_for_eye(XRInterface::EYE_MONO, viewport_size.aspect(), get_znear(), get_zfar()); + + Vector2 vp_he = cm.get_viewport_half_extents(); + + Vector2 point; + point.x = (p_point.x / viewport_size.x) * 2.0 - 1.0; + point.y = (1.0 - (p_point.y / viewport_size.y)) * 2.0 - 1.0; + point *= vp_he; + + Vector3 p(point.x, point.y, -p_z_depth); + + return get_camera_transform().xform(p); +}; + +Vector XRCamera3D::get_frustum() const { + // get our XRServer + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL_V(xr_server, Vector()); + + Ref xr_interface = xr_server->get_primary_interface(); + if (xr_interface.is_null()) { + // we might be in the editor or have VR turned off, just call superclass + return Camera3D::get_frustum(); + } + + ERR_FAIL_COND_V(!is_inside_world(), Vector()); + + Size2 viewport_size = get_viewport()->get_visible_rect().size; + CameraMatrix cm = xr_interface->get_projection_for_eye(XRInterface::EYE_MONO, viewport_size.aspect(), get_znear(), get_zfar()); + return cm.get_projection_planes(get_camera_transform()); +}; + +XRCamera3D::XRCamera3D(){ + // nothing to do here yet for now.. +}; + +XRCamera3D::~XRCamera3D(){ + // nothing to do here yet for now.. +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +void XRController3D::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + set_process_internal(true); + }; break; + case NOTIFICATION_EXIT_TREE: { + set_process_internal(false); + }; break; + case NOTIFICATION_INTERNAL_PROCESS: { + // get our XRServer + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL(xr_server); + + // find the tracker for our controller + XRPositionalTracker *tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, controller_id); + if (tracker == nullptr) { + // this controller is currently turned off + is_active = false; + button_states = 0; + } else { + is_active = true; + set_transform(tracker->get_transform(true)); + + int joy_id = tracker->get_joy_id(); + if (joy_id >= 0) { + int mask = 1; + // check button states + for (int i = 0; i < 16; i++) { + bool was_pressed = (button_states & mask) == mask; + bool is_pressed = InputFilter::get_singleton()->is_joy_button_pressed(joy_id, i); + + if (!was_pressed && is_pressed) { + emit_signal("button_pressed", i); + button_states += mask; + } else if (was_pressed && !is_pressed) { + emit_signal("button_release", i); + button_states -= mask; + }; + + mask = mask << 1; + }; + + } else { + button_states = 0; + }; + + // check for an updated mesh + Ref trackerMesh = tracker->get_mesh(); + if (mesh != trackerMesh) { + mesh = trackerMesh; + emit_signal("mesh_updated", mesh); + } + }; + }; break; + default: + break; + }; +}; + +void XRController3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_controller_id", "controller_id"), &XRController3D::set_controller_id); + ClassDB::bind_method(D_METHOD("get_controller_id"), &XRController3D::get_controller_id); + ADD_PROPERTY(PropertyInfo(Variant::INT, "controller_id", PROPERTY_HINT_RANGE, "0,32,1"), "set_controller_id", "get_controller_id"); + ClassDB::bind_method(D_METHOD("get_controller_name"), &XRController3D::get_controller_name); + + // passthroughs to information about our related joystick + ClassDB::bind_method(D_METHOD("get_joystick_id"), &XRController3D::get_joystick_id); + ClassDB::bind_method(D_METHOD("is_button_pressed", "button"), &XRController3D::is_button_pressed); + ClassDB::bind_method(D_METHOD("get_joystick_axis", "axis"), &XRController3D::get_joystick_axis); + + ClassDB::bind_method(D_METHOD("get_is_active"), &XRController3D::get_is_active); + ClassDB::bind_method(D_METHOD("get_hand"), &XRController3D::get_hand); + + ClassDB::bind_method(D_METHOD("get_rumble"), &XRController3D::get_rumble); + ClassDB::bind_method(D_METHOD("set_rumble", "rumble"), &XRController3D::set_rumble); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rumble", PROPERTY_HINT_RANGE, "0.0,1.0,0.01"), "set_rumble", "get_rumble"); + ADD_PROPERTY_DEFAULT("rumble", 0.0); + + ClassDB::bind_method(D_METHOD("get_mesh"), &XRController3D::get_mesh); + + ADD_SIGNAL(MethodInfo("button_pressed", PropertyInfo(Variant::INT, "button"))); + ADD_SIGNAL(MethodInfo("button_release", PropertyInfo(Variant::INT, "button"))); + ADD_SIGNAL(MethodInfo("mesh_updated", PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"))); +}; + +void XRController3D::set_controller_id(int p_controller_id) { + // We don't check any bounds here, this controller may not yet be active and just be a place holder until it is. + // Note that setting this to 0 means this node is not bound to a controller yet. + controller_id = p_controller_id; + update_configuration_warning(); +}; + +int XRController3D::get_controller_id(void) const { + return controller_id; +}; + +String XRController3D::get_controller_name(void) const { + // get our XRServer + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL_V(xr_server, String()); + + XRPositionalTracker *tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, controller_id); + if (tracker == nullptr) { + return String("Not connected"); + }; + + return tracker->get_name(); +}; + +int XRController3D::get_joystick_id() const { + // get our XRServer + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL_V(xr_server, 0); + + XRPositionalTracker *tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, controller_id); + if (tracker == nullptr) { + // No tracker? no joystick id... (0 is our first joystick) + return -1; + }; + + return tracker->get_joy_id(); +}; + +bool XRController3D::is_button_pressed(int p_button) const { + int joy_id = get_joystick_id(); + if (joy_id == -1) { + return false; + }; + + return InputFilter::get_singleton()->is_joy_button_pressed(joy_id, p_button); +}; + +float XRController3D::get_joystick_axis(int p_axis) const { + int joy_id = get_joystick_id(); + if (joy_id == -1) { + return 0.0; + }; + + return InputFilter::get_singleton()->get_joy_axis(joy_id, p_axis); +}; + +real_t XRController3D::get_rumble() const { + // get our XRServer + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL_V(xr_server, 0.0); + + XRPositionalTracker *tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, controller_id); + if (tracker == nullptr) { + return 0.0; + }; + + return tracker->get_rumble(); +}; + +void XRController3D::set_rumble(real_t p_rumble) { + // get our XRServer + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL(xr_server); + + XRPositionalTracker *tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, controller_id); + if (tracker != nullptr) { + tracker->set_rumble(p_rumble); + }; +}; + +Ref XRController3D::get_mesh() const { + return mesh; +} + +bool XRController3D::get_is_active() const { + return is_active; +}; + +XRPositionalTracker::TrackerHand XRController3D::get_hand() const { + // get our XRServer + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL_V(xr_server, XRPositionalTracker::TRACKER_HAND_UNKNOWN); + + XRPositionalTracker *tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_CONTROLLER, controller_id); + if (tracker == nullptr) { + return XRPositionalTracker::TRACKER_HAND_UNKNOWN; + }; + + return tracker->get_hand(); +}; + +String XRController3D::get_configuration_warning() const { + if (!is_visible() || !is_inside_tree()) + return String(); + + // must be child node of XROrigin! + XROrigin3D *origin = Object::cast_to(get_parent()); + if (origin == nullptr) { + return TTR("XRController3D must have an XROrigin3D node as its parent."); + }; + + if (controller_id == 0) { + return TTR("The controller ID must not be 0 or this controller won't be bound to an actual controller."); + }; + + return String(); +}; + +XRController3D::XRController3D() { + controller_id = 1; + is_active = true; + button_states = 0; +}; + +XRController3D::~XRController3D(){ + // nothing to do here yet for now.. +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +void XRAnchor3D::_notification(int p_what) { + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + set_process_internal(true); + }; break; + case NOTIFICATION_EXIT_TREE: { + set_process_internal(false); + }; break; + case NOTIFICATION_INTERNAL_PROCESS: { + // get our XRServer + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL(xr_server); + + // find the tracker for our anchor + XRPositionalTracker *tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_ANCHOR, anchor_id); + if (tracker == nullptr) { + // this anchor is currently not available + is_active = false; + } else { + is_active = true; + Transform transform; + + // we'll need our world_scale + real_t world_scale = xr_server->get_world_scale(); + + // get our info from our tracker + transform.basis = tracker->get_orientation(); + transform.origin = tracker->get_position(); // <-- already adjusted to world scale + + // our basis is scaled to the size of the plane the anchor is tracking + // extract the size from our basis and reset the scale + size = transform.basis.get_scale() * world_scale; + transform.basis.orthonormalize(); + + // apply our reference frame and set our transform + set_transform(xr_server->get_reference_frame() * transform); + + // check for an updated mesh + Ref trackerMesh = tracker->get_mesh(); + if (mesh != trackerMesh) { + mesh = trackerMesh; + emit_signal("mesh_updated", mesh); + } + }; + }; break; + default: + break; + }; +}; + +void XRAnchor3D::_bind_methods() { + + ClassDB::bind_method(D_METHOD("set_anchor_id", "anchor_id"), &XRAnchor3D::set_anchor_id); + ClassDB::bind_method(D_METHOD("get_anchor_id"), &XRAnchor3D::get_anchor_id); + ADD_PROPERTY(PropertyInfo(Variant::INT, "anchor_id", PROPERTY_HINT_RANGE, "0,32,1"), "set_anchor_id", "get_anchor_id"); + ClassDB::bind_method(D_METHOD("get_anchor_name"), &XRAnchor3D::get_anchor_name); + + ClassDB::bind_method(D_METHOD("get_is_active"), &XRAnchor3D::get_is_active); + ClassDB::bind_method(D_METHOD("get_size"), &XRAnchor3D::get_size); + + ClassDB::bind_method(D_METHOD("get_plane"), &XRAnchor3D::get_plane); + + ClassDB::bind_method(D_METHOD("get_mesh"), &XRAnchor3D::get_mesh); + ADD_SIGNAL(MethodInfo("mesh_updated", PropertyInfo(Variant::OBJECT, "mesh", PROPERTY_HINT_RESOURCE_TYPE, "Mesh"))); +}; + +void XRAnchor3D::set_anchor_id(int p_anchor_id) { + // We don't check any bounds here, this anchor may not yet be active and just be a place holder until it is. + // Note that setting this to 0 means this node is not bound to an anchor yet. + anchor_id = p_anchor_id; + update_configuration_warning(); +}; + +int XRAnchor3D::get_anchor_id(void) const { + return anchor_id; +}; + +Vector3 XRAnchor3D::get_size() const { + return size; +}; + +String XRAnchor3D::get_anchor_name(void) const { + // get our XRServer + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL_V(xr_server, String()); + + XRPositionalTracker *tracker = xr_server->find_by_type_and_id(XRServer::TRACKER_ANCHOR, anchor_id); + if (tracker == nullptr) { + return String("Not connected"); + }; + + return tracker->get_name(); +}; + +bool XRAnchor3D::get_is_active() const { + return is_active; +}; + +String XRAnchor3D::get_configuration_warning() const { + if (!is_visible() || !is_inside_tree()) + return String(); + + // must be child node of XROrigin3D! + XROrigin3D *origin = Object::cast_to(get_parent()); + if (origin == nullptr) { + return TTR("XRAnchor3D must have an XROrigin3D node as its parent."); + }; + + if (anchor_id == 0) { + return TTR("The anchor ID must not be 0 or this anchor won't be bound to an actual anchor."); + }; + + return String(); +}; + +Plane XRAnchor3D::get_plane() const { + Vector3 location = get_translation(); + Basis orientation = get_transform().basis; + + Plane plane(location, orientation.get_axis(1).normalized()); + + return plane; +}; + +Ref XRAnchor3D::get_mesh() const { + return mesh; +} + +XRAnchor3D::XRAnchor3D() { + anchor_id = 1; + is_active = true; +}; + +XRAnchor3D::~XRAnchor3D(){ + // nothing to do here yet for now.. +}; + +//////////////////////////////////////////////////////////////////////////////////////////////////// + +String XROrigin3D::get_configuration_warning() const { + if (!is_visible() || !is_inside_tree()) + return String(); + + if (tracked_camera == nullptr) + return TTR("XROrigin3D requires an XRCamera3D child node."); + + return String(); +}; + +void XROrigin3D::_bind_methods() { + ClassDB::bind_method(D_METHOD("set_world_scale", "world_scale"), &XROrigin3D::set_world_scale); + ClassDB::bind_method(D_METHOD("get_world_scale"), &XROrigin3D::get_world_scale); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "world_scale"), "set_world_scale", "get_world_scale"); +}; + +void XROrigin3D::set_tracked_camera(XRCamera3D *p_tracked_camera) { + tracked_camera = p_tracked_camera; +}; + +void XROrigin3D::clear_tracked_camera_if(XRCamera3D *p_tracked_camera) { + if (tracked_camera == p_tracked_camera) { + tracked_camera = nullptr; + }; +}; + +float XROrigin3D::get_world_scale() const { + // get our XRServer + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL_V(xr_server, 1.0); + + return xr_server->get_world_scale(); +}; + +void XROrigin3D::set_world_scale(float p_world_scale) { + // get our XRServer + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL(xr_server); + + xr_server->set_world_scale(p_world_scale); +}; + +void XROrigin3D::_notification(int p_what) { + // get our XRServer + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL(xr_server); + + switch (p_what) { + case NOTIFICATION_ENTER_TREE: { + set_process_internal(true); + }; break; + case NOTIFICATION_EXIT_TREE: { + set_process_internal(false); + }; break; + case NOTIFICATION_INTERNAL_PROCESS: { + // set our world origin to our node transform + xr_server->set_world_origin(get_global_transform()); + + // check if we have a primary interface + Ref xr_interface = xr_server->get_primary_interface(); + if (xr_interface.is_valid() && tracked_camera != nullptr) { + // get our positioning transform for our headset + Transform t = xr_interface->get_transform_for_eye(XRInterface::EYE_MONO, Transform()); + + // now apply this to our camera + tracked_camera->set_transform(t); + }; + }; break; + default: + break; + }; + + // send our notification to all active XE interfaces, they may need to react to it also + for (int i = 0; i < xr_server->get_interface_count(); i++) { + Ref interface = xr_server->get_interface(i); + if (interface.is_valid() && interface->is_initialized()) { + interface->notification(p_what); + } + } +}; + +XROrigin3D::XROrigin3D() { + tracked_camera = nullptr; +}; + +XROrigin3D::~XROrigin3D(){ + // nothing to do here yet for now.. +}; diff --git a/scene/3d/xr_nodes.h b/scene/3d/xr_nodes.h new file mode 100644 index 0000000000..a2f16545d1 --- /dev/null +++ b/scene/3d/xr_nodes.h @@ -0,0 +1,176 @@ +/*************************************************************************/ +/* xr_nodes.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.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. */ +/*************************************************************************/ + +#ifndef XR_NODES_H +#define XR_NODES_H + +#include "scene/3d/camera_3d.h" +#include "scene/3d/node_3d.h" +#include "scene/resources/mesh.h" +#include "servers/xr/xr_positional_tracker.h" + +/** + @author Bastiaan Olij +**/ + +/* + XRCamera is a subclass of camera which will register itself with its parent XROrigin and as a result is automatically positioned +*/ +class XRCamera3D : public Camera3D { + + GDCLASS(XRCamera3D, Camera3D); + +protected: + void _notification(int p_what); + +public: + String get_configuration_warning() const; + + virtual Vector3 project_local_ray_normal(const Point2 &p_pos) const; + virtual Point2 unproject_position(const Vector3 &p_pos) const; + virtual Vector3 project_position(const Point2 &p_point, float p_z_depth) const; + virtual Vector get_frustum() const; + + XRCamera3D(); + ~XRCamera3D(); +}; + +/* + XRController3D is a helper node that automatically updates its position based on tracker data. + + It must be a child node of our XROrigin node +*/ + +class XRController3D : public Node3D { + + GDCLASS(XRController3D, Node3D); + +private: + int controller_id; + bool is_active; + int button_states; + Ref mesh; + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + void set_controller_id(int p_controller_id); + int get_controller_id(void) const; + String get_controller_name(void) const; + + int get_joystick_id() const; + bool is_button_pressed(int p_button) const; + float get_joystick_axis(int p_axis) const; + + real_t get_rumble() const; + void set_rumble(real_t p_rumble); + + bool get_is_active() const; + XRPositionalTracker::TrackerHand get_hand() const; + + Ref get_mesh(void) const; + + String get_configuration_warning() const; + + XRController3D(); + ~XRController3D(); +}; + +/* + XRAnchor3D is a helper node that automatically updates its position based on anchor data, it represents a real world location. + It must be a child node of our XROrigin3D node +*/ + +class XRAnchor3D : public Node3D { + GDCLASS(XRAnchor3D, Node3D); + +private: + int anchor_id; + bool is_active; + Vector3 size; + Ref mesh; + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + void set_anchor_id(int p_anchor_id); + int get_anchor_id(void) const; + String get_anchor_name(void) const; + + bool get_is_active() const; + Vector3 get_size() const; + + Plane get_plane() const; + + Ref get_mesh(void) const; + + String get_configuration_warning() const; + + XRAnchor3D(); + ~XRAnchor3D(); +}; + +/* + XROrigin3D is special spatial node that acts as our origin point mapping our real world center of our tracking volume into our virtual world. + + It is this point that you will move around the world as the player 'moves while standing still', i.e. the player moves through teleporting or controller inputs as opposed to physically moving. + + Our camera and controllers will always be child nodes and thus place relative to this origin point. + This node will automatically locate any camera child nodes and update its position while our XRController3D node will handle tracked controllers. +*/ +class XROrigin3D : public Node3D { + + GDCLASS(XROrigin3D, Node3D); + +private: + XRCamera3D *tracked_camera; + +protected: + void _notification(int p_what); + static void _bind_methods(); + +public: + String get_configuration_warning() const; + + void set_tracked_camera(XRCamera3D *p_tracked_camera); + void clear_tracked_camera_if(XRCamera3D *p_tracked_camera); + + float get_world_scale() const; + void set_world_scale(float p_world_scale); + + XROrigin3D(); + ~XROrigin3D(); +}; + +#endif /* XR_NODES_H */ diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp index ed4bb05ac7..1cfc3b0260 100644 --- a/scene/main/viewport.cpp +++ b/scene/main/viewport.cpp @@ -3615,13 +3615,14 @@ Viewport::~Viewport() { ///////////////////////////////// -void SubViewport::set_use_arvr(bool p_use_arvr) { - arvr = p_use_arvr; +void SubViewport::set_use_xr(bool p_use_xr) { + xr = p_use_xr; - RS::get_singleton()->viewport_set_use_arvr(get_viewport_rid(), arvr); + RS::get_singleton()->viewport_set_use_xr(get_viewport_rid(), xr); } -bool SubViewport::is_using_arvr() { - return arvr; + +bool SubViewport::is_using_xr() { + return xr; } void SubViewport::set_size(const Size2i &p_size) { @@ -3701,8 +3702,8 @@ void SubViewport::_notification(int p_what) { } void SubViewport::_bind_methods() { - ClassDB::bind_method(D_METHOD("set_use_arvr", "use"), &SubViewport::set_use_arvr); - ClassDB::bind_method(D_METHOD("is_using_arvr"), &SubViewport::is_using_arvr); + ClassDB::bind_method(D_METHOD("set_use_xr", "use"), &SubViewport::set_use_xr); + ClassDB::bind_method(D_METHOD("is_using_xr"), &SubViewport::is_using_xr); ClassDB::bind_method(D_METHOD("set_size", "size"), &SubViewport::set_size); ClassDB::bind_method(D_METHOD("get_size"), &SubViewport::get_size); @@ -3719,7 +3720,7 @@ void SubViewport::_bind_methods() { ClassDB::bind_method(D_METHOD("set_clear_mode", "mode"), &SubViewport::set_clear_mode); ClassDB::bind_method(D_METHOD("get_clear_mode"), &SubViewport::get_clear_mode); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "arvr"), "set_use_arvr", "is_using_arvr"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "xr"), "set_use_xr", "is_using_xr"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size"), "set_size", "get_size"); ADD_PROPERTY(PropertyInfo(Variant::VECTOR2, "size_2d_override"), "set_size_2d_override", "get_size_2d_override"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "size_2d_override_stretch"), "set_size_2d_override_stretch", "is_size_2d_override_stretch_enabled"); @@ -3739,7 +3740,7 @@ void SubViewport::_bind_methods() { } SubViewport::SubViewport() { - arvr = false; + xr = false; size_2d_override_stretch = false; update_mode = UPDATE_WHEN_VISIBLE; clear_mode = CLEAR_MODE_ALWAYS; diff --git a/scene/main/viewport.h b/scene/main/viewport.h index 646ba3c3a1..ab3987d16d 100644 --- a/scene/main/viewport.h +++ b/scene/main/viewport.h @@ -590,7 +590,7 @@ public: private: UpdateMode update_mode; ClearMode clear_mode; - bool arvr; + bool xr; bool size_2d_override_stretch; protected: @@ -606,8 +606,8 @@ public: void set_size_2d_override(const Size2i &p_size); Size2i get_size_2d_override() const; - void set_use_arvr(bool p_use_arvr); - bool is_using_arvr(); + void set_use_xr(bool p_use_xr); + bool is_using_xr(); void set_size_2d_override_stretch(bool p_enable); bool is_size_2d_override_stretch_enabled() const; diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index 94162cbe4e..035d26b3e4 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -179,7 +179,6 @@ #ifndef _3D_DISABLED #include "scene/3d/area_3d.h" -#include "scene/3d/arvr_nodes.h" #include "scene/3d/audio_stream_player_3d.h" #include "scene/3d/baked_lightmap.h" #include "scene/3d/bone_attachment_3d.h" @@ -213,6 +212,7 @@ #include "scene/3d/vehicle_body_3d.h" #include "scene/3d/visibility_notifier_3d.h" #include "scene/3d/world_environment.h" +#include "scene/3d/xr_nodes.h" #include "scene/resources/environment.h" #include "scene/resources/mesh_library.h" #endif @@ -410,10 +410,10 @@ void register_scene_types() { ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); - ClassDB::register_class(); - ClassDB::register_class(); - ClassDB::register_class(); - ClassDB::register_class(); + ClassDB::register_class(); + ClassDB::register_class(); + ClassDB::register_class(); + ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_class(); ClassDB::register_virtual_class(); @@ -853,6 +853,10 @@ void register_scene_types() { ClassDB::add_compatibility_class("World", "World3D"); ClassDB::add_compatibility_class("ProceduralSky", "Sky"); ClassDB::add_compatibility_class("PanoramaSky", "Sky"); + ClassDB::add_compatibility_class("ARVRCamera", "XRCamera3D"); + ClassDB::add_compatibility_class("ARVROrigin", "XROrigin3D"); + ClassDB::add_compatibility_class("ARVRController", "XRController3D"); + ClassDB::add_compatibility_class("ARVRAnchor", "XRAnchor3D"); #endif diff --git a/servers/SCsub b/servers/SCsub index 7080a110da..121990f2e1 100644 --- a/servers/SCsub +++ b/servers/SCsub @@ -5,7 +5,7 @@ Import("env") env.servers_sources = [] env.add_source_files(env.servers_sources, "*.cpp") -SConscript("arvr/SCsub") +SConscript("xr/SCsub") SConscript("camera/SCsub") SConscript("physics_3d/SCsub") SConscript("physics_2d/SCsub") diff --git a/servers/arvr/SCsub b/servers/arvr/SCsub deleted file mode 100644 index 86681f9c74..0000000000 --- a/servers/arvr/SCsub +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env python - -Import("env") - -env.add_source_files(env.servers_sources, "*.cpp") diff --git a/servers/arvr/arvr_interface.cpp b/servers/arvr/arvr_interface.cpp deleted file mode 100644 index 577b4cdd8a..0000000000 --- a/servers/arvr/arvr_interface.cpp +++ /dev/null @@ -1,145 +0,0 @@ -/*************************************************************************/ -/* arvr_interface.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.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 "arvr_interface.h" - -void ARVRInterface::_bind_methods() { - ClassDB::bind_method(D_METHOD("get_name"), &ARVRInterface::get_name); - ClassDB::bind_method(D_METHOD("get_capabilities"), &ARVRInterface::get_capabilities); - - ClassDB::bind_method(D_METHOD("is_primary"), &ARVRInterface::is_primary); - ClassDB::bind_method(D_METHOD("set_is_primary", "enable"), &ARVRInterface::set_is_primary); - - ClassDB::bind_method(D_METHOD("is_initialized"), &ARVRInterface::is_initialized); - ClassDB::bind_method(D_METHOD("set_is_initialized", "initialized"), &ARVRInterface::set_is_initialized); - ClassDB::bind_method(D_METHOD("initialize"), &ARVRInterface::initialize); - ClassDB::bind_method(D_METHOD("uninitialize"), &ARVRInterface::uninitialize); - - ClassDB::bind_method(D_METHOD("get_tracking_status"), &ARVRInterface::get_tracking_status); - - ClassDB::bind_method(D_METHOD("get_render_targetsize"), &ARVRInterface::get_render_targetsize); - ClassDB::bind_method(D_METHOD("is_stereo"), &ARVRInterface::is_stereo); - - ADD_GROUP("Interface", "interface_"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "interface_is_primary"), "set_is_primary", "is_primary"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "interface_is_initialized"), "set_is_initialized", "is_initialized"); - - // we don't have any properties specific to VR yet.... - - // but we do have properties specific to AR.... - ClassDB::bind_method(D_METHOD("get_anchor_detection_is_enabled"), &ARVRInterface::get_anchor_detection_is_enabled); - ClassDB::bind_method(D_METHOD("set_anchor_detection_is_enabled", "enable"), &ARVRInterface::set_anchor_detection_is_enabled); - ClassDB::bind_method(D_METHOD("get_camera_feed_id"), &ARVRInterface::get_camera_feed_id); - - ADD_GROUP("AR", "ar_"); - ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ar_is_anchor_detection_enabled"), "set_anchor_detection_is_enabled", "get_anchor_detection_is_enabled"); - - BIND_ENUM_CONSTANT(ARVR_NONE); - BIND_ENUM_CONSTANT(ARVR_MONO); - BIND_ENUM_CONSTANT(ARVR_STEREO); - BIND_ENUM_CONSTANT(ARVR_AR); - BIND_ENUM_CONSTANT(ARVR_EXTERNAL); - - BIND_ENUM_CONSTANT(EYE_MONO); - BIND_ENUM_CONSTANT(EYE_LEFT); - BIND_ENUM_CONSTANT(EYE_RIGHT); - - BIND_ENUM_CONSTANT(ARVR_NORMAL_TRACKING); - BIND_ENUM_CONSTANT(ARVR_EXCESSIVE_MOTION); - BIND_ENUM_CONSTANT(ARVR_INSUFFICIENT_FEATURES); - BIND_ENUM_CONSTANT(ARVR_UNKNOWN_TRACKING); - BIND_ENUM_CONSTANT(ARVR_NOT_TRACKING); -}; - -StringName ARVRInterface::get_name() const { - return "Unknown"; -}; - -bool ARVRInterface::is_primary() { - ARVRServer *arvr_server = ARVRServer::get_singleton(); - ERR_FAIL_NULL_V(arvr_server, false); - - return arvr_server->get_primary_interface() == this; -}; - -void ARVRInterface::set_is_primary(bool p_is_primary) { - ARVRServer *arvr_server = ARVRServer::get_singleton(); - ERR_FAIL_NULL(arvr_server); - - if (p_is_primary) { - ERR_FAIL_COND(!is_initialized()); - - arvr_server->set_primary_interface(this); - } else { - arvr_server->clear_primary_interface_if(this); - }; -}; - -void ARVRInterface::set_is_initialized(bool p_initialized) { - if (p_initialized) { - if (!is_initialized()) { - initialize(); - }; - } else { - if (is_initialized()) { - uninitialize(); - }; - }; -}; - -ARVRInterface::Tracking_status ARVRInterface::get_tracking_status() const { - return tracking_state; -}; - -ARVRInterface::ARVRInterface() { - tracking_state = ARVR_UNKNOWN_TRACKING; -}; - -ARVRInterface::~ARVRInterface(){}; - -// optional render to external texture which enhances performance on those platforms that require us to submit our end result into special textures. -unsigned int ARVRInterface::get_external_texture_for_eye(ARVRInterface::Eyes p_eye) { - return 0; -}; - -/** these will only be implemented on AR interfaces, so we want dummies for VR **/ -bool ARVRInterface::get_anchor_detection_is_enabled() const { - return false; -}; - -void ARVRInterface::set_anchor_detection_is_enabled(bool p_enable){ - // don't do anything here, this needs to be implemented on AR interface to enable/disable things like plane detection etc. -}; - -int ARVRInterface::get_camera_feed_id() { - // don't do anything here, this needs to be implemented on AR interface to enable/disable things like plane detection etc. - - return 0; -}; diff --git a/servers/arvr/arvr_interface.h b/servers/arvr/arvr_interface.h deleted file mode 100644 index 861061cbf5..0000000000 --- a/servers/arvr/arvr_interface.h +++ /dev/null @@ -1,126 +0,0 @@ -/*************************************************************************/ -/* arvr_interface.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.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. */ -/*************************************************************************/ - -#ifndef ARVR_INTERFACE_H -#define ARVR_INTERFACE_H - -#include "core/math/camera_matrix.h" -#include "core/os/thread_safe.h" -#include "scene/main/window.h" -#include "servers/arvr_server.h" - -/** - @author Bastiaan Olij - - The ARVR interface is a template class ontop of which we build interface to different AR, VR and tracking SDKs. - The idea is that we subclass this class, implement the logic, and then instantiate a singleton of each interface - when Godot starts. These instances do not initialize themselves but register themselves with the AR/VR server. - - If the user wants to enable AR/VR the choose the interface they want to use and initialize it. - - Note that we may make this into a fully instantiable class for GDNative support. -*/ - -class ARVRInterface : public Reference { - GDCLASS(ARVRInterface, Reference); - -public: - enum Capabilities { /* purely meta data, provides some info about what this interface supports */ - ARVR_NONE = 0, /* no capabilities */ - ARVR_MONO = 1, /* can be used with mono output */ - ARVR_STEREO = 2, /* can be used with stereo output */ - ARVR_AR = 4, /* offers a camera feed for AR */ - ARVR_EXTERNAL = 8 /* renders to external device */ - }; - - enum Eyes { - EYE_MONO, /* my son says we should call this EYE_CYCLOPS */ - EYE_LEFT, - EYE_RIGHT - }; - - enum Tracking_status { /* tracking status currently based on AR but we can start doing more with this for VR as well */ - ARVR_NORMAL_TRACKING, - ARVR_EXCESSIVE_MOTION, - ARVR_INSUFFICIENT_FEATURES, - ARVR_UNKNOWN_TRACKING, - ARVR_NOT_TRACKING - }; - -protected: - _THREAD_SAFE_CLASS_ - - Tracking_status tracking_state; - static void _bind_methods(); - -public: - /** general interface information **/ - virtual StringName get_name() const; - virtual int get_capabilities() const = 0; - - bool is_primary(); - void set_is_primary(bool p_is_primary); - - virtual bool is_initialized() const = 0; /* returns true if we've initialized this interface */ - void set_is_initialized(bool p_initialized); /* helper function, will call initialize or uninitialize */ - virtual bool initialize() = 0; /* initialize this interface, if this has an HMD it becomes the primary interface */ - virtual void uninitialize() = 0; /* deinitialize this interface */ - - Tracking_status get_tracking_status() const; /* get the status of our current tracking */ - - /** specific to VR **/ - // nothing yet - - /** specific to AR **/ - virtual bool get_anchor_detection_is_enabled() const; - virtual void set_anchor_detection_is_enabled(bool p_enable); - virtual int get_camera_feed_id(); - - /** rendering and internal **/ - - virtual Size2 get_render_targetsize() = 0; /* returns the recommended render target size per eye for this device */ - virtual bool is_stereo() = 0; /* returns true if this interface requires stereo rendering (for VR HMDs) or mono rendering (for mobile AR) */ - virtual Transform get_transform_for_eye(ARVRInterface::Eyes p_eye, const Transform &p_cam_transform) = 0; /* get each eyes camera transform, also implement EYE_MONO */ - virtual CameraMatrix get_projection_for_eye(ARVRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) = 0; /* get each eyes projection matrix */ - virtual unsigned int get_external_texture_for_eye(ARVRInterface::Eyes p_eye); /* if applicable return external texture to render to */ - virtual void commit_for_eye(ARVRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) = 0; /* output the left or right eye */ - - virtual void process() = 0; - virtual void notification(int p_what) = 0; - - ARVRInterface(); - ~ARVRInterface(); -}; - -VARIANT_ENUM_CAST(ARVRInterface::Capabilities); -VARIANT_ENUM_CAST(ARVRInterface::Eyes); -VARIANT_ENUM_CAST(ARVRInterface::Tracking_status); - -#endif diff --git a/servers/arvr/arvr_positional_tracker.cpp b/servers/arvr/arvr_positional_tracker.cpp deleted file mode 100644 index dabeb7b86f..0000000000 --- a/servers/arvr/arvr_positional_tracker.cpp +++ /dev/null @@ -1,237 +0,0 @@ -/*************************************************************************/ -/* arvr_positional_tracker.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.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 "arvr_positional_tracker.h" -#include "core/input/input_filter.h" - -void ARVRPositionalTracker::_bind_methods() { - BIND_ENUM_CONSTANT(TRACKER_HAND_UNKNOWN); - BIND_ENUM_CONSTANT(TRACKER_LEFT_HAND); - BIND_ENUM_CONSTANT(TRACKER_RIGHT_HAND); - - // this class is read only from GDScript, so we only have access to getters.. - ClassDB::bind_method(D_METHOD("get_type"), &ARVRPositionalTracker::get_type); - ClassDB::bind_method(D_METHOD("get_tracker_id"), &ARVRPositionalTracker::get_tracker_id); - ClassDB::bind_method(D_METHOD("get_name"), &ARVRPositionalTracker::get_name); - ClassDB::bind_method(D_METHOD("get_joy_id"), &ARVRPositionalTracker::get_joy_id); - ClassDB::bind_method(D_METHOD("get_tracks_orientation"), &ARVRPositionalTracker::get_tracks_orientation); - ClassDB::bind_method(D_METHOD("get_orientation"), &ARVRPositionalTracker::get_orientation); - ClassDB::bind_method(D_METHOD("get_tracks_position"), &ARVRPositionalTracker::get_tracks_position); - ClassDB::bind_method(D_METHOD("get_position"), &ARVRPositionalTracker::get_position); - ClassDB::bind_method(D_METHOD("get_hand"), &ARVRPositionalTracker::get_hand); - ClassDB::bind_method(D_METHOD("get_transform", "adjust_by_reference_frame"), &ARVRPositionalTracker::get_transform); - ClassDB::bind_method(D_METHOD("get_mesh"), &ARVRPositionalTracker::get_mesh); - - // these functions we don't want to expose to normal users but do need to be callable from GDNative - ClassDB::bind_method(D_METHOD("_set_type", "type"), &ARVRPositionalTracker::set_type); - ClassDB::bind_method(D_METHOD("_set_name", "name"), &ARVRPositionalTracker::set_name); - ClassDB::bind_method(D_METHOD("_set_joy_id", "joy_id"), &ARVRPositionalTracker::set_joy_id); - ClassDB::bind_method(D_METHOD("_set_orientation", "orientation"), &ARVRPositionalTracker::set_orientation); - ClassDB::bind_method(D_METHOD("_set_rw_position", "rw_position"), &ARVRPositionalTracker::set_rw_position); - ClassDB::bind_method(D_METHOD("_set_mesh", "mesh"), &ARVRPositionalTracker::set_mesh); - ClassDB::bind_method(D_METHOD("get_rumble"), &ARVRPositionalTracker::get_rumble); - ClassDB::bind_method(D_METHOD("set_rumble", "rumble"), &ARVRPositionalTracker::set_rumble); - - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rumble"), "set_rumble", "get_rumble"); -}; - -void ARVRPositionalTracker::set_type(ARVRServer::TrackerType p_type) { - if (type != p_type) { - type = p_type; - hand = ARVRPositionalTracker::TRACKER_HAND_UNKNOWN; - - ARVRServer *arvr_server = ARVRServer::get_singleton(); - ERR_FAIL_NULL(arvr_server); - - // get a tracker id for our type - // note if this is a controller this will be 3 or higher but we may change it later. - tracker_id = arvr_server->get_free_tracker_id_for_type(p_type); - }; -}; - -ARVRServer::TrackerType ARVRPositionalTracker::get_type() const { - return type; -}; - -void ARVRPositionalTracker::set_name(const String &p_name) { - name = p_name; -}; - -StringName ARVRPositionalTracker::get_name() const { - return name; -}; - -int ARVRPositionalTracker::get_tracker_id() const { - return tracker_id; -}; - -void ARVRPositionalTracker::set_joy_id(int p_joy_id) { - joy_id = p_joy_id; -}; - -int ARVRPositionalTracker::get_joy_id() const { - return joy_id; -}; - -bool ARVRPositionalTracker::get_tracks_orientation() const { - return tracks_orientation; -}; - -void ARVRPositionalTracker::set_orientation(const Basis &p_orientation) { - _THREAD_SAFE_METHOD_ - - tracks_orientation = true; // obviously we have this - orientation = p_orientation; -}; - -Basis ARVRPositionalTracker::get_orientation() const { - _THREAD_SAFE_METHOD_ - - return orientation; -}; - -bool ARVRPositionalTracker::get_tracks_position() const { - return tracks_position; -}; - -void ARVRPositionalTracker::set_position(const Vector3 &p_position) { - _THREAD_SAFE_METHOD_ - - ARVRServer *arvr_server = ARVRServer::get_singleton(); - ERR_FAIL_NULL(arvr_server); - real_t world_scale = arvr_server->get_world_scale(); - ERR_FAIL_COND(world_scale == 0); - - tracks_position = true; // obviously we have this - rw_position = p_position / world_scale; -}; - -Vector3 ARVRPositionalTracker::get_position() const { - _THREAD_SAFE_METHOD_ - - ARVRServer *arvr_server = ARVRServer::get_singleton(); - ERR_FAIL_NULL_V(arvr_server, rw_position); - real_t world_scale = arvr_server->get_world_scale(); - - return rw_position * world_scale; -}; - -void ARVRPositionalTracker::set_rw_position(const Vector3 &p_rw_position) { - _THREAD_SAFE_METHOD_ - - tracks_position = true; // obviously we have this - rw_position = p_rw_position; -}; - -Vector3 ARVRPositionalTracker::get_rw_position() const { - _THREAD_SAFE_METHOD_ - - return rw_position; -}; - -void ARVRPositionalTracker::set_mesh(const Ref &p_mesh) { - _THREAD_SAFE_METHOD_ - - mesh = p_mesh; -}; - -Ref ARVRPositionalTracker::get_mesh() const { - _THREAD_SAFE_METHOD_ - - return mesh; -}; - -ARVRPositionalTracker::TrackerHand ARVRPositionalTracker::get_hand() const { - return hand; -}; - -void ARVRPositionalTracker::set_hand(const ARVRPositionalTracker::TrackerHand p_hand) { - ARVRServer *arvr_server = ARVRServer::get_singleton(); - ERR_FAIL_NULL(arvr_server); - - if (hand != p_hand) { - // we can only set this if we've previously set this to be a controller!! - ERR_FAIL_COND((type != ARVRServer::TRACKER_CONTROLLER) && (p_hand != ARVRPositionalTracker::TRACKER_HAND_UNKNOWN)); - - hand = p_hand; - if (hand == ARVRPositionalTracker::TRACKER_LEFT_HAND) { - if (!arvr_server->is_tracker_id_in_use_for_type(type, 1)) { - tracker_id = 1; - }; - } else if (hand == ARVRPositionalTracker::TRACKER_RIGHT_HAND) { - if (!arvr_server->is_tracker_id_in_use_for_type(type, 2)) { - tracker_id = 2; - }; - }; - }; -}; - -Transform ARVRPositionalTracker::get_transform(bool p_adjust_by_reference_frame) const { - Transform new_transform; - - new_transform.basis = get_orientation(); - new_transform.origin = get_position(); - - if (p_adjust_by_reference_frame) { - ARVRServer *arvr_server = ARVRServer::get_singleton(); - ERR_FAIL_NULL_V(arvr_server, new_transform); - - new_transform = arvr_server->get_reference_frame() * new_transform; - }; - - return new_transform; -}; - -real_t ARVRPositionalTracker::get_rumble() const { - return rumble; -}; - -void ARVRPositionalTracker::set_rumble(real_t p_rumble) { - if (p_rumble > 0.0) { - rumble = p_rumble; - } else { - rumble = 0.0; - }; -}; - -ARVRPositionalTracker::ARVRPositionalTracker() { - type = ARVRServer::TRACKER_UNKNOWN; - name = "Unknown"; - joy_id = -1; - tracker_id = 0; - tracks_orientation = false; - tracks_position = false; - hand = TRACKER_HAND_UNKNOWN; - rumble = 0.0; -}; - -ARVRPositionalTracker::~ARVRPositionalTracker(){ - -}; diff --git a/servers/arvr/arvr_positional_tracker.h b/servers/arvr/arvr_positional_tracker.h deleted file mode 100644 index 03c6b33ffe..0000000000 --- a/servers/arvr/arvr_positional_tracker.h +++ /dev/null @@ -1,104 +0,0 @@ -/*************************************************************************/ -/* arvr_positional_tracker.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.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. */ -/*************************************************************************/ - -#ifndef ARVR_POSITIONAL_TRACKER_H -#define ARVR_POSITIONAL_TRACKER_H - -#include "core/os/thread_safe.h" -#include "scene/resources/mesh.h" -#include "servers/arvr_server.h" - -/** - @author Bastiaan Olij - - The positional tracker object as an object that represents the position and orientation of a tracked object like a controller or headset. - An AR/VR Interface will registered the trackers it manages with our AR/VR server and update its position and orientation. - This is where potentially additional AR/VR interfaces may be active as there are AR/VR SDKs that solely deal with positional tracking. -*/ - -class ARVRPositionalTracker : public Object { - GDCLASS(ARVRPositionalTracker, Object); - _THREAD_SAFE_CLASS_ - -public: - enum TrackerHand { - TRACKER_HAND_UNKNOWN, /* unknown or not applicable */ - TRACKER_LEFT_HAND, /* controller is the left hand controller */ - TRACKER_RIGHT_HAND /* controller is the right hand controller */ - }; - -private: - ARVRServer::TrackerType type; // type of tracker - StringName name; // (unique) name of the tracker - int tracker_id; // tracker index id that is unique per type - int joy_id; // if we also have a related joystick entity, the id of the joystick - bool tracks_orientation; // do we track orientation? - Basis orientation; // our orientation - bool tracks_position; // do we track position? - Vector3 rw_position; // our position "in the real world, so without world_scale applied" - Ref mesh; // when available, a mesh that can be used to render this tracker - TrackerHand hand; // if known, the hand this tracker is held in - real_t rumble; // rumble strength, 0.0 is off, 1.0 is maximum, note that we only record here, arvr_interface is responsible for execution - -protected: - static void _bind_methods(); - -public: - void set_type(ARVRServer::TrackerType p_type); - ARVRServer::TrackerType get_type() const; - void set_name(const String &p_name); - StringName get_name() const; - int get_tracker_id() const; - void set_joy_id(int p_joy_id); - int get_joy_id() const; - bool get_tracks_orientation() const; - void set_orientation(const Basis &p_orientation); - Basis get_orientation() const; - bool get_tracks_position() const; - void set_position(const Vector3 &p_position); // set position with world_scale applied - Vector3 get_position() const; // get position with world_scale applied - void set_rw_position(const Vector3 &p_rw_position); - Vector3 get_rw_position() const; - ARVRPositionalTracker::TrackerHand get_hand() const; - void set_hand(const ARVRPositionalTracker::TrackerHand p_hand); - real_t get_rumble() const; - void set_rumble(real_t p_rumble); - void set_mesh(const Ref &p_mesh); - Ref get_mesh() const; - - Transform get_transform(bool p_adjust_by_reference_frame) const; - - ARVRPositionalTracker(); - ~ARVRPositionalTracker(); -}; - -VARIANT_ENUM_CAST(ARVRPositionalTracker::TrackerHand); - -#endif diff --git a/servers/arvr_server.cpp b/servers/arvr_server.cpp deleted file mode 100644 index f5597d8974..0000000000 --- a/servers/arvr_server.cpp +++ /dev/null @@ -1,386 +0,0 @@ -/*************************************************************************/ -/* arvr_server.cpp */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.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 "arvr_server.h" -#include "arvr/arvr_interface.h" -#include "arvr/arvr_positional_tracker.h" -#include "core/project_settings.h" - -ARVRServer *ARVRServer::singleton = nullptr; - -ARVRServer *ARVRServer::get_singleton() { - return singleton; -}; - -void ARVRServer::_bind_methods() { - ClassDB::bind_method(D_METHOD("get_world_scale"), &ARVRServer::get_world_scale); - ClassDB::bind_method(D_METHOD("set_world_scale"), &ARVRServer::set_world_scale); - ClassDB::bind_method(D_METHOD("get_reference_frame"), &ARVRServer::get_reference_frame); - ClassDB::bind_method(D_METHOD("center_on_hmd", "rotation_mode", "keep_height"), &ARVRServer::center_on_hmd); - ClassDB::bind_method(D_METHOD("get_hmd_transform"), &ARVRServer::get_hmd_transform); - - ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "world_scale"), "set_world_scale", "get_world_scale"); - - ClassDB::bind_method(D_METHOD("get_interface_count"), &ARVRServer::get_interface_count); - ClassDB::bind_method(D_METHOD("get_interface", "idx"), &ARVRServer::get_interface); - ClassDB::bind_method(D_METHOD("get_interfaces"), &ARVRServer::get_interfaces); - ClassDB::bind_method(D_METHOD("find_interface", "name"), &ARVRServer::find_interface); - ClassDB::bind_method(D_METHOD("get_tracker_count"), &ARVRServer::get_tracker_count); - ClassDB::bind_method(D_METHOD("get_tracker", "idx"), &ARVRServer::get_tracker); - - ClassDB::bind_method(D_METHOD("get_primary_interface"), &ARVRServer::get_primary_interface); - ClassDB::bind_method(D_METHOD("set_primary_interface", "interface"), &ARVRServer::set_primary_interface); - - ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "primary_interface"), "set_primary_interface", "get_primary_interface"); - - ClassDB::bind_method(D_METHOD("get_last_process_usec"), &ARVRServer::get_last_process_usec); - ClassDB::bind_method(D_METHOD("get_last_commit_usec"), &ARVRServer::get_last_commit_usec); - ClassDB::bind_method(D_METHOD("get_last_frame_usec"), &ARVRServer::get_last_frame_usec); - - BIND_ENUM_CONSTANT(TRACKER_CONTROLLER); - BIND_ENUM_CONSTANT(TRACKER_BASESTATION); - BIND_ENUM_CONSTANT(TRACKER_ANCHOR); - BIND_ENUM_CONSTANT(TRACKER_ANY_KNOWN); - BIND_ENUM_CONSTANT(TRACKER_UNKNOWN); - BIND_ENUM_CONSTANT(TRACKER_ANY); - - BIND_ENUM_CONSTANT(RESET_FULL_ROTATION); - BIND_ENUM_CONSTANT(RESET_BUT_KEEP_TILT); - BIND_ENUM_CONSTANT(DONT_RESET_ROTATION); - - ADD_SIGNAL(MethodInfo("interface_added", PropertyInfo(Variant::STRING_NAME, "interface_name"))); - ADD_SIGNAL(MethodInfo("interface_removed", PropertyInfo(Variant::STRING_NAME, "interface_name"))); - - ADD_SIGNAL(MethodInfo("tracker_added", PropertyInfo(Variant::STRING_NAME, "tracker_name"), PropertyInfo(Variant::INT, "type"), PropertyInfo(Variant::INT, "id"))); - ADD_SIGNAL(MethodInfo("tracker_removed", PropertyInfo(Variant::STRING_NAME, "tracker_name"), PropertyInfo(Variant::INT, "type"), PropertyInfo(Variant::INT, "id"))); -}; - -real_t ARVRServer::get_world_scale() const { - return world_scale; -}; - -void ARVRServer::set_world_scale(real_t p_world_scale) { - if (p_world_scale < 0.01) { - p_world_scale = 0.01; - } else if (p_world_scale > 1000.0) { - p_world_scale = 1000.0; - } - - world_scale = p_world_scale; -}; - -Transform ARVRServer::get_world_origin() const { - return world_origin; -}; - -void ARVRServer::set_world_origin(const Transform &p_world_origin) { - world_origin = p_world_origin; -}; - -Transform ARVRServer::get_reference_frame() const { - return reference_frame; -}; - -void ARVRServer::center_on_hmd(RotationMode p_rotation_mode, bool p_keep_height) { - if (primary_interface != nullptr) { - // clear our current reference frame or we'll end up double adjusting it - reference_frame = Transform(); - - // requesting our EYE_MONO transform should return our current HMD position - Transform new_reference_frame = primary_interface->get_transform_for_eye(ARVRInterface::EYE_MONO, Transform()); - - // remove our tilt - if (p_rotation_mode == 1) { - // take the Y out of our Z - new_reference_frame.basis.set_axis(2, Vector3(new_reference_frame.basis.elements[0][2], 0.0, new_reference_frame.basis.elements[2][2]).normalized()); - - // Y is straight up - new_reference_frame.basis.set_axis(1, Vector3(0.0, 1.0, 0.0)); - - // and X is our cross reference - new_reference_frame.basis.set_axis(0, new_reference_frame.basis.get_axis(1).cross(new_reference_frame.basis.get_axis(2)).normalized()); - } else if (p_rotation_mode == 2) { - // remove our rotation, we're only interesting in centering on position - new_reference_frame.basis = Basis(); - }; - - // don't negate our height - if (p_keep_height) { - new_reference_frame.origin.y = 0.0; - }; - - reference_frame = new_reference_frame.inverse(); - }; -}; - -Transform ARVRServer::get_hmd_transform() { - Transform hmd_transform; - if (primary_interface != nullptr) { - hmd_transform = primary_interface->get_transform_for_eye(ARVRInterface::EYE_MONO, hmd_transform); - }; - return hmd_transform; -}; - -void ARVRServer::add_interface(const Ref &p_interface) { - ERR_FAIL_COND(p_interface.is_null()); - - for (int i = 0; i < interfaces.size(); i++) { - - if (interfaces[i] == p_interface) { - ERR_PRINT("Interface was already added"); - return; - }; - }; - - interfaces.push_back(p_interface); - emit_signal("interface_added", p_interface->get_name()); -}; - -void ARVRServer::remove_interface(const Ref &p_interface) { - ERR_FAIL_COND(p_interface.is_null()); - - int idx = -1; - for (int i = 0; i < interfaces.size(); i++) { - - if (interfaces[i] == p_interface) { - - idx = i; - break; - }; - }; - - ERR_FAIL_COND(idx == -1); - - print_verbose("ARVR: Removed interface" + p_interface->get_name()); - - emit_signal("interface_removed", p_interface->get_name()); - interfaces.remove(idx); -}; - -int ARVRServer::get_interface_count() const { - return interfaces.size(); -}; - -Ref ARVRServer::get_interface(int p_index) const { - ERR_FAIL_INDEX_V(p_index, interfaces.size(), nullptr); - - return interfaces[p_index]; -}; - -Ref ARVRServer::find_interface(const String &p_name) const { - int idx = -1; - for (int i = 0; i < interfaces.size(); i++) { - - if (interfaces[i]->get_name() == p_name) { - - idx = i; - break; - }; - }; - - ERR_FAIL_COND_V(idx == -1, nullptr); - - return interfaces[idx]; -}; - -Array ARVRServer::get_interfaces() const { - Array ret; - - for (int i = 0; i < interfaces.size(); i++) { - Dictionary iface_info; - - iface_info["id"] = i; - iface_info["name"] = interfaces[i]->get_name(); - - ret.push_back(iface_info); - }; - - return ret; -}; - -/* - A little extra info on the tracker ids, these are unique per tracker type so we get some consistency in recognising our trackers, specifically controllers. - - The first controller that is turned of will get ID 1, the second will get ID 2, etc. - The magic happens when one of the controllers is turned off, say controller 1 turns off, controller 2 will remain controller 2, controller 3 will remain controller 3. - If controller number 1 is turned on again it again gets ID 1 unless another new controller was turned on since. - - The most likely scenario however is a controller that runs out of battery and another controller being used to replace it. - Because the controllers are often linked to physical objects, say you're holding a shield in controller 1, your left hand, and a gun in controller 2, your right hand, and controller 1 dies: - - using our tracker index would suddenly make the gun disappear and the shield jump into your right hand because controller 2 becomes controller 1. - - using this approach the shield disappears or is no longer tracked, but the gun stays firmly in your right hand because that is still controller 2, further more, if controller 1 is replaced the shield will return. -*/ - -bool ARVRServer::is_tracker_id_in_use_for_type(TrackerType p_tracker_type, int p_tracker_id) const { - for (int i = 0; i < trackers.size(); i++) { - if (trackers[i]->get_type() == p_tracker_type && trackers[i]->get_tracker_id() == p_tracker_id) { - return true; - }; - }; - - // all good - return false; -}; - -int ARVRServer::get_free_tracker_id_for_type(TrackerType p_tracker_type) { - // We start checking at 1, 0 means that it's not a controller.. - // Note that for controller we reserve: - // - 1 for the left hand controller and - // - 2 for the right hand controller - // so we start at 3 :) - int tracker_id = p_tracker_type == ARVRServer::TRACKER_CONTROLLER ? 3 : 1; - - while (is_tracker_id_in_use_for_type(p_tracker_type, tracker_id)) { - // try the next one - tracker_id++; - }; - - return tracker_id; -}; - -void ARVRServer::add_tracker(ARVRPositionalTracker *p_tracker) { - ERR_FAIL_NULL(p_tracker); - - trackers.push_back(p_tracker); - emit_signal("tracker_added", p_tracker->get_name(), p_tracker->get_type(), p_tracker->get_tracker_id()); -}; - -void ARVRServer::remove_tracker(ARVRPositionalTracker *p_tracker) { - ERR_FAIL_NULL(p_tracker); - - int idx = -1; - for (int i = 0; i < trackers.size(); i++) { - - if (trackers[i] == p_tracker) { - - idx = i; - break; - }; - }; - - ERR_FAIL_COND(idx == -1); - - emit_signal("tracker_removed", p_tracker->get_name(), p_tracker->get_type(), p_tracker->get_tracker_id()); - trackers.remove(idx); -}; - -int ARVRServer::get_tracker_count() const { - return trackers.size(); -}; - -ARVRPositionalTracker *ARVRServer::get_tracker(int p_index) const { - ERR_FAIL_INDEX_V(p_index, trackers.size(), nullptr); - - return trackers[p_index]; -}; - -ARVRPositionalTracker *ARVRServer::find_by_type_and_id(TrackerType p_tracker_type, int p_tracker_id) const { - ERR_FAIL_COND_V(p_tracker_id == 0, nullptr); - - for (int i = 0; i < trackers.size(); i++) { - if (trackers[i]->get_type() == p_tracker_type && trackers[i]->get_tracker_id() == p_tracker_id) { - return trackers[i]; - }; - }; - - return nullptr; -}; - -Ref ARVRServer::get_primary_interface() const { - return primary_interface; -}; - -void ARVRServer::set_primary_interface(const Ref &p_primary_interface) { - primary_interface = p_primary_interface; - - print_verbose("ARVR: Primary interface set to: " + primary_interface->get_name()); -}; - -void ARVRServer::clear_primary_interface_if(const Ref &p_primary_interface) { - if (primary_interface == p_primary_interface) { - print_verbose("ARVR: Clearing primary interface"); - primary_interface.unref(); - }; -}; - -uint64_t ARVRServer::get_last_process_usec() { - return last_process_usec; -}; - -uint64_t ARVRServer::get_last_commit_usec() { - return last_commit_usec; -}; - -uint64_t ARVRServer::get_last_frame_usec() { - return last_frame_usec; -}; - -void ARVRServer::_process() { - /* called from rendering_server_viewport.draw_viewports right before we start drawing our viewports */ - - /* mark for our frame timing */ - last_process_usec = OS::get_singleton()->get_ticks_usec(); - - /* process all active interfaces */ - for (int i = 0; i < interfaces.size(); i++) { - if (!interfaces[i].is_valid()) { - // ignore, not a valid reference - } else if (interfaces[i]->is_initialized()) { - interfaces.write[i]->process(); - }; - }; -}; - -void ARVRServer::_mark_commit() { - /* time this */ - last_commit_usec = OS::get_singleton()->get_ticks_usec(); - - /* now store our difference as we may overwrite last_process_usec before this is accessed */ - last_frame_usec = last_commit_usec - last_process_usec; -}; - -ARVRServer::ARVRServer() { - singleton = this; - world_scale = 1.0; -}; - -ARVRServer::~ARVRServer() { - primary_interface.unref(); - - while (interfaces.size() > 0) { - interfaces.remove(0); - } - - while (trackers.size() > 0) { - trackers.remove(0); - } - - singleton = nullptr; -}; diff --git a/servers/arvr_server.h b/servers/arvr_server.h deleted file mode 100644 index ab2f0d721b..0000000000 --- a/servers/arvr_server.h +++ /dev/null @@ -1,192 +0,0 @@ -/*************************************************************************/ -/* arvr_server.h */ -/*************************************************************************/ -/* This file is part of: */ -/* GODOT ENGINE */ -/* https://godotengine.org */ -/*************************************************************************/ -/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ -/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.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. */ -/*************************************************************************/ - -#ifndef ARVR_SERVER_H -#define ARVR_SERVER_H - -#include "core/os/os.h" -#include "core/os/thread_safe.h" -#include "core/reference.h" -#include "core/rid.h" -#include "core/variant.h" - -class ARVRInterface; -class ARVRPositionalTracker; - -/** - @author Bastiaan Olij - - The ARVR server is a singleton object that gives access to the various - objects and SDKs that are available on the system. - Because there can be multiple SDKs active this is exposed as an array - and our ARVR server object acts as a pass through - Also each positioning tracker is accessible from here. - - I've added some additional info into this header file that should move - into the documentation, I will do so when we're close to accepting this PR - or as a separate PR once this has been merged into the master branch. -**/ - -class ARVRServer : public Object { - GDCLASS(ARVRServer, Object); - _THREAD_SAFE_CLASS_ - -public: - enum TrackerType { - TRACKER_CONTROLLER = 0x01, /* tracks a controller */ - TRACKER_BASESTATION = 0x02, /* tracks location of a base station */ - TRACKER_ANCHOR = 0x04, /* tracks an anchor point, used in AR to track a real live location */ - TRACKER_UNKNOWN = 0x80, /* unknown tracker */ - - TRACKER_ANY_KNOWN = 0x7f, /* all except unknown */ - TRACKER_ANY = 0xff /* used by get_connected_trackers to return all types */ - }; - - enum RotationMode { - RESET_FULL_ROTATION = 0, /* we reset the full rotation, regardless of how the HMD is oriented, we're looking dead ahead */ - RESET_BUT_KEEP_TILT = 1, /* reset rotation but keep tilt. */ - DONT_RESET_ROTATION = 2, /* don't reset the rotation, we will only center on position */ - }; - -private: - Vector> interfaces; - Vector trackers; - - Ref primary_interface; /* we'll identify one interface as primary, this will be used by our viewports */ - - real_t world_scale; /* scale by which we multiply our tracker positions */ - Transform world_origin; /* our world origin point, maps a location in our virtual world to the origin point in our real world tracking volume */ - Transform reference_frame; /* our reference frame */ - - uint64_t last_process_usec; /* for frame timing, usec when we did our processing */ - uint64_t last_commit_usec; /* for frame timing, usec when we finished committing both eyes */ - uint64_t last_frame_usec; /* time it took between process and committing, we should probably average this over the last x frames */ - -protected: - static ARVRServer *singleton; - - static void _bind_methods(); - -public: - static ARVRServer *get_singleton(); - - /* - World scale allows you to specify a scale factor that is applied to all positioning vectors in our VR world in essence scaling up, or scaling down the world. - For stereoscopic rendering specifically this is very important to give an accurate sense of scale. - Add controllers into the mix and an accurate mapping of real world movement to perceived virtual movement becomes very important. - - Most VR platforms, and our assumption, is that 1 unit in our virtual world equates to 1 meter in the real mode. - This scale basically effects the unit size relationship to real world size. - - I may remove access to this property in GDScript in favour of exposing it on the ARVROrigin node - */ - real_t get_world_scale() const; - void set_world_scale(real_t p_world_scale); - - /* - The world maps the 0,0,0 coordinate of our real world coordinate system for our tracking volume to a location in our - virtual world. It is this origin point that should be moved when the player is moved through the world by controller - actions be it straffing, teleporting, etc. Movement of the player by moving through the physical space is always tracked - in relation to this point. - - Note that the ARVROrigin spatial node in your scene automatically updates this property and it should be used instead of - direct access to this property and it therefore is not available in GDScript - - Note: this should not be used in AR and should be ignored by an AR based interface as it would throw what you're looking at in the real world - and in the virtual world out of sync - */ - Transform get_world_origin() const; - void set_world_origin(const Transform &p_world_origin); - - /* - center_on_hmd calculates a new reference frame. This ensures the HMD is positioned to 0,0,0 facing 0,0,-1 (need to verify this direction) - in the virtual world. - - You can ignore the tilt of the device ensuring you're looking straight forward even if the player is looking down or sideways. - You can chose to keep the height the tracking provides which is important for room scale capable tracking. - - Note: this should not be used in AR and should be ignored by an AR based interface as it would throw what you're looking at in the real world - and in the virtual world out of sync - */ - Transform get_reference_frame() const; - void center_on_hmd(RotationMode p_rotation_mode, bool p_keep_height); - - /* - get_hmd_transform gets our hmd transform (centered between eyes) with most up to date tracking, relative to the origin - */ - Transform get_hmd_transform(); - - /* - Interfaces are objects that 'glue' Godot to an AR or VR SDK such as the Oculus SDK, OpenVR, OpenHMD, etc. - */ - void add_interface(const Ref &p_interface); - void remove_interface(const Ref &p_interface); - int get_interface_count() const; - Ref get_interface(int p_index) const; - Ref find_interface(const String &p_name) const; - Array get_interfaces() const; - - /* - note, more then one interface can technically be active, especially on mobile, but only one interface is used for - rendering. This interface identifies itself by calling set_primary_interface when it is initialized - */ - Ref get_primary_interface() const; - void set_primary_interface(const Ref &p_primary_interface); - void clear_primary_interface_if(const Ref &p_primary_interface); /* this is automatically called if an interface destructs */ - - /* - Our trackers are objects that expose the orientation and position of physical devices such as controller, anchor points, etc. - They are created and managed by our active AR/VR interfaces. - */ - bool is_tracker_id_in_use_for_type(TrackerType p_tracker_type, int p_tracker_id) const; - int get_free_tracker_id_for_type(TrackerType p_tracker_type); - void add_tracker(ARVRPositionalTracker *p_tracker); - void remove_tracker(ARVRPositionalTracker *p_tracker); - int get_tracker_count() const; - ARVRPositionalTracker *get_tracker(int p_index) const; - ARVRPositionalTracker *find_by_type_and_id(TrackerType p_tracker_type, int p_tracker_id) const; - - uint64_t get_last_process_usec(); - uint64_t get_last_commit_usec(); - uint64_t get_last_frame_usec(); - - void _process(); - void _mark_commit(); - - ARVRServer(); - ~ARVRServer(); -}; - -#define ARVR ARVRServer - -VARIANT_ENUM_CAST(ARVRServer::TrackerType); -VARIANT_ENUM_CAST(ARVRServer::RotationMode); - -#endif diff --git a/servers/register_server_types.cpp b/servers/register_server_types.cpp index 64b48bea50..dadd26dade 100644 --- a/servers/register_server_types.cpp +++ b/servers/register_server_types.cpp @@ -33,9 +33,6 @@ #include "core/engine.h" #include "core/project_settings.h" -#include "arvr/arvr_interface.h" -#include "arvr/arvr_positional_tracker.h" -#include "arvr_server.h" #include "audio/audio_effect.h" #include "audio/audio_stream.h" #include "audio/effects/audio_effect_amplify.h" @@ -67,6 +64,9 @@ #include "physics_server_3d.h" #include "rendering_server.h" #include "servers/rendering/shader_types.h" +#include "xr/xr_interface.h" +#include "xr/xr_positional_tracker.h" +#include "xr_server.h" ShaderTypes *shader_types = nullptr; @@ -102,11 +102,15 @@ void register_server_types() { ClassDB::register_class(); ClassDB::register_virtual_class(); ClassDB::register_virtual_class(); - ClassDB::register_class(); + ClassDB::register_class(); ClassDB::register_class(); - ClassDB::register_virtual_class(); - ClassDB::register_class(); + ClassDB::register_virtual_class(); + ClassDB::register_class(); + + ClassDB::add_compatibility_class("ARVRServer", "XRServer"); + ClassDB::add_compatibility_class("ARVRInterface", "XRInterface"); + ClassDB::add_compatibility_class("ARVRPositionalTracker", "XRPositionalTracker"); ClassDB::register_virtual_class(); ClassDB::register_virtual_class(); @@ -198,6 +202,6 @@ void register_server_singletons() { Engine::get_singleton()->add_singleton(Engine::Singleton("PhysicsServer3D", PhysicsServer3D::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationServer2D", NavigationServer2D::get_singleton_mut())); Engine::get_singleton()->add_singleton(Engine::Singleton("NavigationServer3D", NavigationServer3D::get_singleton_mut())); - Engine::get_singleton()->add_singleton(Engine::Singleton("ARVRServer", ARVRServer::get_singleton())); + Engine::get_singleton()->add_singleton(Engine::Singleton("XRServer", XRServer::get_singleton())); Engine::get_singleton()->add_singleton(Engine::Singleton("CameraServer", CameraServer::get_singleton())); } diff --git a/servers/rendering/rendering_server_raster.h b/servers/rendering/rendering_server_raster.h index 1162946796..1b9755397a 100644 --- a/servers/rendering/rendering_server_raster.h +++ b/servers/rendering/rendering_server_raster.h @@ -456,7 +456,7 @@ public: BIND0R(RID, viewport_create) - BIND2(viewport_set_use_arvr, RID, bool) + BIND2(viewport_set_use_xr, RID, bool) BIND3(viewport_set_size, RID, int, int) BIND2(viewport_set_active, RID, bool) diff --git a/servers/rendering/rendering_server_scene.cpp b/servers/rendering/rendering_server_scene.cpp index a367d4522c..4f40012c98 100644 --- a/servers/rendering/rendering_server_scene.cpp +++ b/servers/rendering/rendering_server_scene.cpp @@ -1883,7 +1883,7 @@ void RenderingServerScene::render_camera(RID p_render_buffers, RID p_camera, RID #endif } -void RenderingServerScene::render_camera(RID p_render_buffers, Ref &p_interface, ARVRInterface::Eyes p_eye, RID p_camera, RID p_scenario, Size2 p_viewport_size, RID p_shadow_atlas) { +void RenderingServerScene::render_camera(RID p_render_buffers, Ref &p_interface, XRInterface::Eyes p_eye, RID p_camera, RID p_scenario, Size2 p_viewport_size, RID p_shadow_atlas) { // render for AR/VR interface Camera *camera = camera_owner.getornull(p_camera); @@ -1895,16 +1895,14 @@ void RenderingServerScene::render_camera(RID p_render_buffers, Refget_world_origin(); + Transform world_origin = XRServer::get_singleton()->get_world_origin(); Transform cam_transform = p_interface->get_transform_for_eye(p_eye, world_origin); // For stereo render we only prepare for our left eye and then reuse the outcome for our right eye - if (p_eye == ARVRInterface::EYE_LEFT) { - ///@TODO possibly move responsibility for this into our ARVRServer or ARVRInterface? - + if (p_eye == XRInterface::EYE_LEFT) { // Center our transform, we assume basis is equal. Transform mono_transform = cam_transform; - Transform right_transform = p_interface->get_transform_for_eye(ARVRInterface::EYE_RIGHT, world_origin); + Transform right_transform = p_interface->get_transform_for_eye(XRInterface::EYE_RIGHT, world_origin); mono_transform.origin += right_transform.origin; mono_transform.origin *= 0.5; @@ -1958,7 +1956,7 @@ void RenderingServerScene::render_camera(RID p_render_buffers, Refenv, camera->effects, camera->visible_layers, p_scenario, p_shadow_atlas, RID()); - } else if (p_eye == ARVRInterface::EYE_MONO) { + } else if (p_eye == XRInterface::EYE_MONO) { // For mono render, prepare as per usual _prepare_scene(cam_transform, camera_matrix, false, false, camera->env, camera->effects, camera->visible_layers, p_scenario, p_shadow_atlas, RID()); } diff --git a/servers/rendering/rendering_server_scene.h b/servers/rendering/rendering_server_scene.h index 80f226e1cb..0970fed6c4 100644 --- a/servers/rendering/rendering_server_scene.h +++ b/servers/rendering/rendering_server_scene.h @@ -39,7 +39,7 @@ #include "core/os/thread.h" #include "core/rid_owner.h" #include "core/self_list.h" -#include "servers/arvr/arvr_interface.h" +#include "servers/xr/xr_interface.h" class RenderingServerScene { public: @@ -426,7 +426,7 @@ public: void render_empty_scene(RID p_render_buffers, RID p_scenario, RID p_shadow_atlas); void render_camera(RID p_render_buffers, RID p_camera, RID p_scenario, Size2 p_viewport_size, RID p_shadow_atlas); - void render_camera(RID p_render_buffers, Ref &p_interface, ARVRInterface::Eyes p_eye, RID p_camera, RID p_scenario, Size2 p_viewport_size, RID p_shadow_atlas); + void render_camera(RID p_render_buffers, Ref &p_interface, XRInterface::Eyes p_eye, RID p_camera, RID p_scenario, Size2 p_viewport_size, RID p_shadow_atlas); void update_dirty_instances(); void render_probes(); diff --git a/servers/rendering/rendering_server_viewport.cpp b/servers/rendering/rendering_server_viewport.cpp index aa65101ddf..87dcb772bc 100644 --- a/servers/rendering/rendering_server_viewport.cpp +++ b/servers/rendering/rendering_server_viewport.cpp @@ -62,24 +62,24 @@ static Transform2D _canvas_get_transform(RenderingServerViewport::Viewport *p_vi return xf; } -void RenderingServerViewport::_draw_3d(Viewport *p_viewport, ARVRInterface::Eyes p_eye) { +void RenderingServerViewport::_draw_3d(Viewport *p_viewport, XRInterface::Eyes p_eye) { RENDER_TIMESTAMP(">Begin Rendering 3D Scene"); - Ref arvr_interface; - if (ARVRServer::get_singleton() != nullptr) { - arvr_interface = ARVRServer::get_singleton()->get_primary_interface(); + Ref xr_interface; + if (XRServer::get_singleton() != nullptr) { + xr_interface = XRServer::get_singleton()->get_primary_interface(); } - if (p_viewport->use_arvr && arvr_interface.is_valid()) { - RSG::scene->render_camera(p_viewport->render_buffers, arvr_interface, p_eye, p_viewport->camera, p_viewport->scenario, p_viewport->size, p_viewport->shadow_atlas); + if (p_viewport->use_xr && xr_interface.is_valid()) { + RSG::scene->render_camera(p_viewport->render_buffers, xr_interface, p_eye, p_viewport->camera, p_viewport->scenario, p_viewport->size, p_viewport->shadow_atlas); } else { RSG::scene->render_camera(p_viewport->render_buffers, p_viewport->camera, p_viewport->scenario, p_viewport->size, p_viewport->shadow_atlas); } RENDER_TIMESTAMP(" arvr_interface; + // get our xr interface in case we need it + Ref xr_interface; - if (ARVRServer::get_singleton() != nullptr) { - arvr_interface = ARVRServer::get_singleton()->get_primary_interface(); + if (XRServer::get_singleton() != nullptr) { + xr_interface = XRServer::get_singleton()->get_primary_interface(); // process all our active interfaces - ARVRServer::get_singleton()->_process(); + XRServer::get_singleton()->_process(); } -#endif if (Engine::get_singleton()->is_editor_hint()) { set_default_clear_color(GLOBAL_GET("rendering/environment/default_clear_color")); @@ -367,38 +365,41 @@ void RenderingServerViewport::draw_viewports() { RSG::storage->render_target_set_as_unused(vp->render_target); #if 0 - if (vp->use_arvr && arvr_interface.is_valid()) { + // TODO fix up this code after we change our commit_for_eye to accept our new render targets + + if (vp->use_xr && xr_interface.is_valid()) { // override our size, make sure it matches our required size - vp->size = arvr_interface->get_render_targetsize(); + vp->size = xr_interface->get_render_targetsize(); RSG::storage->render_target_set_size(vp->render_target, vp->size.x, vp->size.y); // render mono or left eye first - ARVRInterface::Eyes leftOrMono = arvr_interface->is_stereo() ? ARVRInterface::EYE_LEFT : ARVRInterface::EYE_MONO; + XRInterface::Eyes leftOrMono = xr_interface->is_stereo() ? XRInterface::EYE_LEFT : XRInterface::EYE_MONO; // check for an external texture destination for our left eye/mono - RSG::storage->render_target_set_external_texture(vp->render_target, arvr_interface->get_external_texture_for_eye(leftOrMono)); + // TODO investigate how we're going to make external textures work + RSG::storage->render_target_set_external_texture(vp->render_target, xr_interface->get_external_texture_for_eye(leftOrMono)); // set our render target as current RSG::rasterizer->set_current_render_target(vp->render_target); // and draw left eye/mono _draw_viewport(vp, leftOrMono); - arvr_interface->commit_for_eye(leftOrMono, vp->render_target, vp->viewport_to_screen_rect); + xr_interface->commit_for_eye(leftOrMono, vp->render_target, vp->viewport_to_screen_rect); // render right eye - if (leftOrMono == ARVRInterface::EYE_LEFT) { + if (leftOrMono == XRInterface::EYE_LEFT) { // check for an external texture destination for our right eye - RSG::storage->render_target_set_external_texture(vp->render_target, arvr_interface->get_external_texture_for_eye(ARVRInterface::EYE_RIGHT)); + RSG::storage->render_target_set_external_texture(vp->render_target, xr_interface->get_external_texture_for_eye(XRInterface::EYE_RIGHT)); // commit for eye may have changed the render target RSG::rasterizer->set_current_render_target(vp->render_target); - _draw_viewport(vp, ARVRInterface::EYE_RIGHT); - arvr_interface->commit_for_eye(ARVRInterface::EYE_RIGHT, vp->render_target, vp->viewport_to_screen_rect); + _draw_viewport(vp, XRInterface::EYE_RIGHT); + xr_interface->commit_for_eye(XRInterface::EYE_RIGHT, vp->render_target, vp->viewport_to_screen_rect); } // and for our frame timing, mark when we've finished committing our eyes - ARVRServer::get_singleton()->_mark_commit(); + XRServer::get_singleton()->_mark_commit(); } else { #endif { @@ -470,11 +471,11 @@ RID RenderingServerViewport::viewport_create() { return rid; } -void RenderingServerViewport::viewport_set_use_arvr(RID p_viewport, bool p_use_arvr) { +void RenderingServerViewport::viewport_set_use_xr(RID p_viewport, bool p_use_xr) { Viewport *viewport = viewport_owner.getornull(p_viewport); ERR_FAIL_COND(!viewport); - viewport->use_arvr = p_use_arvr; + viewport->use_xr = p_use_xr; } void RenderingServerViewport::viewport_set_size(RID p_viewport, int p_width, int p_height) { diff --git a/servers/rendering/rendering_server_viewport.h b/servers/rendering/rendering_server_viewport.h index f574c58d96..71d8408ed1 100644 --- a/servers/rendering/rendering_server_viewport.h +++ b/servers/rendering/rendering_server_viewport.h @@ -34,8 +34,8 @@ #include "core/rid_owner.h" #include "core/self_list.h" #include "rasterizer.h" -#include "servers/arvr/arvr_interface.h" #include "servers/rendering_server.h" +#include "servers/xr/xr_interface.h" class RenderingServerViewport { public: @@ -47,7 +47,7 @@ public: RID self; RID parent; - bool use_arvr; /* use arvr interface to override camera positioning and projection matrices and control output */ + bool use_xr; /* use xr interface to override camera positioning and projection matrices and control output */ Size2i size; RID camera; @@ -127,7 +127,7 @@ public: for (int i = 0; i < RS::VIEWPORT_RENDER_INFO_MAX; i++) { render_info[i] = 0; } - use_arvr = false; + use_xr = false; } }; @@ -152,13 +152,13 @@ public: Vector active_viewports; private: - void _draw_3d(Viewport *p_viewport, ARVRInterface::Eyes p_eye); - void _draw_viewport(Viewport *p_viewport, ARVRInterface::Eyes p_eye = ARVRInterface::EYE_MONO); + void _draw_3d(Viewport *p_viewport, XRInterface::Eyes p_eye); + void _draw_viewport(Viewport *p_viewport, XRInterface::Eyes p_eye = XRInterface::EYE_MONO); public: RID viewport_create(); - void viewport_set_use_arvr(RID p_viewport, bool p_use_arvr); + void viewport_set_use_xr(RID p_viewport, bool p_use_xr); void viewport_set_size(RID p_viewport, int p_width, int p_height); diff --git a/servers/rendering/rendering_server_wrap_mt.h b/servers/rendering/rendering_server_wrap_mt.h index a3077980ce..9a98841b2c 100644 --- a/servers/rendering/rendering_server_wrap_mt.h +++ b/servers/rendering/rendering_server_wrap_mt.h @@ -370,7 +370,7 @@ public: FUNCRID(viewport) - FUNC2(viewport_set_use_arvr, RID, bool) + FUNC2(viewport_set_use_xr, RID, bool) FUNC3(viewport_set_size, RID, int, int) diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index d492586ce4..0a1b7b98e4 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -1766,7 +1766,7 @@ void RenderingServer::_bind_methods() { ClassDB::bind_method(D_METHOD("camera_set_use_vertical_aspect", "camera", "enable"), &RenderingServer::camera_set_use_vertical_aspect); ClassDB::bind_method(D_METHOD("viewport_create"), &RenderingServer::viewport_create); - ClassDB::bind_method(D_METHOD("viewport_set_use_arvr", "viewport", "use_arvr"), &RenderingServer::viewport_set_use_arvr); + ClassDB::bind_method(D_METHOD("viewport_set_use_xr", "viewport", "use_xr"), &RenderingServer::viewport_set_use_xr); ClassDB::bind_method(D_METHOD("viewport_set_size", "viewport", "width", "height"), &RenderingServer::viewport_set_size); ClassDB::bind_method(D_METHOD("viewport_set_active", "viewport", "active"), &RenderingServer::viewport_set_active); ClassDB::bind_method(D_METHOD("viewport_set_parent_viewport", "viewport", "parent_viewport"), &RenderingServer::viewport_set_parent_viewport); diff --git a/servers/rendering_server.h b/servers/rendering_server.h index ddae78cb1f..cb7c2cecf6 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -580,7 +580,7 @@ public: virtual RID viewport_create() = 0; - virtual void viewport_set_use_arvr(RID p_viewport, bool p_use_arvr) = 0; + virtual void viewport_set_use_xr(RID p_viewport, bool p_use_xr) = 0; virtual void viewport_set_size(RID p_viewport, int p_width, int p_height) = 0; virtual void viewport_set_active(RID p_viewport, bool p_active) = 0; virtual void viewport_set_parent_viewport(RID p_viewport, RID p_parent_viewport) = 0; diff --git a/servers/xr/SCsub b/servers/xr/SCsub new file mode 100644 index 0000000000..86681f9c74 --- /dev/null +++ b/servers/xr/SCsub @@ -0,0 +1,5 @@ +#!/usr/bin/env python + +Import("env") + +env.add_source_files(env.servers_sources, "*.cpp") diff --git a/servers/xr/xr_interface.cpp b/servers/xr/xr_interface.cpp new file mode 100644 index 0000000000..c1233ae810 --- /dev/null +++ b/servers/xr/xr_interface.cpp @@ -0,0 +1,145 @@ +/*************************************************************************/ +/* xr_interface.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.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 "xr_interface.h" + +void XRInterface::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_name"), &XRInterface::get_name); + ClassDB::bind_method(D_METHOD("get_capabilities"), &XRInterface::get_capabilities); + + ClassDB::bind_method(D_METHOD("is_primary"), &XRInterface::is_primary); + ClassDB::bind_method(D_METHOD("set_is_primary", "enable"), &XRInterface::set_is_primary); + + ClassDB::bind_method(D_METHOD("is_initialized"), &XRInterface::is_initialized); + ClassDB::bind_method(D_METHOD("set_is_initialized", "initialized"), &XRInterface::set_is_initialized); + ClassDB::bind_method(D_METHOD("initialize"), &XRInterface::initialize); + ClassDB::bind_method(D_METHOD("uninitialize"), &XRInterface::uninitialize); + + ClassDB::bind_method(D_METHOD("get_tracking_status"), &XRInterface::get_tracking_status); + + ClassDB::bind_method(D_METHOD("get_render_targetsize"), &XRInterface::get_render_targetsize); + ClassDB::bind_method(D_METHOD("is_stereo"), &XRInterface::is_stereo); + + ADD_GROUP("Interface", "interface_"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "interface_is_primary"), "set_is_primary", "is_primary"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "interface_is_initialized"), "set_is_initialized", "is_initialized"); + + // we don't have any properties specific to VR yet.... + + // but we do have properties specific to AR.... + ClassDB::bind_method(D_METHOD("get_anchor_detection_is_enabled"), &XRInterface::get_anchor_detection_is_enabled); + ClassDB::bind_method(D_METHOD("set_anchor_detection_is_enabled", "enable"), &XRInterface::set_anchor_detection_is_enabled); + ClassDB::bind_method(D_METHOD("get_camera_feed_id"), &XRInterface::get_camera_feed_id); + + ADD_GROUP("AR", "ar_"); + ADD_PROPERTY(PropertyInfo(Variant::BOOL, "ar_is_anchor_detection_enabled"), "set_anchor_detection_is_enabled", "get_anchor_detection_is_enabled"); + + BIND_ENUM_CONSTANT(XR_NONE); + BIND_ENUM_CONSTANT(XR_MONO); + BIND_ENUM_CONSTANT(XR_STEREO); + BIND_ENUM_CONSTANT(XR_AR); + BIND_ENUM_CONSTANT(XR_EXTERNAL); + + BIND_ENUM_CONSTANT(EYE_MONO); + BIND_ENUM_CONSTANT(EYE_LEFT); + BIND_ENUM_CONSTANT(EYE_RIGHT); + + BIND_ENUM_CONSTANT(XR_NORMAL_TRACKING); + BIND_ENUM_CONSTANT(XR_EXCESSIVE_MOTION); + BIND_ENUM_CONSTANT(XR_INSUFFICIENT_FEATURES); + BIND_ENUM_CONSTANT(XR_UNKNOWN_TRACKING); + BIND_ENUM_CONSTANT(XR_NOT_TRACKING); +}; + +StringName XRInterface::get_name() const { + return "Unknown"; +}; + +bool XRInterface::is_primary() { + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL_V(xr_server, false); + + return xr_server->get_primary_interface() == this; +}; + +void XRInterface::set_is_primary(bool p_is_primary) { + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL(xr_server); + + if (p_is_primary) { + ERR_FAIL_COND(!is_initialized()); + + xr_server->set_primary_interface(this); + } else { + xr_server->clear_primary_interface_if(this); + }; +}; + +void XRInterface::set_is_initialized(bool p_initialized) { + if (p_initialized) { + if (!is_initialized()) { + initialize(); + }; + } else { + if (is_initialized()) { + uninitialize(); + }; + }; +}; + +XRInterface::Tracking_status XRInterface::get_tracking_status() const { + return tracking_state; +}; + +XRInterface::XRInterface() { + tracking_state = XR_UNKNOWN_TRACKING; +}; + +XRInterface::~XRInterface(){}; + +// optional render to external texture which enhances performance on those platforms that require us to submit our end result into special textures. +unsigned int XRInterface::get_external_texture_for_eye(XRInterface::Eyes p_eye) { + return 0; +}; + +/** these will only be implemented on AR interfaces, so we want dummies for VR **/ +bool XRInterface::get_anchor_detection_is_enabled() const { + return false; +}; + +void XRInterface::set_anchor_detection_is_enabled(bool p_enable){ + // don't do anything here, this needs to be implemented on AR interface to enable/disable things like plane detection etc. +}; + +int XRInterface::get_camera_feed_id() { + // don't do anything here, this needs to be implemented on AR interface to enable/disable things like plane detection etc. + + return 0; +}; diff --git a/servers/xr/xr_interface.h b/servers/xr/xr_interface.h new file mode 100644 index 0000000000..99fcef7925 --- /dev/null +++ b/servers/xr/xr_interface.h @@ -0,0 +1,126 @@ +/*************************************************************************/ +/* xr_interface.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.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. */ +/*************************************************************************/ + +#ifndef XR_INTERFACE_H +#define XR_INTERFACE_H + +#include "core/math/camera_matrix.h" +#include "core/os/thread_safe.h" +#include "scene/main/window.h" +#include "servers/xr_server.h" + +/** + @author Bastiaan Olij + + The XR interface is a template class ontop of which we build interface to different AR, VR and tracking SDKs. + The idea is that we subclass this class, implement the logic, and then instantiate a singleton of each interface + when Godot starts. These instances do not initialize themselves but register themselves with the AR/VR server. + + If the user wants to enable AR/VR the choose the interface they want to use and initialize it. + + Note that we may make this into a fully instantiable class for GDNative support. +*/ + +class XRInterface : public Reference { + GDCLASS(XRInterface, Reference); + +public: + enum Capabilities { /* purely meta data, provides some info about what this interface supports */ + XR_NONE = 0, /* no capabilities */ + XR_MONO = 1, /* can be used with mono output */ + XR_STEREO = 2, /* can be used with stereo output */ + XR_AR = 4, /* offers a camera feed for AR */ + XR_EXTERNAL = 8 /* renders to external device */ + }; + + enum Eyes { + EYE_MONO, /* my son says we should call this EYE_CYCLOPS */ + EYE_LEFT, + EYE_RIGHT + }; + + enum Tracking_status { /* tracking status currently based on AR but we can start doing more with this for VR as well */ + XR_NORMAL_TRACKING, + XR_EXCESSIVE_MOTION, + XR_INSUFFICIENT_FEATURES, + XR_UNKNOWN_TRACKING, + XR_NOT_TRACKING + }; + +protected: + _THREAD_SAFE_CLASS_ + + Tracking_status tracking_state; + static void _bind_methods(); + +public: + /** general interface information **/ + virtual StringName get_name() const; + virtual int get_capabilities() const = 0; + + bool is_primary(); + void set_is_primary(bool p_is_primary); + + virtual bool is_initialized() const = 0; /* returns true if we've initialized this interface */ + void set_is_initialized(bool p_initialized); /* helper function, will call initialize or uninitialize */ + virtual bool initialize() = 0; /* initialize this interface, if this has an HMD it becomes the primary interface */ + virtual void uninitialize() = 0; /* deinitialize this interface */ + + Tracking_status get_tracking_status() const; /* get the status of our current tracking */ + + /** specific to VR **/ + // nothing yet + + /** specific to AR **/ + virtual bool get_anchor_detection_is_enabled() const; + virtual void set_anchor_detection_is_enabled(bool p_enable); + virtual int get_camera_feed_id(); + + /** rendering and internal **/ + + virtual Size2 get_render_targetsize() = 0; /* returns the recommended render target size per eye for this device */ + virtual bool is_stereo() = 0; /* returns true if this interface requires stereo rendering (for VR HMDs) or mono rendering (for mobile AR) */ + virtual Transform get_transform_for_eye(XRInterface::Eyes p_eye, const Transform &p_cam_transform) = 0; /* get each eyes camera transform, also implement EYE_MONO */ + virtual CameraMatrix get_projection_for_eye(XRInterface::Eyes p_eye, real_t p_aspect, real_t p_z_near, real_t p_z_far) = 0; /* get each eyes projection matrix */ + virtual unsigned int get_external_texture_for_eye(XRInterface::Eyes p_eye); /* if applicable return external texture to render to */ + virtual void commit_for_eye(XRInterface::Eyes p_eye, RID p_render_target, const Rect2 &p_screen_rect) = 0; /* output the left or right eye */ + + virtual void process() = 0; + virtual void notification(int p_what) = 0; + + XRInterface(); + ~XRInterface(); +}; + +VARIANT_ENUM_CAST(XRInterface::Capabilities); +VARIANT_ENUM_CAST(XRInterface::Eyes); +VARIANT_ENUM_CAST(XRInterface::Tracking_status); + +#endif diff --git a/servers/xr/xr_positional_tracker.cpp b/servers/xr/xr_positional_tracker.cpp new file mode 100644 index 0000000000..808b0a608f --- /dev/null +++ b/servers/xr/xr_positional_tracker.cpp @@ -0,0 +1,237 @@ +/*************************************************************************/ +/* xr_positional_tracker.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.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 "xr_positional_tracker.h" +#include "core/input/input_filter.h" + +void XRPositionalTracker::_bind_methods() { + BIND_ENUM_CONSTANT(TRACKER_HAND_UNKNOWN); + BIND_ENUM_CONSTANT(TRACKER_LEFT_HAND); + BIND_ENUM_CONSTANT(TRACKER_RIGHT_HAND); + + // this class is read only from GDScript, so we only have access to getters.. + ClassDB::bind_method(D_METHOD("get_type"), &XRPositionalTracker::get_type); + ClassDB::bind_method(D_METHOD("get_tracker_id"), &XRPositionalTracker::get_tracker_id); + ClassDB::bind_method(D_METHOD("get_name"), &XRPositionalTracker::get_name); + ClassDB::bind_method(D_METHOD("get_joy_id"), &XRPositionalTracker::get_joy_id); + ClassDB::bind_method(D_METHOD("get_tracks_orientation"), &XRPositionalTracker::get_tracks_orientation); + ClassDB::bind_method(D_METHOD("get_orientation"), &XRPositionalTracker::get_orientation); + ClassDB::bind_method(D_METHOD("get_tracks_position"), &XRPositionalTracker::get_tracks_position); + ClassDB::bind_method(D_METHOD("get_position"), &XRPositionalTracker::get_position); + ClassDB::bind_method(D_METHOD("get_hand"), &XRPositionalTracker::get_hand); + ClassDB::bind_method(D_METHOD("get_transform", "adjust_by_reference_frame"), &XRPositionalTracker::get_transform); + ClassDB::bind_method(D_METHOD("get_mesh"), &XRPositionalTracker::get_mesh); + + // these functions we don't want to expose to normal users but do need to be callable from GDNative + ClassDB::bind_method(D_METHOD("_set_type", "type"), &XRPositionalTracker::set_type); + ClassDB::bind_method(D_METHOD("_set_name", "name"), &XRPositionalTracker::set_name); + ClassDB::bind_method(D_METHOD("_set_joy_id", "joy_id"), &XRPositionalTracker::set_joy_id); + ClassDB::bind_method(D_METHOD("_set_orientation", "orientation"), &XRPositionalTracker::set_orientation); + ClassDB::bind_method(D_METHOD("_set_rw_position", "rw_position"), &XRPositionalTracker::set_rw_position); + ClassDB::bind_method(D_METHOD("_set_mesh", "mesh"), &XRPositionalTracker::set_mesh); + ClassDB::bind_method(D_METHOD("get_rumble"), &XRPositionalTracker::get_rumble); + ClassDB::bind_method(D_METHOD("set_rumble", "rumble"), &XRPositionalTracker::set_rumble); + + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "rumble"), "set_rumble", "get_rumble"); +}; + +void XRPositionalTracker::set_type(XRServer::TrackerType p_type) { + if (type != p_type) { + type = p_type; + hand = XRPositionalTracker::TRACKER_HAND_UNKNOWN; + + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL(xr_server); + + // get a tracker id for our type + // note if this is a controller this will be 3 or higher but we may change it later. + tracker_id = xr_server->get_free_tracker_id_for_type(p_type); + }; +}; + +XRServer::TrackerType XRPositionalTracker::get_type() const { + return type; +}; + +void XRPositionalTracker::set_name(const String &p_name) { + name = p_name; +}; + +StringName XRPositionalTracker::get_name() const { + return name; +}; + +int XRPositionalTracker::get_tracker_id() const { + return tracker_id; +}; + +void XRPositionalTracker::set_joy_id(int p_joy_id) { + joy_id = p_joy_id; +}; + +int XRPositionalTracker::get_joy_id() const { + return joy_id; +}; + +bool XRPositionalTracker::get_tracks_orientation() const { + return tracks_orientation; +}; + +void XRPositionalTracker::set_orientation(const Basis &p_orientation) { + _THREAD_SAFE_METHOD_ + + tracks_orientation = true; // obviously we have this + orientation = p_orientation; +}; + +Basis XRPositionalTracker::get_orientation() const { + _THREAD_SAFE_METHOD_ + + return orientation; +}; + +bool XRPositionalTracker::get_tracks_position() const { + return tracks_position; +}; + +void XRPositionalTracker::set_position(const Vector3 &p_position) { + _THREAD_SAFE_METHOD_ + + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL(xr_server); + real_t world_scale = xr_server->get_world_scale(); + ERR_FAIL_COND(world_scale == 0); + + tracks_position = true; // obviously we have this + rw_position = p_position / world_scale; +}; + +Vector3 XRPositionalTracker::get_position() const { + _THREAD_SAFE_METHOD_ + + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL_V(xr_server, rw_position); + real_t world_scale = xr_server->get_world_scale(); + + return rw_position * world_scale; +}; + +void XRPositionalTracker::set_rw_position(const Vector3 &p_rw_position) { + _THREAD_SAFE_METHOD_ + + tracks_position = true; // obviously we have this + rw_position = p_rw_position; +}; + +Vector3 XRPositionalTracker::get_rw_position() const { + _THREAD_SAFE_METHOD_ + + return rw_position; +}; + +void XRPositionalTracker::set_mesh(const Ref &p_mesh) { + _THREAD_SAFE_METHOD_ + + mesh = p_mesh; +}; + +Ref XRPositionalTracker::get_mesh() const { + _THREAD_SAFE_METHOD_ + + return mesh; +}; + +XRPositionalTracker::TrackerHand XRPositionalTracker::get_hand() const { + return hand; +}; + +void XRPositionalTracker::set_hand(const XRPositionalTracker::TrackerHand p_hand) { + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL(xr_server); + + if (hand != p_hand) { + // we can only set this if we've previously set this to be a controller!! + ERR_FAIL_COND((type != XRServer::TRACKER_CONTROLLER) && (p_hand != XRPositionalTracker::TRACKER_HAND_UNKNOWN)); + + hand = p_hand; + if (hand == XRPositionalTracker::TRACKER_LEFT_HAND) { + if (!xr_server->is_tracker_id_in_use_for_type(type, 1)) { + tracker_id = 1; + }; + } else if (hand == XRPositionalTracker::TRACKER_RIGHT_HAND) { + if (!xr_server->is_tracker_id_in_use_for_type(type, 2)) { + tracker_id = 2; + }; + }; + }; +}; + +Transform XRPositionalTracker::get_transform(bool p_adjust_by_reference_frame) const { + Transform new_transform; + + new_transform.basis = get_orientation(); + new_transform.origin = get_position(); + + if (p_adjust_by_reference_frame) { + XRServer *xr_server = XRServer::get_singleton(); + ERR_FAIL_NULL_V(xr_server, new_transform); + + new_transform = xr_server->get_reference_frame() * new_transform; + }; + + return new_transform; +}; + +real_t XRPositionalTracker::get_rumble() const { + return rumble; +}; + +void XRPositionalTracker::set_rumble(real_t p_rumble) { + if (p_rumble > 0.0) { + rumble = p_rumble; + } else { + rumble = 0.0; + }; +}; + +XRPositionalTracker::XRPositionalTracker() { + type = XRServer::TRACKER_UNKNOWN; + name = "Unknown"; + joy_id = -1; + tracker_id = 0; + tracks_orientation = false; + tracks_position = false; + hand = TRACKER_HAND_UNKNOWN; + rumble = 0.0; +}; + +XRPositionalTracker::~XRPositionalTracker(){ + +}; diff --git a/servers/xr/xr_positional_tracker.h b/servers/xr/xr_positional_tracker.h new file mode 100644 index 0000000000..d9d1f909e9 --- /dev/null +++ b/servers/xr/xr_positional_tracker.h @@ -0,0 +1,104 @@ +/*************************************************************************/ +/* xr_positional_tracker.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.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. */ +/*************************************************************************/ + +#ifndef XR_POSITIONAL_TRACKER_H +#define XR_POSITIONAL_TRACKER_H + +#include "core/os/thread_safe.h" +#include "scene/resources/mesh.h" +#include "servers/xr_server.h" + +/** + @author Bastiaan Olij + + The positional tracker object as an object that represents the position and orientation of a tracked object like a controller or headset. + An AR/VR Interface will registered the trackers it manages with our AR/VR server and update its position and orientation. + This is where potentially additional AR/VR interfaces may be active as there are AR/VR SDKs that solely deal with positional tracking. +*/ + +class XRPositionalTracker : public Object { + GDCLASS(XRPositionalTracker, Object); + _THREAD_SAFE_CLASS_ + +public: + enum TrackerHand { + TRACKER_HAND_UNKNOWN, /* unknown or not applicable */ + TRACKER_LEFT_HAND, /* controller is the left hand controller */ + TRACKER_RIGHT_HAND /* controller is the right hand controller */ + }; + +private: + XRServer::TrackerType type; // type of tracker + StringName name; // (unique) name of the tracker + int tracker_id; // tracker index id that is unique per type + int joy_id; // if we also have a related joystick entity, the id of the joystick + bool tracks_orientation; // do we track orientation? + Basis orientation; // our orientation + bool tracks_position; // do we track position? + Vector3 rw_position; // our position "in the real world, so without world_scale applied" + Ref mesh; // when available, a mesh that can be used to render this tracker + TrackerHand hand; // if known, the hand this tracker is held in + real_t rumble; // rumble strength, 0.0 is off, 1.0 is maximum, note that we only record here, xr_interface is responsible for execution + +protected: + static void _bind_methods(); + +public: + void set_type(XRServer::TrackerType p_type); + XRServer::TrackerType get_type() const; + void set_name(const String &p_name); + StringName get_name() const; + int get_tracker_id() const; + void set_joy_id(int p_joy_id); + int get_joy_id() const; + bool get_tracks_orientation() const; + void set_orientation(const Basis &p_orientation); + Basis get_orientation() const; + bool get_tracks_position() const; + void set_position(const Vector3 &p_position); // set position with world_scale applied + Vector3 get_position() const; // get position with world_scale applied + void set_rw_position(const Vector3 &p_rw_position); + Vector3 get_rw_position() const; + XRPositionalTracker::TrackerHand get_hand() const; + void set_hand(const XRPositionalTracker::TrackerHand p_hand); + real_t get_rumble() const; + void set_rumble(real_t p_rumble); + void set_mesh(const Ref &p_mesh); + Ref get_mesh() const; + + Transform get_transform(bool p_adjust_by_reference_frame) const; + + XRPositionalTracker(); + ~XRPositionalTracker(); +}; + +VARIANT_ENUM_CAST(XRPositionalTracker::TrackerHand); + +#endif diff --git a/servers/xr_server.cpp b/servers/xr_server.cpp new file mode 100644 index 0000000000..a93b99025f --- /dev/null +++ b/servers/xr_server.cpp @@ -0,0 +1,386 @@ +/*************************************************************************/ +/* xr_server.cpp */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.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 "xr_server.h" +#include "core/project_settings.h" +#include "xr/xr_interface.h" +#include "xr/xr_positional_tracker.h" + +XRServer *XRServer::singleton = nullptr; + +XRServer *XRServer::get_singleton() { + return singleton; +}; + +void XRServer::_bind_methods() { + ClassDB::bind_method(D_METHOD("get_world_scale"), &XRServer::get_world_scale); + ClassDB::bind_method(D_METHOD("set_world_scale"), &XRServer::set_world_scale); + ClassDB::bind_method(D_METHOD("get_reference_frame"), &XRServer::get_reference_frame); + ClassDB::bind_method(D_METHOD("center_on_hmd", "rotation_mode", "keep_height"), &XRServer::center_on_hmd); + ClassDB::bind_method(D_METHOD("get_hmd_transform"), &XRServer::get_hmd_transform); + + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "world_scale"), "set_world_scale", "get_world_scale"); + + ClassDB::bind_method(D_METHOD("get_interface_count"), &XRServer::get_interface_count); + ClassDB::bind_method(D_METHOD("get_interface", "idx"), &XRServer::get_interface); + ClassDB::bind_method(D_METHOD("get_interfaces"), &XRServer::get_interfaces); + ClassDB::bind_method(D_METHOD("find_interface", "name"), &XRServer::find_interface); + ClassDB::bind_method(D_METHOD("get_tracker_count"), &XRServer::get_tracker_count); + ClassDB::bind_method(D_METHOD("get_tracker", "idx"), &XRServer::get_tracker); + + ClassDB::bind_method(D_METHOD("get_primary_interface"), &XRServer::get_primary_interface); + ClassDB::bind_method(D_METHOD("set_primary_interface", "interface"), &XRServer::set_primary_interface); + + ADD_PROPERTY(PropertyInfo(Variant::OBJECT, "primary_interface"), "set_primary_interface", "get_primary_interface"); + + ClassDB::bind_method(D_METHOD("get_last_process_usec"), &XRServer::get_last_process_usec); + ClassDB::bind_method(D_METHOD("get_last_commit_usec"), &XRServer::get_last_commit_usec); + ClassDB::bind_method(D_METHOD("get_last_frame_usec"), &XRServer::get_last_frame_usec); + + BIND_ENUM_CONSTANT(TRACKER_CONTROLLER); + BIND_ENUM_CONSTANT(TRACKER_BASESTATION); + BIND_ENUM_CONSTANT(TRACKER_ANCHOR); + BIND_ENUM_CONSTANT(TRACKER_ANY_KNOWN); + BIND_ENUM_CONSTANT(TRACKER_UNKNOWN); + BIND_ENUM_CONSTANT(TRACKER_ANY); + + BIND_ENUM_CONSTANT(RESET_FULL_ROTATION); + BIND_ENUM_CONSTANT(RESET_BUT_KEEP_TILT); + BIND_ENUM_CONSTANT(DONT_RESET_ROTATION); + + ADD_SIGNAL(MethodInfo("interface_added", PropertyInfo(Variant::STRING_NAME, "interface_name"))); + ADD_SIGNAL(MethodInfo("interface_removed", PropertyInfo(Variant::STRING_NAME, "interface_name"))); + + ADD_SIGNAL(MethodInfo("tracker_added", PropertyInfo(Variant::STRING_NAME, "tracker_name"), PropertyInfo(Variant::INT, "type"), PropertyInfo(Variant::INT, "id"))); + ADD_SIGNAL(MethodInfo("tracker_removed", PropertyInfo(Variant::STRING_NAME, "tracker_name"), PropertyInfo(Variant::INT, "type"), PropertyInfo(Variant::INT, "id"))); +}; + +real_t XRServer::get_world_scale() const { + return world_scale; +}; + +void XRServer::set_world_scale(real_t p_world_scale) { + if (p_world_scale < 0.01) { + p_world_scale = 0.01; + } else if (p_world_scale > 1000.0) { + p_world_scale = 1000.0; + } + + world_scale = p_world_scale; +}; + +Transform XRServer::get_world_origin() const { + return world_origin; +}; + +void XRServer::set_world_origin(const Transform &p_world_origin) { + world_origin = p_world_origin; +}; + +Transform XRServer::get_reference_frame() const { + return reference_frame; +}; + +void XRServer::center_on_hmd(RotationMode p_rotation_mode, bool p_keep_height) { + if (primary_interface != nullptr) { + // clear our current reference frame or we'll end up double adjusting it + reference_frame = Transform(); + + // requesting our EYE_MONO transform should return our current HMD position + Transform new_reference_frame = primary_interface->get_transform_for_eye(XRInterface::EYE_MONO, Transform()); + + // remove our tilt + if (p_rotation_mode == 1) { + // take the Y out of our Z + new_reference_frame.basis.set_axis(2, Vector3(new_reference_frame.basis.elements[0][2], 0.0, new_reference_frame.basis.elements[2][2]).normalized()); + + // Y is straight up + new_reference_frame.basis.set_axis(1, Vector3(0.0, 1.0, 0.0)); + + // and X is our cross reference + new_reference_frame.basis.set_axis(0, new_reference_frame.basis.get_axis(1).cross(new_reference_frame.basis.get_axis(2)).normalized()); + } else if (p_rotation_mode == 2) { + // remove our rotation, we're only interesting in centering on position + new_reference_frame.basis = Basis(); + }; + + // don't negate our height + if (p_keep_height) { + new_reference_frame.origin.y = 0.0; + }; + + reference_frame = new_reference_frame.inverse(); + }; +}; + +Transform XRServer::get_hmd_transform() { + Transform hmd_transform; + if (primary_interface != nullptr) { + hmd_transform = primary_interface->get_transform_for_eye(XRInterface::EYE_MONO, hmd_transform); + }; + return hmd_transform; +}; + +void XRServer::add_interface(const Ref &p_interface) { + ERR_FAIL_COND(p_interface.is_null()); + + for (int i = 0; i < interfaces.size(); i++) { + + if (interfaces[i] == p_interface) { + ERR_PRINT("Interface was already added"); + return; + }; + }; + + interfaces.push_back(p_interface); + emit_signal("interface_added", p_interface->get_name()); +}; + +void XRServer::remove_interface(const Ref &p_interface) { + ERR_FAIL_COND(p_interface.is_null()); + + int idx = -1; + for (int i = 0; i < interfaces.size(); i++) { + + if (interfaces[i] == p_interface) { + + idx = i; + break; + }; + }; + + ERR_FAIL_COND(idx == -1); + + print_verbose("XR: Removed interface" + p_interface->get_name()); + + emit_signal("interface_removed", p_interface->get_name()); + interfaces.remove(idx); +}; + +int XRServer::get_interface_count() const { + return interfaces.size(); +}; + +Ref XRServer::get_interface(int p_index) const { + ERR_FAIL_INDEX_V(p_index, interfaces.size(), nullptr); + + return interfaces[p_index]; +}; + +Ref XRServer::find_interface(const String &p_name) const { + int idx = -1; + for (int i = 0; i < interfaces.size(); i++) { + + if (interfaces[i]->get_name() == p_name) { + + idx = i; + break; + }; + }; + + ERR_FAIL_COND_V(idx == -1, nullptr); + + return interfaces[idx]; +}; + +Array XRServer::get_interfaces() const { + Array ret; + + for (int i = 0; i < interfaces.size(); i++) { + Dictionary iface_info; + + iface_info["id"] = i; + iface_info["name"] = interfaces[i]->get_name(); + + ret.push_back(iface_info); + }; + + return ret; +}; + +/* + A little extra info on the tracker ids, these are unique per tracker type so we get some consistency in recognising our trackers, specifically controllers. + + The first controller that is turned of will get ID 1, the second will get ID 2, etc. + The magic happens when one of the controllers is turned off, say controller 1 turns off, controller 2 will remain controller 2, controller 3 will remain controller 3. + If controller number 1 is turned on again it again gets ID 1 unless another new controller was turned on since. + + The most likely scenario however is a controller that runs out of battery and another controller being used to replace it. + Because the controllers are often linked to physical objects, say you're holding a shield in controller 1, your left hand, and a gun in controller 2, your right hand, and controller 1 dies: + - using our tracker index would suddenly make the gun disappear and the shield jump into your right hand because controller 2 becomes controller 1. + - using this approach the shield disappears or is no longer tracked, but the gun stays firmly in your right hand because that is still controller 2, further more, if controller 1 is replaced the shield will return. +*/ + +bool XRServer::is_tracker_id_in_use_for_type(TrackerType p_tracker_type, int p_tracker_id) const { + for (int i = 0; i < trackers.size(); i++) { + if (trackers[i]->get_type() == p_tracker_type && trackers[i]->get_tracker_id() == p_tracker_id) { + return true; + }; + }; + + // all good + return false; +}; + +int XRServer::get_free_tracker_id_for_type(TrackerType p_tracker_type) { + // We start checking at 1, 0 means that it's not a controller.. + // Note that for controller we reserve: + // - 1 for the left hand controller and + // - 2 for the right hand controller + // so we start at 3 :) + int tracker_id = p_tracker_type == XRServer::TRACKER_CONTROLLER ? 3 : 1; + + while (is_tracker_id_in_use_for_type(p_tracker_type, tracker_id)) { + // try the next one + tracker_id++; + }; + + return tracker_id; +}; + +void XRServer::add_tracker(XRPositionalTracker *p_tracker) { + ERR_FAIL_NULL(p_tracker); + + trackers.push_back(p_tracker); + emit_signal("tracker_added", p_tracker->get_name(), p_tracker->get_type(), p_tracker->get_tracker_id()); +}; + +void XRServer::remove_tracker(XRPositionalTracker *p_tracker) { + ERR_FAIL_NULL(p_tracker); + + int idx = -1; + for (int i = 0; i < trackers.size(); i++) { + + if (trackers[i] == p_tracker) { + + idx = i; + break; + }; + }; + + ERR_FAIL_COND(idx == -1); + + emit_signal("tracker_removed", p_tracker->get_name(), p_tracker->get_type(), p_tracker->get_tracker_id()); + trackers.remove(idx); +}; + +int XRServer::get_tracker_count() const { + return trackers.size(); +}; + +XRPositionalTracker *XRServer::get_tracker(int p_index) const { + ERR_FAIL_INDEX_V(p_index, trackers.size(), nullptr); + + return trackers[p_index]; +}; + +XRPositionalTracker *XRServer::find_by_type_and_id(TrackerType p_tracker_type, int p_tracker_id) const { + ERR_FAIL_COND_V(p_tracker_id == 0, nullptr); + + for (int i = 0; i < trackers.size(); i++) { + if (trackers[i]->get_type() == p_tracker_type && trackers[i]->get_tracker_id() == p_tracker_id) { + return trackers[i]; + }; + }; + + return nullptr; +}; + +Ref XRServer::get_primary_interface() const { + return primary_interface; +}; + +void XRServer::set_primary_interface(const Ref &p_primary_interface) { + primary_interface = p_primary_interface; + + print_verbose("XR: Primary interface set to: " + primary_interface->get_name()); +}; + +void XRServer::clear_primary_interface_if(const Ref &p_primary_interface) { + if (primary_interface == p_primary_interface) { + print_verbose("XR: Clearing primary interface"); + primary_interface.unref(); + }; +}; + +uint64_t XRServer::get_last_process_usec() { + return last_process_usec; +}; + +uint64_t XRServer::get_last_commit_usec() { + return last_commit_usec; +}; + +uint64_t XRServer::get_last_frame_usec() { + return last_frame_usec; +}; + +void XRServer::_process() { + /* called from rendering_server_viewport.draw_viewports right before we start drawing our viewports */ + + /* mark for our frame timing */ + last_process_usec = OS::get_singleton()->get_ticks_usec(); + + /* process all active interfaces */ + for (int i = 0; i < interfaces.size(); i++) { + if (!interfaces[i].is_valid()) { + // ignore, not a valid reference + } else if (interfaces[i]->is_initialized()) { + interfaces.write[i]->process(); + }; + }; +}; + +void XRServer::_mark_commit() { + /* time this */ + last_commit_usec = OS::get_singleton()->get_ticks_usec(); + + /* now store our difference as we may overwrite last_process_usec before this is accessed */ + last_frame_usec = last_commit_usec - last_process_usec; +}; + +XRServer::XRServer() { + singleton = this; + world_scale = 1.0; +}; + +XRServer::~XRServer() { + primary_interface.unref(); + + while (interfaces.size() > 0) { + interfaces.remove(0); + } + + while (trackers.size() > 0) { + trackers.remove(0); + } + + singleton = nullptr; +}; diff --git a/servers/xr_server.h b/servers/xr_server.h new file mode 100644 index 0000000000..e04c7b3592 --- /dev/null +++ b/servers/xr_server.h @@ -0,0 +1,192 @@ +/*************************************************************************/ +/* xr_server.h */ +/*************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/*************************************************************************/ +/* Copyright (c) 2007-2020 Juan Linietsky, Ariel Manzur. */ +/* Copyright (c) 2014-2020 Godot Engine contributors (cf. AUTHORS.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. */ +/*************************************************************************/ + +#ifndef XR_SERVER_H +#define XR_SERVER_H + +#include "core/os/os.h" +#include "core/os/thread_safe.h" +#include "core/reference.h" +#include "core/rid.h" +#include "core/variant.h" + +class XRInterface; +class XRPositionalTracker; + +/** + @author Bastiaan Olij + + The XR server is a singleton object that gives access to the various + objects and SDKs that are available on the system. + Because there can be multiple SDKs active this is exposed as an array + and our XR server object acts as a pass through + Also each positioning tracker is accessible from here. + + I've added some additional info into this header file that should move + into the documentation, I will do so when we're close to accepting this PR + or as a separate PR once this has been merged into the master branch. +**/ + +class XRServer : public Object { + GDCLASS(XRServer, Object); + _THREAD_SAFE_CLASS_ + +public: + enum TrackerType { + TRACKER_CONTROLLER = 0x01, /* tracks a controller */ + TRACKER_BASESTATION = 0x02, /* tracks location of a base station */ + TRACKER_ANCHOR = 0x04, /* tracks an anchor point, used in AR to track a real live location */ + TRACKER_UNKNOWN = 0x80, /* unknown tracker */ + + TRACKER_ANY_KNOWN = 0x7f, /* all except unknown */ + TRACKER_ANY = 0xff /* used by get_connected_trackers to return all types */ + }; + + enum RotationMode { + RESET_FULL_ROTATION = 0, /* we reset the full rotation, regardless of how the HMD is oriented, we're looking dead ahead */ + RESET_BUT_KEEP_TILT = 1, /* reset rotation but keep tilt. */ + DONT_RESET_ROTATION = 2, /* don't reset the rotation, we will only center on position */ + }; + +private: + Vector> interfaces; + Vector trackers; + + Ref primary_interface; /* we'll identify one interface as primary, this will be used by our viewports */ + + real_t world_scale; /* scale by which we multiply our tracker positions */ + Transform world_origin; /* our world origin point, maps a location in our virtual world to the origin point in our real world tracking volume */ + Transform reference_frame; /* our reference frame */ + + uint64_t last_process_usec; /* for frame timing, usec when we did our processing */ + uint64_t last_commit_usec; /* for frame timing, usec when we finished committing both eyes */ + uint64_t last_frame_usec; /* time it took between process and committing, we should probably average this over the last x frames */ + +protected: + static XRServer *singleton; + + static void _bind_methods(); + +public: + static XRServer *get_singleton(); + + /* + World scale allows you to specify a scale factor that is applied to all positioning vectors in our VR world in essence scaling up, or scaling down the world. + For stereoscopic rendering specifically this is very important to give an accurate sense of scale. + Add controllers into the mix and an accurate mapping of real world movement to perceived virtual movement becomes very important. + + Most VR platforms, and our assumption, is that 1 unit in our virtual world equates to 1 meter in the real mode. + This scale basically effects the unit size relationship to real world size. + + I may remove access to this property in GDScript in favour of exposing it on the XROrigin3D node + */ + real_t get_world_scale() const; + void set_world_scale(real_t p_world_scale); + + /* + The world maps the 0,0,0 coordinate of our real world coordinate system for our tracking volume to a location in our + virtual world. It is this origin point that should be moved when the player is moved through the world by controller + actions be it straffing, teleporting, etc. Movement of the player by moving through the physical space is always tracked + in relation to this point. + + Note that the XROrigin3D spatial node in your scene automatically updates this property and it should be used instead of + direct access to this property and it therefore is not available in GDScript + + Note: this should not be used in AR and should be ignored by an AR based interface as it would throw what you're looking at in the real world + and in the virtual world out of sync + */ + Transform get_world_origin() const; + void set_world_origin(const Transform &p_world_origin); + + /* + center_on_hmd calculates a new reference frame. This ensures the HMD is positioned to 0,0,0 facing 0,0,-1 (need to verify this direction) + in the virtual world. + + You can ignore the tilt of the device ensuring you're looking straight forward even if the player is looking down or sideways. + You can chose to keep the height the tracking provides which is important for room scale capable tracking. + + Note: this should not be used in AR and should be ignored by an AR based interface as it would throw what you're looking at in the real world + and in the virtual world out of sync + */ + Transform get_reference_frame() const; + void center_on_hmd(RotationMode p_rotation_mode, bool p_keep_height); + + /* + get_hmd_transform gets our hmd transform (centered between eyes) with most up to date tracking, relative to the origin + */ + Transform get_hmd_transform(); + + /* + Interfaces are objects that 'glue' Godot to an AR or VR SDK such as the Oculus SDK, OpenVR, OpenHMD, etc. + */ + void add_interface(const Ref &p_interface); + void remove_interface(const Ref &p_interface); + int get_interface_count() const; + Ref get_interface(int p_index) const; + Ref find_interface(const String &p_name) const; + Array get_interfaces() const; + + /* + note, more then one interface can technically be active, especially on mobile, but only one interface is used for + rendering. This interface identifies itself by calling set_primary_interface when it is initialized + */ + Ref get_primary_interface() const; + void set_primary_interface(const Ref &p_primary_interface); + void clear_primary_interface_if(const Ref &p_primary_interface); /* this is automatically called if an interface destructs */ + + /* + Our trackers are objects that expose the orientation and position of physical devices such as controller, anchor points, etc. + They are created and managed by our active AR/VR interfaces. + */ + bool is_tracker_id_in_use_for_type(TrackerType p_tracker_type, int p_tracker_id) const; + int get_free_tracker_id_for_type(TrackerType p_tracker_type); + void add_tracker(XRPositionalTracker *p_tracker); + void remove_tracker(XRPositionalTracker *p_tracker); + int get_tracker_count() const; + XRPositionalTracker *get_tracker(int p_index) const; + XRPositionalTracker *find_by_type_and_id(TrackerType p_tracker_type, int p_tracker_id) const; + + uint64_t get_last_process_usec(); + uint64_t get_last_commit_usec(); + uint64_t get_last_frame_usec(); + + void _process(); + void _mark_commit(); + + XRServer(); + ~XRServer(); +}; + +#define XR XRServer + +VARIANT_ENUM_CAST(XRServer::TrackerType); +VARIANT_ENUM_CAST(XRServer::RotationMode); + +#endif -- cgit v1.2.3