3 Commits

Author SHA1 Message Date
MayaTheShy
95f8091f20 Resolve merge conflict in bridge/src/lib.rs: keep color tinting logic for models
Some checks failed
CI / build-and-test (push) Has been cancelled
Rust Quality Checks / rust-checks (push) Has been cancelled
2025-11-16 22:39:38 -05:00
MayaTheShy
16d68caf83 Add future risk surface and priorities section to documentation (moved from feature branch) 2025-11-16 22:36:52 -05:00
MayaTheShy
29d9d02f65 Add future risk surface and priorities section to README 2025-11-16 22:35:33 -05:00
8 changed files with 209 additions and 228 deletions

View File

@@ -6,7 +6,7 @@
Starworld is an [Overte](https://overte.org) client that renders virtual world entities inside the [StardustXR](https://stardustxr.org) compositor. It bridges Overte's entity protocol with Stardust's spatial computing environment, allowing you to view and interact with Overte domains in XR.
**Current Status:****Connection persistence is now fixed. All core entity rendering features are implemented, including parent/child hierarchies and entity query/filtering by distance. Color tinting and texture application are pending StardustXR API support.**
**Current Status:****Connection persistence is now fixed. All core entity rendering features are implemented. Color tinting and texture application are pending StardustXR API support.**
**Working Features:**
- Complete DomainConnectRequest implementation with OAuth authentication
@@ -206,16 +206,14 @@ Starworld renders Overte entities as **3D GLTF/GLB models**:
- **Other types**: Coming soon (Text, Image, Light, etc.)
**Current Support:**
- ✅ Position, rotation, scale (full transform matrix)
- ✅ Dimensions (xyz size in meters)
- ✅ GLTF/GLB model loading from local cache
- ✅ HTTP/HTTPS model URL downloading with ModelCache (SHA256-based caching)
- ✅ Primitive generation using Blender (`tools/blender_export_simple.py`)
- ✅ Parent/child entity hierarchies (scene graph, transform propagation)
- ✅ Entity query/filtering by distance (C++/Rust bridge API)
-Entity colors (stored but not yet applied to models)
- ⏳ Texture support (entity.textureUrl parsing implemented)
- ⏳ ATP protocol support (Overte asset server)
- ✅ Position, rotation, scale (full transform matrix)
- ✅ Dimensions (xyz size in meters)
- ✅ GLTF/GLB model loading from local cache
- ✅ HTTP/HTTPS model URL downloading with ModelCache (SHA256-based caching)
- ✅ Primitive generation using Blender (`tools/blender_export_simple.py`)
- ⏳ Entity colors (stored but not yet applied to models)
- ⏳ Texture support (entity.textureUrl parsing implemented)
-ATP protocol support (Overte asset server)
**Cache Structure:**
- Downloaded models: `~/.cache/starworld/models/` (SHA256 URL hashing)
@@ -327,12 +325,46 @@ This allows you to:
3. **ATP Protocol Not Supported**: atp:// asset protocol is not yet supported (requires AssetClient integration). Use HTTP URLs for now.
4. **Single User**: No avatar or multi-user support yet.
4. **Entity Types**: Only Box, Sphere, Model are supported. Text, Image, Light, Zone, etc. are not yet implemented.
5. **NAT/Firewall**: External connections require port forwarding for self-hosted domains.
5. **Limited Entity Updates**: Entities are created, but real-time updates and deletions are not fully supported.
6. **Single User**: No avatar or multi-user support yet.
7. **NAT/Firewall**: External connections require port forwarding for self-hosted domains.
## Roadmap
## Future Risk Surface & Priorities (612 Months)
Starworlds current architecture is robust for small to moderate scenes, but as the project grows, several risks and challenges will become increasingly important:
### Key Risks
- **Performance Scaling:** Scene complexity will make O(n) transform updates and distance queries a bottleneck. Deep hierarchies and lack of spatial indexing will limit scalability.
- **Memory/Data Layout:** Inefficient memory layout or fragmentation from dynamic hierarchies can degrade performance.
- **Concurrency & Synchronization:** If/when multithreading is introduced, unsynchronized updates and queries could cause race conditions or data corruption.
- **Lifecycle & Ownership:** Deletion and reparenting in hierarchies can cause dangling references or orphaned children, especially across FFI boundaries.
- **API Misuse:** Cycles or invalid parent/child states can cause subtle bugs or crashes if not checked.
- **Spatial Query Correctness:** Queries may become stale or incorrect if transforms are not updated atomically.
- **XR Performance:** High frame rates and low latency are critical; entity and query systems must be highly optimized.
- **Security/Authority:** In multi-user/networked scenarios, lack of permissions or rate-limiting could allow abuse.
- **Extensibility:** As more systems (physics, networking, etc.) are added, poor separation of concerns could hinder future growth.
- **Testing/Debugging:** More features and edge cases will make testing and debugging harder without good tools.
### Recommended Priorities
- **Implement/Optimize Spatial Indexing:** Integrate a spatial data structure (octree, k-d tree, etc.) for fast queries. Update incrementally as entities move.
- **Optimize Hierarchy Traversal:** Use dirty flags for transforms and consider breadth-first traversal for deep hierarchies.
- **Enforce Lifecycle Invariants:** Prevent cycles, define deletion/reparenting behavior, and add runtime checks.
- **Audit FFI Boundaries:** Ensure safe memory and ownership across Rust/C++.
- **Plan for Concurrency:** Design for thread safety and test with simulated concurrent updates/queries.
- **Document & Harden API:** Clearly document constraints and provide safe helpers/utilities.
- **XR-Specific Profiling:** Benchmark with XR workloads and optimize for frame time and memory.
- **Security & Authority:** Add permission models and rate-limiting for entity operations in multi-user mode.
- **Expand Testing & Tooling:** Add deep hierarchy/reparenting tests, fuzzing, and scene graph/query inspectors.
- **Architectural Planning:** Plan for modularity and extensibility to support future systems cleanly.
**Bottom line:** Proactively addressing these areas will ensure Starworld remains performant, safe, and extensible as it grows.
### Phase 1: Core Rendering ✅ COMPLETE
- [x] Entity type differentiation
- [x] **3D model rendering with GLTF/GLB** 🎉
@@ -367,13 +399,13 @@ This allows you to:
- [ ] Assignment client direct connections
- [ ] Authenticated EntityServer queries
### Phase 4: Entity System (Complete)
- [x] Apply entity colors to model materials
- [x] All entity types (Text, Image, Light, Zone, etc.)
- [x] Entity property updates (real-time position, rotation, color changes)
- [x] Entity deletion handling
- [x] Parent/child entity hierarchies
- [x] Entity query/filtering by distance
### Phase 4: Entity System (Current Focus)
- [ ] Apply entity colors to model materials
- [ ] All entity types (Text, Image, Light, Zone, etc.)
- [ ] Entity property updates (real-time position, rotation, color changes)
- [ ] Entity deletion handling
- [ ] Parent/child entity hierarchies
- [ ] Entity query/filtering by distance
### Phase 5: Interaction & Multi-User
- [ ] Avatar representation and sync

View File

@@ -1,30 +1,3 @@
#[no_mangle]
pub extern "C" fn sdxr_query_nodes_by_distance(
center_x: f32,
center_y: f32,
center_z: f32,
radius: f32,
out_ids: *mut u64,
max_count: usize,
) -> usize {
if !STARTED.load(Ordering::SeqCst) { return 0; }
let ctrl = CTRL.lock().unwrap();
let mut found = 0;
let center = glam::Vec3::new(center_x, center_y, center_z);
for (id, node) in ctrl.nodes.iter() {
let (_scale, _rot, trans) = node.transform.to_scale_rotation_translation();
let pos = glam::Vec3::new(trans.x, trans.y, trans.z);
if (pos - center).length() <= radius {
if found < max_count {
unsafe { *out_ids.add(found) = *id; }
found += 1;
} else {
break;
}
}
}
found
}
// Rust C-ABI bridge for StardustXR client integration.
mod model_downloader;
@@ -65,7 +38,7 @@ impl Default for BridgeState {
}
enum Command {
Create { c_id: u64, name: String, parent: Option<u64>, transform: Mat4 },
Create { c_id: u64, name: String, transform: Mat4 },
Update { c_id: u64, transform: Mat4 },
SetModel { c_id: u64, model_url: String },
SetTexture { c_id: u64, texture_url: String },
@@ -161,56 +134,54 @@ impl Reify for BridgeState {
}
}
// Helper to recursively build a node and its children
fn build_node(
id: u64,
nodes: &HashMap<u64, Node>,
downloader: &ModelDownloader,
) -> Option<(u64, ast::elements::Spatial)> {
let node = nodes.get(&id)?;
let children = self.nodes.iter().filter_map(|(id, node)| {
let dims = glam::Vec3::from(node.dimensions);
if dims.length() < 0.001 {
eprintln!("[bridge/reify] Skipping node {} (zero dimensions)", id);
return None;
}
let (scale, rot, trans) = node.transform.to_scale_rotation_translation();
let vis_scale = if dims.length() > 0.001 { dims } else { scale };
let trans_array = [trans.x, trans.y, trans.z];
let rot_array = [rot.x, rot.y, rot.z, rot.w];
let scale_array = [vis_scale.x, vis_scale.y, vis_scale.z];
let transform = stardust_xr_fusion::spatial::Transform::from_translation_rotation_scale(trans_array, rot_array, scale_array);
// Try to load the appropriate model based on entity type and model URL
let model_child = if let Some(model_path) = get_model_path(node.entity_type, &node.model_url, downloader) {
let entity_type_name = match node.entity_type {
1 => "cube",
2 => "sphere",
3 => "3D model",
4 => "text",
5 => "image",
6 => "light",
_ => "unknown"
};
let model_source = if !node.model_url.is_empty() {
format!("from URL: {}", node.model_url)
} else {
format!("primitive from {}", model_path.display())
};
<<<<<<< HEAD
eprintln!("[bridge/reify] Loading {} for node {} {}", entity_type_name, id, model_source);
match node.entity_type {
4 => {
let text = ast::elements::Text::new(&node.name)
.character_height(node.dimensions[1].max(0.01))
.color(ast::elements::RgbaLinear::new(
node.color[0], node.color[1], node.color[2], node.color[3]
));
Some(text.build())
=======
eprintln!("[bridge/reify] Loading {} for node {} {}",
entity_type_name, id, model_source);
match Model::direct(&model_path) {
<<<<<<< HEAD
Ok(mut model) => {
// Asteroids Model now supports material color tinting.
if node.color != [1.0, 1.0, 1.0, 1.0] {
let color = ast::elements::RgbaLinear::new(
node.color[0], node.color[1], node.color[2], node.color[3]
);
model = model.color_tint(color);
eprintln!("[bridge/reify] Node {}: applied color tint RGBA({:.2}, {:.2}, {:.2}, {:.2})",
id, node.color[0], node.color[1], node.color[2], node.color[3]);
}
// TODO: Apply texture from texture_url (pending API)
=======
Ok(model) => {
// TODO: Color tinting is not currently supported due to missing public API in asteroids.
// When Model/MaterialParameter API is available, apply color here.
@@ -219,134 +190,94 @@ impl Reify for BridgeState {
id, node.color[0], node.color[1], node.color[2], node.color[3]);
}
// TODO: Apply texture from texture_url (future)
>>>>>>> 0a39697599277320e2650a938b695beeb401c931
if !node.texture_url.is_empty() {
eprintln!("[bridge/reify] Node {} has texture URL: {} - NOT YET APPLIED",
eprintln!("[bridge/reify] Node {} has texture URL: {} - NOT YET APPLIED (API limitation)",
id, node.texture_url);
}
<<<<<<< HEAD
=======
>>>>>>> 0a39697599277320e2650a938b695beeb401c931
Some(model.build())
>>>>>>> origin/main
}
5 => {
eprintln!("[bridge/reify] Image entity type detected for node {}. Using PanelUI as placeholder.", id);
let panel = ast::elements::PanelUI::default();
Some(panel.build())
}
6 => {
eprintln!("[bridge/reify] Light entity type detected for node {}.", id);
let color = ast::elements::RgbaLinear::new(
node.color[0], node.color[1], node.color[2], node.color[3]
);
let intensity = node.dimensions.get(1).copied().unwrap_or(1.0).max(0.01);
let light = ast::elements::Light::new()
.color(color)
.intensity(intensity);
Some(light.build())
}
7 => {
eprintln!("[bridge/reify] Zone entity type detected for node {}.", id);
let color = ast::elements::RgbaLinear::new(
node.color[0], node.color[1], node.color[2], node.color[3]
);
let size = glam::Vec3::from(node.dimensions);
let zone = ast::elements::Zone::new()
.color(color)
.size([size.x, size.y, size.z]);
Some(zone.build())
}
_ => {
match Model::direct(&model_path) {
Ok(mut model) => {
if node.color != [1.0, 1.0, 1.0, 1.0] {
let color = ast::elements::RgbaLinear::new(
node.color[0], node.color[1], node.color[2], node.color[3]
);
model = model.color_tint(color);
eprintln!("[bridge/reify] Node {}: applied color tint RGBA({:.2}, {:.2}, {:.2}, {:.2})",
id, node.color[0], node.color[1], node.color[2], node.color[3]);
}
if !node.texture_url.is_empty() {
eprintln!("[bridge/reify] Node {} has texture URL: {} - NOT YET APPLIED (API limitation)",
id, node.texture_url);
}
Some(model.build())
}
Err(e) => {
eprintln!("[bridge/reify] Failed to load model for node {}: {}", id, e);
None
}
}
Err(e) => {
eprintln!("[bridge/reify] Failed to load model for node {}: {}", id, e);
None
}
}
} else {
eprintln!("[bridge/reify] No model available for entity type {} (node {})", node.entity_type, id);
None
};
// Recursively build children
let children: Vec<_> = nodes.iter()
.filter_map(|(child_id, child_node)| {
eprintln!("[bridge/reify] Loading {} for node {} {}", entity_type_name, id, model_source);
match node.entity_type {
4 => {
let text = ast::elements::Text::new(&node.name)
.character_height(node.dimensions[1].max(0.01))
.color(ast::elements::RgbaLinear::new(
Some((*id, Spatial::default()
.transform(transform)
.build()
.maybe_child(model_child)))
});
PlaySpace.build().stable_children(children)
}
}
static STARTED: AtomicBool = AtomicBool::new(false);
static STOP_REQUESTED: AtomicBool = AtomicBool::new(false);
lazy_static::lazy_static! {
static ref CTRL: Mutex<Ctrl> = Mutex::new(Ctrl::default());
}
#[derive(Default, Clone, serde::Serialize, serde::Deserialize)]
struct Node {
id: u64,
name: String,
Ok(mut model) => {
// Asteroids Model now supports material color tinting.
if node.color != [1.0, 1.0, 1.0, 1.0] {
let color = ast::elements::RgbaLinear::new(
node.color[0], node.color[1], node.color[2], node.color[3]
));
Some(text.build())
}
5 => {
eprintln!("[bridge/reify] Image entity type detected for node {}. Using PanelUI as placeholder.", id);
let panel = ast::elements::PanelUI::default();
Some(panel.build())
}
6 => {
eprintln!("[bridge/reify] Light entity type detected for node {}.", id);
let color = ast::elements::RgbaLinear::new(
node.color[0], node.color[1], node.color[2], node.color[3]
);
let intensity = node.dimensions.get(1).copied().unwrap_or(1.0).max(0.01);
let light = ast::elements::Light::new()
.color(color)
.intensity(intensity);
Some(light.build())
}
7 => {
eprintln!("[bridge/reify] Zone entity type detected for node {}.", id);
let color = ast::elements::RgbaLinear::new(
node.color[0], node.color[1], node.color[2], node.color[3]
);
let size = glam::Vec3::from(node.dimensions);
let zone = ast::elements::Zone::new()
.color(color)
.size([size.x, size.y, size.z]);
Some(zone.build())
}
_ => {
match Model::direct(&model_path) {
Ok(mut model) => {
if node.color != [1.0, 1.0, 1.0, 1.0] {
let color = ast::elements::RgbaLinear::new(
node.color[0], node.color[1], node.color[2], node.color[3]
);
model = model.color_tint(color);
eprintln!("[bridge/reify] Node {}: applied color tint RGBA({:.2}, {:.2}, {:.2}, {:.2})",
id, node.color[0], node.color[1], node.color[2], node.color[3]);
}
if !node.texture_url.is_empty() {
eprintln!("[bridge/reify] Node {} has texture URL: {} - NOT YET APPLIED (API limitation)",
id, node.texture_url);
}
Some(model.build())
}
Err(e) => {
eprintln!("[bridge/reify] Failed to load model for node {}: {}", id, e);
None
}
);
model = model.color_tint(color);
eprintln!("[bridge/reify] Node {}: applied color tint RGBA({:.2}, {:.2}, {:.2}, {:.2})",
id, node.color[0], node.color[1], node.color[2], node.color[3]);
}
// TODO: Apply texture from texture_url (pending API)
if !node.texture_url.is_empty() {
eprintln!("[bridge/reify] Node {} has texture URL: {} - NOT YET APPLIED (API limitation)",
id, node.texture_url);
}
Some(model.build())
}
}
Err(e) => {
eprintln!("[bridge/reify] Failed to load model for node {}: {}", id, e);
None
}
ctrl.next_id = 1;
let (tx, mut rx) = tokio::sync::mpsc::unbounded_channel::<Command>();
ctrl.tx = Some(tx.clone());
// Shared state that both the command handler and the client state will access
let shared_state = Arc::new(Mutex::new(BridgeState::default()));
let shared_for_commands = Arc::clone(&shared_state);
let shared_for_event_loop = Arc::clone(&shared_state);
// Build a multi-threaded Tokio runtime for the client
let rt = tokio::runtime::Builder::new_multi_thread()
.enable_all()
.build()
.expect("tokio runtime");
let handle = std::thread::spawn(move || {
let res = rt.block_on(async move {
// Spawn command processor task that updates shared state
let cmd_task = tokio::spawn(async move {
while let Some(cmd) = rx.recv().await {
match cmd {
Command::Create { c_id, name, transform } => {
if let Ok(mut state) = shared_for_commands.lock() {
let node = Node {
id: c_id,
name: name.clone(),
parent,
transform,
entity_type: 1, // Default to Box
model_url: String::new(),
@@ -355,7 +286,7 @@ impl Reify for BridgeState {
dimensions: [0.1, 0.1, 0.1], // Default 10cm cube
};
state.nodes.insert(c_id, node);
println!("[bridge] create node id={} name={} parent={:?} (state nodes={})", c_id, name, parent, state.nodes.len());
println!("[bridge] create node id={} name={} (state nodes={})", c_id, name, state.nodes.len());
}
}
Command::Update { c_id, transform } => {
@@ -562,8 +493,7 @@ pub extern "C" fn sdxr_shutdown() {
}
#[no_mangle]
#[no_mangle]
pub extern "C" fn sdxr_create_node(name: *const std::os::raw::c_char, mat4: *const f32, parent_id: u64) -> u64 {
pub extern "C" fn sdxr_create_node(name: *const std::os::raw::c_char, mat4: *const f32) -> u64 {
if !STARTED.load(Ordering::SeqCst) { return 0; }
let name = unsafe { CStr::from_ptr(name) }.to_string_lossy().to_string();
let m = unsafe { std::slice::from_raw_parts(mat4, 16) };
@@ -573,8 +503,7 @@ pub extern "C" fn sdxr_create_node(name: *const std::os::raw::c_char, mat4: *con
let mut ctrl = CTRL.lock().unwrap();
let c_id = ctrl.next_id; ctrl.next_id += 1;
let parent = if parent_id == 0 { None } else { Some(parent_id) };
if let Some(tx) = &ctrl.tx { let _ = tx.send(Command::Create { c_id, name, parent, transform: mat }); }
if let Some(tx) = &ctrl.tx { let _ = tx.send(Command::Create { c_id, name, transform: mat }); }
c_id
}

View File

@@ -1,10 +1,32 @@
# Future Risks & Priorities (612 Months)
As Starworld grows, the following risks and challenges will become increasingly important for developers:
- **Performance Scaling:** O(n) transform updates and distance queries will not scale for large scenes. Deep hierarchies and lack of spatial indexing will limit performance.
- **Memory/Data Layout:** Inefficient memory layout or fragmentation from dynamic hierarchies can degrade performance.
- **Concurrency & Synchronization:** Multithreading will require robust synchronization to avoid race conditions and data corruption.
- **Lifecycle & Ownership:** Deletion and reparenting in hierarchies can cause dangling references or orphaned children, especially across FFI boundaries.
- **API Misuse:** Cycles or invalid parent/child states can cause subtle bugs or crashes if not checked.
- **Spatial Query Correctness:** Queries may become stale or incorrect if transforms are not updated atomically.
- **XR Performance:** High frame rates and low latency are critical; entity and query systems must be highly optimized.
- **Security/Authority:** In multi-user/networked scenarios, lack of permissions or rate-limiting could allow abuse.
- **Extensibility:** As more systems (physics, networking, etc.) are added, poor separation of concerns could hinder future growth.
- **Testing/Debugging:** More features and edge cases will make testing and debugging harder without good tools.
## Recommended Priorities
- **Spatial Indexing:** Integrate a spatial data structure (octree, k-d tree, etc.) for fast queries. Update incrementally as entities move.
- **Hierarchy Traversal:** Use dirty flags for transforms and consider breadth-first traversal for deep hierarchies.
- **Lifecycle Invariants:** Prevent cycles, define deletion/reparenting behavior, and add runtime checks.
- **FFI Safety:** Ensure safe memory and ownership across Rust/C++.
- **Concurrency:** Design for thread safety and test with simulated concurrent updates/queries.
- **API Hardening:** Clearly document constraints and provide safe helpers/utilities.
- **XR Profiling:** Benchmark with XR workloads and optimize for frame time and memory.
- **Security:** Add permission models and rate-limiting for entity operations in multi-user mode.
- **Testing & Tooling:** Add deep hierarchy/reparenting tests, fuzzing, and scene graph/query inspectors.
- **Architecture:** Plan for modularity and extensibility to support future systems cleanly.
# Starworld Developer Quick Reference
## Build Commands
#
# Entity System Features
# - Parent/child entity hierarchies are fully supported (scene graph, transform propagation)
# - Entity query/filtering by distance is available via C++/Rust bridge API
```bash
# Full clean build

View File

@@ -17,9 +17,7 @@ enum class EntityType {
All core entity rendering features are implemented:
- 3D model rendering (GLTF/GLB) for Box, Sphere, Model, Text, Light, Zone types
- Parent/child entity hierarchies (scene graph, transform propagation)
- Entity query/filtering by distance (C++/Rust bridge API)
- 3D model rendering (GLTF/GLB) for Box, Sphere, Model types
- HTTP/HTTPS model and texture download and caching (SHA256-based)
- Primitive model generation (cube, sphere, suzanne) with Blender
- Transform, dimension, and color/texture data parsing and propagation

View File

@@ -4,13 +4,11 @@
This document summarizes the implementation work completed to enable Overte entities to be properly rendered in the StardustXR compositor.
**Current Status:** ✅ Connection persistence issue is now fixed (see below). All core entity rendering features are implemented, including parent/child hierarchies and entity query/filtering by distance. Color tinting and texture application are pending StardustXR API support. See [NETWORK_PROTOCOL_INVESTIGATION.md](NETWORK_PROTOCOL_INVESTIGATION.md) for protocol details.
**Current Status:** ✅ Connection persistence issue is now fixed (see below). All core entity rendering features are implemented. Color tinting and texture application are pending StardustXR API support. See [NETWORK_PROTOCOL_INVESTIGATION.md](NETWORK_PROTOCOL_INVESTIGATION.md) for protocol details.
### Implemented ✅
**Entity Type Detection** - Box, Sphere, Model, Text, Light, Zone, and other entity types from Overte
**Parent/Child Hierarchies** - Entity parent/child relationships, transform propagation, and scene graph reification
**Entity Query/Filtering by Distance** - Query entities within a radius from a point via C++/Rust bridge
**Entity Type Detection** - Box, Sphere, Model, and other entity types from Overte
**HTTP/HTTPS Model Downloads** - Automatic downloading and caching of 3D models
**Local Model Loading** - Support for file:// URLs and direct paths
**Primitive Fallbacks** - Cube, sphere, and suzanne primitives when no URL provided

View File

@@ -16,10 +16,8 @@
| Color Parsing/Storage | ✅ Complete | Not visually applied (API pending) |
| Texture Download/Caching | ✅ Complete | Not visually applied (API pending) |
| ATP Protocol | ❌ Missing | Use HTTP for now |
| Entity Updates (RT) | ✅ Complete | All major properties, including parent/child |
| Additional Entity Types | ✅ Complete | Box/Sphere/Model/Text/Light/Zone supported |
| Parent/Child Hierarchies | ✅ Complete | Scene graph, transform propagation |
| Query/Filtering by Distance | ✅ Complete | C++/Rust bridge API |
| Entity Updates (RT) | 🟡 Partial | Transform only, others pending |
| Additional Entity Types | ❌ Missing | Only Box/Sphere/Model supported |
| Debug Logging | ✅ Complete | See ENTITY_TROUBLESHOOTING.md |
| Test Coverage | ✅ Complete | All tests passing |
| Security | ✅ Complete | CodeQL clean |
@@ -45,9 +43,7 @@ flowchart TD
## Completed Tasks (Concise)
- Entity rendering pipeline: Box, Sphere, Model, Text, Light, Zone (GLTF/GLB, HTTP, primitives)
- Parent/child entity hierarchies: transform propagation, scene graph
- Entity query/filtering by distance: C++/Rust bridge API
- Entity rendering pipeline: Box, Sphere, Model (GLTF/GLB, HTTP, primitives)
- Color/texture: parsed, stored, logged, downloaded, cached (visual application pending API)
- Debug logging: opt-in, covers entity lifecycle, packets, network
- Test suite: entity parsing, structure, and protocol validation
@@ -376,12 +372,33 @@ make -j$(nproc)
## Future Work
- Color/texture visual application (StardustXR API extension)
- ATP protocol support (Overte asset server)
- More entity types (Text, Light, Zone, etc.)
- See IMPLEMENTATION_COMPLETE.md for priorities
## Future Risks & Priorities (612 Months)
As Starworld evolves, the following risks and challenges will become increasingly important:
- **Performance Scaling:** O(n) transform updates and distance queries will not scale for large scenes. Deep hierarchies and lack of spatial indexing will limit performance.
- **Memory/Data Layout:** Inefficient memory layout or fragmentation from dynamic hierarchies can degrade performance.
- **Concurrency & Synchronization:** Multithreading will require robust synchronization to avoid race conditions and data corruption.
- **Lifecycle & Ownership:** Deletion and reparenting in hierarchies can cause dangling references or orphaned children, especially across FFI boundaries.
- **API Misuse:** Cycles or invalid parent/child states can cause subtle bugs or crashes if not checked.
- **Spatial Query Correctness:** Queries may become stale or incorrect if transforms are not updated atomically.
- **XR Performance:** High frame rates and low latency are critical; entity and query systems must be highly optimized.
- **Security/Authority:** In multi-user/networked scenarios, lack of permissions or rate-limiting could allow abuse.
- **Extensibility:** As more systems (physics, networking, etc.) are added, poor separation of concerns could hinder future growth.
- **Testing/Debugging:** More features and edge cases will make testing and debugging harder without good tools.
### Recommended Priorities
- **Spatial Indexing:** Integrate a spatial data structure (octree, k-d tree, etc.) for fast queries. Update incrementally as entities move.
- **Hierarchy Traversal:** Use dirty flags for transforms and consider breadth-first traversal for deep hierarchies.
- **Lifecycle Invariants:** Prevent cycles, define deletion/reparenting behavior, and add runtime checks.
- **FFI Safety:** Ensure safe memory and ownership across Rust/C++.
- **Concurrency:** Design for thread safety and test with simulated concurrent updates/queries.
- **API Hardening:** Clearly document constraints and provide safe helpers/utilities.
- **XR Profiling:** Benchmark with XR workloads and optimize for frame time and memory.
- **Security:** Add permission models and rate-limiting for entity operations in multi-user mode.
- **Testing & Tooling:** Add deep hierarchy/reparenting tests, fuzzing, and scene graph/query inspectors.
- **Architecture:** Plan for modularity and extensibility to support future systems cleanly.
---

View File

@@ -1,12 +1,3 @@
#include <array>
std::vector<StardustBridge::NodeId> StardustBridge::queryNodesByDistance(const glm::vec3& center, float radius, size_t maxCount) {
std::vector<NodeId> result;
if (!m_fnQueryNodesByDistance) return result;
std::vector<std::uint64_t> ids(maxCount);
std::size_t found = m_fnQueryNodesByDistance(center.x, center.y, center.z, radius, ids.data(), maxCount);
result.assign(ids.begin(), ids.begin() + found);
return result;
}
// StardustBridge.cpp
#include "StardustBridge.hpp"
#include "ModelCache.hpp"
@@ -169,8 +160,7 @@ StardustBridge::NodeId StardustBridge::createNode(const std::string& name,
float m[16];
// GLM mat4 is column-major; pass as 16 floats as-is
std::memcpy(m, &transform[0][0], sizeof(m));
std::uint64_t parentVal = parent.has_value() ? *parent : 0;
std::uint64_t rid = m_fnCreateNode(name.c_str(), m, parentVal);
std::uint64_t rid = m_fnCreateNode(name.c_str(), m);
(void)rid; // Could map to NodeId if Rust returns its own id
}
return id;
@@ -389,8 +379,7 @@ bool StardustBridge::loadBridge() {
m_fnSetTexture = reinterpret_cast<fn_set_texture_t>(req("sdxr_set_node_texture"));
m_fnSetColor = reinterpret_cast<fn_set_color_t>(req("sdxr_set_node_color"));
m_fnSetDimensions = reinterpret_cast<fn_set_dimensions_t>(req("sdxr_set_node_dimensions"));
m_fnQueryNodesByDistance = reinterpret_cast<fn_query_nodes_by_distance_t>(req("sdxr_query_nodes_by_distance"));
m_fnSetEntityType = reinterpret_cast<fn_set_entity_type_t>(req("sdxr_set_node_entity_type"));
m_fnSetEntityType = reinterpret_cast<fn_set_entity_type_t>(req("sdxr_set_node_entity_type"));
if (m_fnStart && m_fnPoll && m_fnCreateNode && m_fnUpdateNode) {
m_bridgeHandle = h;
std::cout << "[StardustBridge] Loaded Rust bridge: " << path << std::endl;

View File

@@ -1,5 +1,3 @@
// Query node IDs within a distance of a point
std::vector<NodeId> queryNodesByDistance(const glm::vec3& center, float radius, size_t maxCount = 128);
// StardustBridge.hpp
#pragma once
@@ -85,14 +83,13 @@ private:
using fn_start_t = int(*)(const char*);
using fn_poll_t = int(*)();
using fn_shutdown_t = void(*)();
using fn_create_node_t = std::uint64_t(*)(const char*, const float*, std::uint64_t);
using fn_create_node_t = std::uint64_t(*)(const char*, const float*);
using fn_update_node_t = int(*)(std::uint64_t, const float*);
using fn_remove_node_t = int(*)(std::uint64_t);
using fn_set_model_t = int(*)(std::uint64_t, const char*);
using fn_set_texture_t = int(*)(std::uint64_t, const char*);
using fn_set_color_t = int(*)(std::uint64_t, float, float, float, float);
using fn_set_dimensions_t = int(*)(std::uint64_t, float, float, float);
using fn_query_nodes_by_distance_t = std::size_t(*)(float, float, float, float, std::uint64_t*, std::size_t);
using fn_set_entity_type_t = int(*)(std::uint64_t, std::uint8_t);
fn_start_t m_fnStart{nullptr};
@@ -105,7 +102,6 @@ private:
fn_set_texture_t m_fnSetTexture{nullptr};
fn_set_color_t m_fnSetColor{nullptr};
fn_set_dimensions_t m_fnSetDimensions{nullptr};
fn_query_nodes_by_distance_t m_fnQueryNodesByDistance{nullptr};
fn_set_entity_type_t m_fnSetEntityType{nullptr};
bool loadBridge();