#pragma once
#include <godot_cpp/classes/array_mesh.hpp>
#include <godot_cpp/classes/mesh.hpp>
#include <godot_cpp/classes/mesh_instance3d.hpp>
#include <godot_cpp/classes/node.hpp>
#include <godot_cpp/classes/node3d.hpp>
#include <godot_cpp/classes/file_access.hpp>
#include <godot_cpp/classes/image.hpp>
#include <godot_cpp/classes/image_texture.hpp>
#include <godot_cpp/classes/standard_material3d.hpp>
#include <godot_cpp/core/class_db.hpp>
#include <godot_cpp/variant/dictionary.hpp>
#include <godot_cpp/variant/packed_float32_array.hpp>
#include <godot_cpp/variant/packed_vector2_array.hpp>
#include <godot_cpp/variant/packed_vector3_array.hpp>
#include <godot_cpp/variant/string.hpp>
#include <godot_cpp/variant/transform3d.hpp>
#include <godot_cpp/variant/typed_array.hpp>
#include <set>
#include "shm_protocol.h"
#include "uds_server.h"
using namespace godot;
class BlenderBridge : public Node {
GDCLASS(BlenderBridge, Node)
protected:
static void _bind_methods();
public:
BlenderBridge();
~BlenderBridge();
// ── Server lifecycle ────────────────────────────────────────────────
Error start_server(const String &socket_path = "/tmp/blender_bridge.sock");
void stop_server();
bool is_connected() const;
int get_client_count() const;
// ── Bootstrap: export current Godot scene to shared memory ──────────
Error export_scene(Node *root);
Dictionary get_export_info() const;
// ── Live sync: import changes from shared memory ────────────────────
void import_changes(Node *root);
// ── Frame tick ──────────────────────────────────────────────────────
void _process(double delta) override;
// ── Properties ──────────────────────────────────────────────────────
void set_shm_name(const String &name);
String get_shm_name() const;
void set_shm_size(int size);
int get_shm_size() const;
void set_max_objects(int count);
int get_max_objects() const;
void set_auto_sync(bool enabled);
bool get_auto_sync() const;
// ── VirtualTexture integration ──────────────────────────────────────
void set_virtual_texture_path(const NodePath &path);
NodePath get_virtual_texture_path() const;
// Forward texture dirty rect from Blender to VirtualTexture
void notify_texture_dirty(int texture_id, int tile_x, int tile_y, int w, int h, int mip);
// ── Finalize (save textures + scene to disk) ────────────────────────
Error finalize_scene(Node *root, const String &save_dir);
// ── Signals ─────────────────────────────────────────────────────────
// client_connected(client_id: int)
// client_disconnected(client_id: int)
// scene_exported(object_count: int)
// changes_imported(object_count: int)
// texture_dirty(texture_id: int, tile_x: int, tile_y: int, w: int, h: int, mip: int)
private:
// ── SHM + UDS ───────────────────────────────────────────────────────
ShmRegion _shm;
UdsServer _uds;
String _shm_name = "/blender_bridge";
int _shm_size = 24 * 1024 * 1024; // 24 MB (8 MB region 0 + 8 MB region 1 + 8 MB tile ring)
int _max_objects = 256;
bool _auto_sync = false;
bool _server_running = false;
bool _import_pending = false; // Set by UDS "updated" from Blender
int _import_cooldown = 0; // Frames to skip _detect_and_push_changes after import
NodePath _virtual_texture_path;
// ── Image texture cache (whole-image transfer from Blender) ──────────
HashMap<int, Ref<ImageTexture>> _image_textures;
// ── UDS message handling ────────────────────────────────────────────
void _handle_uds_message(const UdsMessage &msg);
// ── Split-region SHM pointers ───────────────────────────────────────
// Region 0: Godot→Blender (Godot writes, Blender reads)
// Region 1: Blender→Godot (Blender writes, Godot reads)
void *_write_base = nullptr; // Region 0 base
void *_read_base = nullptr; // Region 1 base
size_t _region_size = 0;
// ── Sequence tracking ───────────────────────────────────────────────
uint64_t _export_sequence = 0;
uint64_t _last_import_sequence = 0;
// ── Object ID mapping ───────────────────────────────────────────────
struct TrackedObject {
uint32_t object_id;
ObjectID godot_instance_id;
String name;
ObjectType type;
// Change detection: cached transform hash
uint64_t transform_hash = 0;
// Origin tracking: true if created by Blender import
bool from_blender = false;
};
std::vector<TrackedObject> _tracked_objects;
uint32_t _next_object_id = 1;
// ── Scene structure tracking ────────────────────────────────────────
ObjectID _export_root_id;
int _last_export_node_count = 0;
std::set<uint64_t> _known_instance_ids;
// ── Export helpers ───────────────────────────────────────────────────
void _collect_exportable_nodes(Node *node, std::vector<Node3D *> &out);
uint32_t _export_node(Node3D *node, uint32_t index, uint8_t *blob_base, uint32_t &blob_offset);
static void _write_name_prefix(const String &name, uint8_t *blob_base, uint32_t &blob_offset);
// ── Change detection ────────────────────────────────────────────────
static uint64_t _hash_transform(const Transform3D &xform);
void _detect_and_push_changes();
// ── Coordinate conversion ───────────────────────────────────────────
// Godot Y-up (x, y, z) → Blender Z-up (x, -z, y)
static void _godot_to_blender_vec3(const float *in, float *out) {
out[0] = in[0];
out[1] = -in[2];
out[2] = in[1];
}
// Blender Z-up (x, y, z) → Godot Y-up (x, z, -y)
static void _blender_to_godot_vec3(const float *in, float *out) {
out[0] = in[0];
out[1] = in[2];
out[2] = -in[1];
}
// Transform3D → 4×4 float array in Blender convention
static void _export_transform(const Transform3D &xform, float *out);
};