Skip to content

Commit

Permalink
Add Tangible Tracking Camera support
Browse files Browse the repository at this point in the history
Added enabling the tangible camera and reading frames and pose
Added the T5ImageCapture node to interface with the camera
  • Loading branch information
patrickdown committed May 19, 2024
1 parent a0207ea commit 03c44fc
Show file tree
Hide file tree
Showing 15 changed files with 483 additions and 8 deletions.
5 changes: 5 additions & 0 deletions example.gd/addons/tiltfive/scenes/T5XRRig.gd
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ var _gameboard_size := AABB()
var _origin : T5Origin3D
var _camera : T5Camera3D
var _wand : T5Controller3D
var _image_capture : T5ImageCapture

## Get the ID attached to a pair of Tilt Five glasses
func get_glasses_id() -> StringName:
Expand All @@ -32,10 +33,14 @@ func get_camera() -> T5Camera3D:
func get_wand() -> T5Controller3D:
return _wand

func get_image_capture() -> T5ImageCapture:
return _image_capture

func _enter_tree():
_origin = $Origin
_camera = $Origin/Camera
_wand = $Origin/Wand_1
_image_capture = $Origin.get_node("T5ImageCapture")

func _process(_delta):
if _wand: _wand.visible = _wand.get_has_tracking_data()
43 changes: 39 additions & 4 deletions example.gd/main.gd
Original file line number Diff line number Diff line change
@@ -1,7 +1,42 @@
extends Node3D

func _on_t5_manager_glasses_scene_was_added(glasses):
print("Scene ", glasses.name, " added")
var image_capture : T5ImageCapture
var is_capturing := false
var image_size : Vector2i
var camera_image : Image
@onready var texture_rect : TextureRect = $ScreenUI/CameraView

func _on_t5_manager_glasses_scene_will_be_removed(glasses):
print("Scene ", glasses.name, " removed")
func _on_t_5_manager_xr_rig_was_added(xr_rig : T5XRRig):
image_capture = xr_rig.get_image_capture()

func _on_t_5_manager_xr_rig_will_be_removed(_xr_rig):
image_capture = null

func _input(event):
if image_capture == null:
return
if not event.is_action_pressed("toggle_camera"):
return
if not is_capturing and image_capture.start_capture():
is_capturing = true
texture_rect.visible = true
else:
image_capture.stop_capture()
is_capturing = false
texture_rect.visible = false

func _process(_delta):
if not is_capturing:
return
if image_capture.acquire_buffer():
var buffer := image_capture.get_image_data()
var new_size = image_capture.get_image_size()
var new_illumination_mode = image_capture.get_frame_illumination_mode()
if camera_image == null or image_size != new_size:
image_size = new_size
camera_image = Image.create_from_data(image_size.x, image_size.y, false, Image.FORMAT_R8, buffer)
texture_rect.texture = ImageTexture.create_from_image(camera_image)
else:
camera_image.set_data(image_size.x, image_size.y, false, Image.FORMAT_R8, buffer)
texture_rect.texture.update(camera_image)
image_capture.release_buffer()
36 changes: 35 additions & 1 deletion example.gd/main.tscn
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
[gd_scene load_steps=17 format=3 uid="uid://ckbe6draoen0x"]
[gd_scene load_steps=18 format=3 uid="uid://ckbe6draoen0x"]

[ext_resource type="Script" path="res://main.gd" id="1_xvgge"]
[ext_resource type="Script" path="res://addons/tiltfive/T5Manager.gd" id="2_dibvp"]
Expand Down Expand Up @@ -34,6 +34,8 @@ albedo_color = Color(0.862745, 0, 0.0235294, 1)
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_qrhlq"]
albedo_color = Color(0.741176, 0, 0.686275, 1)

[sub_resource type="ImageTexture" id="ImageTexture_0jitc"]

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

Expand Down Expand Up @@ -119,3 +121,35 @@ surface_material_override/0 = SubResource("StandardMaterial3D_qrhlq")
[node name="SpectatorCam" type="Camera3D" parent="."]
transform = Transform3D(0.670983, -0.138786, 0.728368, 0, 0.982326, 0.187176, -0.741472, -0.125592, 0.659125, 14.0459, 4.9572, 12.9908)
cull_mask = 3

[node name="ScreenUI" type="Control" parent="."]
visibility_layer = 2
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2

[node name="CameraView" type="TextureRect" parent="ScreenUI"]
visible = false
visibility_layer = 2
layout_mode = 1
anchors_preset = 15
anchor_right = 1.0
anchor_bottom = 1.0
grow_horizontal = 2
grow_vertical = 2
texture = SubResource("ImageTexture_0jitc")

[node name="Label" type="Label" parent="ScreenUI"]
visibility_layer = 4
layout_mode = 1
offset_left = 17.0
offset_top = 14.0
offset_right = 197.0
offset_bottom = 37.0
text = "C - Toggle Camera View"

[connection signal="xr_rig_was_added" from="T5Manager" to="." method="_on_t_5_manager_xr_rig_was_added"]
[connection signal="xr_rig_will_be_removed" from="T5Manager" to="." method="_on_t_5_manager_xr_rig_will_be_removed"]
8 changes: 7 additions & 1 deletion example.gd/project.godot
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ config_version=5

config/name="T5Example.gd"
run/main_scene="res://main.tscn"
config/features=PackedStringArray("4.1")
config/features=PackedStringArray("4.2")
run/max_fps=60
config/icon="res://icon.png"

Expand All @@ -31,6 +31,11 @@ trigger={
"events": [Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":-1,"axis":5,"axis_value":1.0,"script":null)
]
}
toggle_camera={
"deadzone": 0.5,
"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":67,"key_label":0,"unicode":99,"echo":false,"script":null)
]
}

[layer_names]

Expand All @@ -41,3 +46,4 @@ trigger={
[xr]

shaders/enabled=true
tilt_five/debug_logging=true
2 changes: 2 additions & 0 deletions example.gd/scenes/ExampleXRRig.tscn
Original file line number Diff line number Diff line change
Expand Up @@ -80,5 +80,7 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -2.77558, 0)
mesh = SubResource("BoxMesh_aaxuw")
surface_material_override/0 = SubResource("StandardMaterial3D_iuako")

[node name="T5ImageCapture" type="T5ImageCapture" parent="Origin" index="3"]

[connection signal="button_pressed" from="Origin/Wand_1" to="Origin/Wand_1" method="_on_button_pressed"]
[connection signal="button_released" from="Origin/Wand_1" to="Origin/Wand_1" method="_on_button_released"]
143 changes: 143 additions & 0 deletions extension/T5Integration/Camera.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
#include <Camera.h>
#include <Logging.h>
#include <iostream>

namespace T5Integration {

extern std::mutex g_t5_exclusivity_group_1;

bool Camera::start_capture() {
LOG_FAIL_COND_V(_glasses.expired(), false);
_is_captured = configure_camera(true);

// Put the buffers into the queue for the first time.a
if (_is_captured) {
for (int i = 0; i < camera_buffer_count; ++i) {
release_filled_buffer(i);
}
}
return _is_captured;
}

void Camera::stop_capture() {
LOG_FAIL_COND(_glasses.expired());
configure_camera(false);
}

void Camera::release_filled_buffer(int buffer_index) {
LOG_FAIL_COND(_glasses.expired());
LOG_FAIL_COND(!_is_captured);
LOG_FAIL_COND(buffer_index < 0 || buffer_index >= camera_buffer_count);

auto& buffer = _camera_buffers[buffer_index];
auto& camImage = _camera_buffer_info[buffer_index];

camImage.cameraIndex = 0;
camImage.imageWidth = 0;
camImage.imageHeight = 0;
camImage.cameraIndex = 0;
camImage.imageStride = 0;
camImage.illuminationMode = 0;
camImage.bufferSize = buffer.size();
camImage.pixelData = buffer.data();

T5_Result result;
{
std::lock_guard lock(g_t5_exclusivity_group_1);
result = t5SubmitEmptyCamImageBuffer(_glasses.lock()->_glasses_handle, &camImage);
}
if (result != T5_SUCCESS) {
LOG_T5_ERROR(result);
}
}

int Camera::acquire_filled_buffer() {
LOG_FAIL_COND_V(_glasses.expired(), -1);
T5_CamImage camImage;
T5_Result result;
{
std::lock_guard lock(g_t5_exclusivity_group_1);
result = t5GetFilledCamImageBuffer(_glasses.lock()->_glasses_handle, &camImage);
}
if (result == T5_ERROR_TRY_AGAIN) {
return -1;
} else if (result != T5_SUCCESS) {
LOG_T5_ERROR(result);
return -1;
}
for (int i = 0; i < _camera_buffers.size(); ++i) {
if (_camera_buffers[i].data() == camImage.pixelData) {
_camera_buffer_info[i] = camImage;
return i;
}
}
LOG_ERROR("Failed to find buffer.");
return -1;
}

std::span<uint8_t> Camera::get_buffer(int buffer_index) {
LOG_FAIL_COND_V(buffer_index < 0 || buffer_index >= _camera_buffers.size(), std::span<uint8_t>());

return std::span(_camera_buffers[buffer_index]);
}

int Camera::get_image_width(int buffer_index) const {
LOG_FAIL_COND_V(buffer_index < 0 || buffer_index >= _camera_buffer_info.size(), 0);

return _camera_buffer_info[buffer_index].imageWidth;
}

int Camera::get_image_height(int buffer_index) const {
LOG_FAIL_COND_V(buffer_index < 0 || buffer_index >= _camera_buffer_info.size(), 0);

return _camera_buffer_info[buffer_index].imageHeight;
}

int Camera::get_image_stride(int buffer_index) const {
LOG_FAIL_COND_V(buffer_index < 0 || buffer_index >= _camera_buffer_info.size(), 0);

return _camera_buffer_info[buffer_index].imageStride;
}

uint8_t Camera::get_illumination_mode(int buffer_index) const {
LOG_FAIL_COND_V(buffer_index < 0 || buffer_index >= _camera_buffer_info.size(), false);

return _camera_buffer_info[buffer_index].illuminationMode;
}

void Camera::get_camera_position(int buffer_index, float& out_pos_x, float& out_pos_y, float& out_pos_z) const {
LOG_FAIL_COND(buffer_index < 0 || buffer_index >= _camera_buffer_info.size());

out_pos_x = _camera_buffer_info[buffer_index].posCAM_GBD.x;
out_pos_y = _camera_buffer_info[buffer_index].posCAM_GBD.y;
out_pos_z = _camera_buffer_info[buffer_index].posCAM_GBD.z;
}

void Camera::get_camera_orientation(int buffer_index, float& out_quat_x, float& out_quat_y, float& out_quat_z, float& out_quat_w) const {
LOG_FAIL_COND(buffer_index < 0 || buffer_index >= _camera_buffer_info.size());

out_quat_x = _camera_buffer_info[buffer_index].rotToCAM_GBD.x;
out_quat_y = _camera_buffer_info[buffer_index].rotToCAM_GBD.y;
out_quat_z = _camera_buffer_info[buffer_index].rotToCAM_GBD.z;
out_quat_w = _camera_buffer_info[buffer_index].rotToCAM_GBD.w;
}

bool Camera::configure_camera(bool enable) {
LOG_FAIL_COND_V(_glasses.expired(), false);
LOG_FAIL_COND_V(_camera_buffers.size() <= 0, false);

T5_CameraStreamConfig config{ _camera_idx, enable };
T5_Result result = T5_SUCCESS;
{
std::lock_guard lock(g_t5_exclusivity_group_1);
result = t5ConfigureCameraStreamForGlasses(_glasses.lock()->_glasses_handle, config);
}

if (result != T5_SUCCESS) {
LOG_T5_ERROR(result);
return false;
}
return true;
}

} //namespace T5Integration
67 changes: 67 additions & 0 deletions extension/T5Integration/Camera.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#ifndef _WANDSERVICE_H
#define _WANDSERVICE_H

#include <TiltFiveNative.h>
#include <array>
#include <chrono>
#include <mutex>
#include <span>
#include <thread>
#include <vector>

#include <Glasses.h>

using namespace std::chrono_literals;

namespace T5Integration {

const int camera_buffer_count = 3;
const size_t camera_buffer_size = T5_MIN_CAM_IMAGE_BUFFER_WIDTH * T5_MIN_CAM_IMAGE_BUFFER_HEIGHT;

using CameraBuffer = std::array<uint8_t, camera_buffer_size>;

class Camera {
public:
void set_camera_idx(uint8_t idx);
void set_glasses(Glasses::Ptr glasses);

bool start_capture();
void stop_capture();
bool is_capturing() const;

void release_filled_buffer(int buffer_index);
int acquire_filled_buffer();
std::span<uint8_t> get_buffer(int buffer_index);
int get_image_width(int buffer_index) const;
int get_image_height(int buffer_index) const;
int get_image_stride(int buffer_index) const;
uint8_t get_illumination_mode(int buffer_index) const;
void get_camera_position(int buffer_index, float& out_pos_x, float& out_pos_y, float& out_pos_z) const;
void get_camera_orientation(int buffer_index, float& out_quat_x, float& out_quat_y, float& out_quat_z, float& out_quat_w) const;

private:
bool configure_camera(bool enable);

bool _is_captured = false;
uint8_t _camera_idx = 0;
std::weak_ptr<Glasses> _glasses;

std::array<CameraBuffer, camera_buffer_count> _camera_buffers;
std::array<T5_CamImage, camera_buffer_count> _camera_buffer_info;
};

inline void Camera::set_camera_idx(uint8_t idx) {
_camera_idx = idx;
}

inline bool Camera::is_capturing() const {
return _is_captured;
}

inline void Camera::set_glasses(Glasses::Ptr glasses) {
_glasses = glasses;
}

} //namespace T5Integration

#endif //_WANDSERVICE_H
2 changes: 2 additions & 0 deletions extension/T5Integration/Glasses.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ namespace T5Integration {
using namespace std::chrono_literals;
using GlassesFlags = StateFlags<uint16_t>;
class T5Service;
class Camera;
using TaskSystem::CotaskPtr;
using TaskSystem::Scheduler;

Expand Down Expand Up @@ -58,6 +59,7 @@ struct GlassesEvent {

class Glasses {
friend T5Service;
friend Camera;

protected:
struct SwapChainFrame {
Expand Down
Loading

0 comments on commit 03c44fc

Please sign in to comment.