329 lines
9.6 KiB
C++
329 lines
9.6 KiB
C++
/**************************************************************************/
|
|
/* kinect_bridge.cpp */
|
|
/**************************************************************************/
|
|
/* This file is part of: */
|
|
/* AETHEX ENGINE */
|
|
/* https://aethex.foundation */
|
|
/**************************************************************************/
|
|
/* Copyright (c) 2026-present AeThex Labs. */
|
|
/**************************************************************************/
|
|
|
|
#include "kinect_bridge.h"
|
|
#include "core/config/engine.h"
|
|
#include <cstring>
|
|
|
|
KinectBridge *KinectBridge::singleton = nullptr;
|
|
|
|
KinectBridge *KinectBridge::get_singleton() {
|
|
return singleton;
|
|
}
|
|
|
|
void KinectBridge::_bind_methods() {
|
|
// Server control
|
|
ClassDB::bind_method(D_METHOD("start", "port"), &KinectBridge::start, DEFVAL(9000));
|
|
ClassDB::bind_method(D_METHOD("stop"), &KinectBridge::stop);
|
|
ClassDB::bind_method(D_METHOD("is_active"), &KinectBridge::is_active);
|
|
ClassDB::bind_method(D_METHOD("get_port"), &KinectBridge::get_port);
|
|
|
|
// Mode
|
|
ClassDB::bind_method(D_METHOD("set_mode", "mode"), &KinectBridge::set_mode);
|
|
ClassDB::bind_method(D_METHOD("get_mode"), &KinectBridge::get_mode);
|
|
ADD_PROPERTY(PropertyInfo(Variant::INT, "mode", PROPERTY_HINT_ENUM, "Foundry,GameForge"), "set_mode", "get_mode");
|
|
|
|
// Skeleton
|
|
ClassDB::bind_method(D_METHOD("get_skeleton"), &KinectBridge::get_skeleton);
|
|
ClassDB::bind_method(D_METHOD("is_skeleton_detected"), &KinectBridge::is_skeleton_detected);
|
|
ClassDB::bind_method(D_METHOD("get_joint_position", "joint_id"), &KinectBridge::get_joint_position);
|
|
|
|
// Gesture detector
|
|
ClassDB::bind_method(D_METHOD("get_gesture_detector"), &KinectBridge::get_gesture_detector);
|
|
|
|
// Debug
|
|
ClassDB::bind_method(D_METHOD("set_debug_mode", "enabled"), &KinectBridge::set_debug_mode);
|
|
ClassDB::bind_method(D_METHOD("is_debug_mode"), &KinectBridge::is_debug_mode);
|
|
|
|
// Signals
|
|
ADD_SIGNAL(MethodInfo("skeleton_detected", PropertyInfo(Variant::BOOL, "detected")));
|
|
ADD_SIGNAL(MethodInfo("joint_updated",
|
|
PropertyInfo(Variant::INT, "joint_id"),
|
|
PropertyInfo(Variant::VECTOR3, "position"),
|
|
PropertyInfo(Variant::INT, "tracking_state")));
|
|
ADD_SIGNAL(MethodInfo("gesture_received",
|
|
PropertyInfo(Variant::STRING, "gesture_type"),
|
|
PropertyInfo(Variant::DICTIONARY, "params")));
|
|
ADD_SIGNAL(MethodInfo("identity_forged",
|
|
PropertyInfo(Variant::DICTIONARY, "signature_data")));
|
|
ADD_SIGNAL(MethodInfo("forge_progress",
|
|
PropertyInfo(Variant::FLOAT, "progress")));
|
|
|
|
// Enums
|
|
BIND_ENUM_CONSTANT(MODE_FOUNDRY);
|
|
BIND_ENUM_CONSTANT(MODE_GAMEFORGE);
|
|
}
|
|
|
|
void KinectBridge::_notification(int p_what) {
|
|
switch (p_what) {
|
|
case NOTIFICATION_PROCESS: {
|
|
if (!is_running || udp_peer.is_null()) {
|
|
return;
|
|
}
|
|
|
|
// Process incoming OSC packets
|
|
while (udp_peer->get_available_packet_count() > 0) {
|
|
Vector<uint8_t> packet;
|
|
Error err = udp_peer->get_packet_buffer(packet);
|
|
if (err == OK && packet.size() > 0) {
|
|
_process_osc_packet(packet);
|
|
}
|
|
}
|
|
} break;
|
|
|
|
case NOTIFICATION_READY: {
|
|
set_process(true);
|
|
} break;
|
|
}
|
|
}
|
|
|
|
Error KinectBridge::start(int p_port) {
|
|
if (is_running) {
|
|
stop();
|
|
}
|
|
|
|
listen_port = p_port;
|
|
|
|
udp_peer.instantiate();
|
|
Error err = udp_peer->bind(listen_port);
|
|
|
|
if (err != OK) {
|
|
ERR_PRINT(vformat("[KinectBridge] Failed to bind UDP port %d: %s", listen_port, itos(err)));
|
|
udp_peer.unref();
|
|
return err;
|
|
}
|
|
|
|
is_running = true;
|
|
print_line(vformat("[KinectBridge] Listening on UDP port %d", listen_port));
|
|
print_line("[KinectBridge] Mode: " + String(current_mode == MODE_FOUNDRY ? "FOUNDRY" : "GAMEFORGE"));
|
|
|
|
return OK;
|
|
}
|
|
|
|
void KinectBridge::stop() {
|
|
if (udp_peer.is_valid()) {
|
|
udp_peer->close();
|
|
udp_peer.unref();
|
|
}
|
|
is_running = false;
|
|
print_line("[KinectBridge] Stopped");
|
|
}
|
|
|
|
void KinectBridge::set_mode(Mode p_mode) {
|
|
current_mode = p_mode;
|
|
if (gesture_detector.is_valid()) {
|
|
// Update gesture detector mode
|
|
}
|
|
}
|
|
|
|
Vector3 KinectBridge::get_joint_position(int p_joint_id) const {
|
|
if (skeleton.is_valid()) {
|
|
return skeleton->get_joint_position(p_joint_id);
|
|
}
|
|
return Vector3();
|
|
}
|
|
|
|
void KinectBridge::set_debug_mode(bool p_enabled) {
|
|
// Store in skeleton or local flag
|
|
}
|
|
|
|
bool KinectBridge::is_debug_mode() const {
|
|
return false;
|
|
}
|
|
|
|
// OSC Parsing
|
|
|
|
void KinectBridge::_process_osc_packet(const Vector<uint8_t> &p_data) {
|
|
if (p_data.size() < 8) {
|
|
return;
|
|
}
|
|
|
|
// Check for OSC bundle
|
|
String header;
|
|
for (int i = 0; i < 8 && i < p_data.size(); i++) {
|
|
header += char(p_data[i]);
|
|
}
|
|
|
|
if (header.begins_with("#bundle")) {
|
|
// Parse bundle - skip header (8) + timestamp (8)
|
|
int offset = 16;
|
|
while (offset < p_data.size() - 4) {
|
|
// Read message size
|
|
int32_t size = _read_osc_int32(p_data, offset);
|
|
if (size > 0 && offset + size <= p_data.size()) {
|
|
Vector<uint8_t> msg_data;
|
|
msg_data.resize(size);
|
|
for (int i = 0; i < size; i++) {
|
|
msg_data.write[i] = p_data[offset + i];
|
|
}
|
|
int msg_offset = 0;
|
|
|
|
String address = _read_osc_string(msg_data, msg_offset);
|
|
String type_tags = _read_osc_string(msg_data, msg_offset);
|
|
|
|
Array args;
|
|
for (int i = 0; i < type_tags.length(); i++) {
|
|
char tag = type_tags[i];
|
|
if (tag == ',') continue;
|
|
if (tag == 'i') {
|
|
args.push_back(_read_osc_int32(msg_data, msg_offset));
|
|
} else if (tag == 'f') {
|
|
args.push_back(_read_osc_float32(msg_data, msg_offset));
|
|
} else if (tag == 's') {
|
|
args.push_back(_read_osc_string(msg_data, msg_offset));
|
|
}
|
|
}
|
|
|
|
_dispatch_osc_message(address, args);
|
|
offset += size;
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
} else {
|
|
// Single message
|
|
int offset = 0;
|
|
String address = _read_osc_string(p_data, offset);
|
|
String type_tags = _read_osc_string(p_data, offset);
|
|
|
|
Array args;
|
|
for (int i = 0; i < type_tags.length(); i++) {
|
|
char tag = type_tags[i];
|
|
if (tag == ',') continue;
|
|
if (tag == 'i') {
|
|
args.push_back(_read_osc_int32(p_data, offset));
|
|
} else if (tag == 'f') {
|
|
args.push_back(_read_osc_float32(p_data, offset));
|
|
} else if (tag == 's') {
|
|
args.push_back(_read_osc_string(p_data, offset));
|
|
}
|
|
}
|
|
|
|
_dispatch_osc_message(address, args);
|
|
}
|
|
}
|
|
|
|
String KinectBridge::_read_osc_string(const Vector<uint8_t> &p_data, int &r_offset) {
|
|
String result;
|
|
while (r_offset < p_data.size() && p_data[r_offset] != 0) {
|
|
result += char(p_data[r_offset]);
|
|
r_offset++;
|
|
}
|
|
r_offset++; // Skip null terminator
|
|
// Align to 4 bytes
|
|
r_offset = ((r_offset + 3) / 4) * 4;
|
|
return result;
|
|
}
|
|
|
|
int32_t KinectBridge::_read_osc_int32(const Vector<uint8_t> &p_data, int &r_offset) {
|
|
if (r_offset + 4 > p_data.size()) return 0;
|
|
|
|
// Big-endian
|
|
int32_t val = (p_data[r_offset] << 24) |
|
|
(p_data[r_offset + 1] << 16) |
|
|
(p_data[r_offset + 2] << 8) |
|
|
p_data[r_offset + 3];
|
|
r_offset += 4;
|
|
return val;
|
|
}
|
|
|
|
float KinectBridge::_read_osc_float32(const Vector<uint8_t> &p_data, int &r_offset) {
|
|
if (r_offset + 4 > p_data.size()) return 0.0f;
|
|
|
|
// Big-endian to little-endian
|
|
uint8_t bytes[4] = {
|
|
p_data[r_offset + 3],
|
|
p_data[r_offset + 2],
|
|
p_data[r_offset + 1],
|
|
p_data[r_offset]
|
|
};
|
|
r_offset += 4;
|
|
|
|
float result;
|
|
memcpy(&result, bytes, 4);
|
|
return result;
|
|
}
|
|
|
|
void KinectBridge::_dispatch_osc_message(const String &p_address, const Array &p_args) {
|
|
if (p_address == "/skeleton/detected") {
|
|
bool detected = p_args.size() > 0 ? (int)p_args[0] != 0 : false;
|
|
skeleton_detected = detected;
|
|
emit_signal(SNAME("skeleton_detected"), detected);
|
|
|
|
if (detected) {
|
|
print_line("[KinectBridge] Skeleton DETECTED");
|
|
} else {
|
|
print_line("[KinectBridge] Skeleton LOST");
|
|
}
|
|
}
|
|
else if (p_address == "/skeleton/joint") {
|
|
if (p_args.size() >= 5 && skeleton.is_valid()) {
|
|
int joint_id = p_args[0];
|
|
Vector3 pos((float)p_args[1], (float)p_args[2], (float)p_args[3]);
|
|
int tracking_state = p_args[4];
|
|
|
|
skeleton->set_joint(joint_id, pos, tracking_state);
|
|
emit_signal(SNAME("joint_updated"), joint_id, pos, tracking_state);
|
|
|
|
// Update gesture detector
|
|
if (gesture_detector.is_valid()) {
|
|
gesture_detector->update_joint(joint_id, pos, tracking_state);
|
|
}
|
|
}
|
|
}
|
|
else if (p_address == "/gesture/arms_raised") {
|
|
Dictionary params;
|
|
if (p_args.size() > 0) params["duration"] = p_args[0];
|
|
params["completed"] = true;
|
|
emit_signal(SNAME("gesture_received"), "arms_raised", params);
|
|
}
|
|
else if (p_address == "/gesture/arms_raised_progress") {
|
|
if (p_args.size() >= 2) {
|
|
emit_signal(SNAME("forge_progress"), (float)p_args[1]);
|
|
}
|
|
}
|
|
else if (p_address == "/gesture/push") {
|
|
Dictionary params;
|
|
params["hand"] = "right";
|
|
if (p_args.size() > 1) params["strength"] = p_args[1];
|
|
emit_signal(SNAME("gesture_received"), "push", params);
|
|
}
|
|
else if (p_address == "/gesture/swipe") {
|
|
Dictionary params;
|
|
if (p_args.size() > 1) params["direction"] = p_args[1];
|
|
emit_signal(SNAME("gesture_received"), "swipe", params);
|
|
}
|
|
else if (p_address == "/foundry/identity_forged") {
|
|
Dictionary signature;
|
|
if (p_args.size() > 0) signature["seed"] = p_args[0];
|
|
if (p_args.size() > 1) signature["color_hue"] = p_args[1];
|
|
if (p_args.size() > 2) signature["particle_density"] = p_args[2];
|
|
if (p_args.size() > 3) signature["movement_signature"] = p_args[3];
|
|
|
|
emit_signal(SNAME("identity_forged"), signature);
|
|
print_line(vformat("[KinectBridge] 🔥 IDENTITY FORGED! Seed: %d", (int)signature["seed"]));
|
|
}
|
|
}
|
|
|
|
KinectBridge::KinectBridge() {
|
|
if (singleton == nullptr) {
|
|
singleton = this;
|
|
}
|
|
|
|
skeleton.instantiate();
|
|
gesture_detector.instantiate();
|
|
}
|
|
|
|
KinectBridge::~KinectBridge() {
|
|
stop();
|
|
if (singleton == this) {
|
|
singleton = nullptr;
|
|
}
|
|
}
|