Compare commits
18 Commits
5dd99a1195
...
d42f24274b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d42f24274b | ||
|
|
026b33fb41 | ||
|
|
4d63f37421 | ||
|
|
ab56fdeb21 | ||
|
|
6a89f890b5 | ||
|
|
b925a1ae90 | ||
|
|
ce667bad67 | ||
|
|
662ad1bbd8 | ||
|
|
d4ca24fd0b | ||
|
|
142dd5fc9c | ||
|
|
0e0bee57dc | ||
|
|
973ff95de8 | ||
|
|
62928c2fd6 | ||
|
|
c01c299d8f | ||
|
|
dd19ff6c77 | ||
|
|
4214c47ea2 | ||
|
|
a232d3dbc1 | ||
|
|
49ea26e269 |
109
bridge/Cargo.lock
generated
109
bridge/Cargo.lock
generated
@@ -776,7 +776,16 @@ version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d"
|
||||
dependencies = [
|
||||
"dirs-sys",
|
||||
"dirs-sys 0.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs"
|
||||
version = "5.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225"
|
||||
dependencies = [
|
||||
"dirs-sys 0.4.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -785,7 +794,19 @@ version = "6.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
|
||||
dependencies = [
|
||||
"dirs-sys",
|
||||
"dirs-sys 0.5.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dirs-sys"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"option-ext",
|
||||
"redox_users 0.4.6",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -796,7 +817,7 @@ checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"option-ext",
|
||||
"redox_users",
|
||||
"redox_users 0.5.2",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
@@ -1738,7 +1759,7 @@ version = "0.50.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
|
||||
dependencies = [
|
||||
"windows-sys 0.60.2",
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -2148,6 +2169,17 @@ dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
|
||||
dependencies = [
|
||||
"getrandom 0.2.16",
|
||||
"libredox",
|
||||
"thiserror 1.0.69",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.5.2"
|
||||
@@ -2403,7 +2435,7 @@ version = "0.45.0"
|
||||
source = "git+https://github.com/StardustXR/core.git?branch=dev#5176d1e9d26f1e8a4bd774519f3e7d1fddc121a9"
|
||||
dependencies = [
|
||||
"color-eyre",
|
||||
"dirs",
|
||||
"dirs 6.0.0",
|
||||
"global_counter",
|
||||
"mint",
|
||||
"nix 0.27.1",
|
||||
@@ -2513,6 +2545,7 @@ dependencies = [
|
||||
name = "stardust_bridge"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"dirs 5.0.1",
|
||||
"glam 0.28.0",
|
||||
"lazy_static",
|
||||
"serde",
|
||||
@@ -3193,6 +3226,15 @@ version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.48.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
|
||||
dependencies = [
|
||||
"windows-targets 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-sys"
|
||||
version = "0.52.0"
|
||||
@@ -3220,6 +3262,21 @@ dependencies = [
|
||||
"windows-link",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c"
|
||||
dependencies = [
|
||||
"windows_aarch64_gnullvm 0.48.5",
|
||||
"windows_aarch64_msvc 0.48.5",
|
||||
"windows_i686_gnu 0.48.5",
|
||||
"windows_i686_msvc 0.48.5",
|
||||
"windows_x86_64_gnu 0.48.5",
|
||||
"windows_x86_64_gnullvm 0.48.5",
|
||||
"windows_x86_64_msvc 0.48.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows-targets"
|
||||
version = "0.52.6"
|
||||
@@ -3253,6 +3310,12 @@ dependencies = [
|
||||
"windows_x86_64_msvc 0.53.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_gnullvm"
|
||||
version = "0.52.6"
|
||||
@@ -3265,6 +3328,12 @@ version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_aarch64_msvc"
|
||||
version = "0.52.6"
|
||||
@@ -3277,6 +3346,12 @@ version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_gnu"
|
||||
version = "0.52.6"
|
||||
@@ -3301,6 +3376,12 @@ version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406"
|
||||
|
||||
[[package]]
|
||||
name = "windows_i686_msvc"
|
||||
version = "0.52.6"
|
||||
@@ -3313,6 +3394,12 @@ version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnu"
|
||||
version = "0.52.6"
|
||||
@@ -3325,6 +3412,12 @@ version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_gnullvm"
|
||||
version = "0.52.6"
|
||||
@@ -3337,6 +3430,12 @@ version = "0.53.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.48.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538"
|
||||
|
||||
[[package]]
|
||||
name = "windows_x86_64_msvc"
|
||||
version = "0.52.6"
|
||||
|
||||
@@ -14,6 +14,7 @@ lazy_static = "1.4"
|
||||
zbus = { version = "5.5.0", features = ["tokio"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
|
||||
dirs = "5.0"
|
||||
|
||||
[dependencies.stardust-xr-asteroids]
|
||||
git = "https://github.com/StardustXR/asteroids.git"
|
||||
|
||||
@@ -10,14 +10,15 @@ use glam::Mat4;
|
||||
use stardust_xr_asteroids as ast; // alias for brevity
|
||||
use stardust_xr_asteroids::{
|
||||
client::ClientState,
|
||||
elements::{PlaySpace, Spatial, Lines},
|
||||
Migrate, Reify, CustomElement, Projector, Context,
|
||||
elements::{PlaySpace, Spatial, Model},
|
||||
Migrate, Reify, CustomElement, Projector, Context, Transformable,
|
||||
};
|
||||
use stardust_xr_molecules::accent_color::AccentColor;
|
||||
use stardust_xr_fusion::objects::connect_client as fusion_connect_client;
|
||||
use stardust_xr_fusion::node::NodeType;
|
||||
use stardust_xr_fusion::root::RootAspect;
|
||||
use tokio::runtime::Runtime;
|
||||
use std::path::PathBuf;
|
||||
|
||||
#[derive(Clone, serde::Serialize, serde::Deserialize)]
|
||||
struct BridgeState {
|
||||
@@ -69,89 +70,25 @@ impl ClientState for BridgeState {
|
||||
|
||||
impl Reify for BridgeState {
|
||||
fn reify(&self) -> impl ast::Element<Self> {
|
||||
use stardust_xr_fusion::values::{color, Vector3};
|
||||
use stardust_xr_fusion::drawable::{Line, LinePoint};
|
||||
use stardust_xr_fusion::values::color;
|
||||
|
||||
eprintln!("[bridge/reify] Reifying {} nodes", self.nodes.len());
|
||||
|
||||
fn create_wireframe_cube(color_val: stardust_xr_fusion::values::Color, thickness: f32) -> Vec<Line> {
|
||||
let h = 0.5; // half size
|
||||
let points = [
|
||||
[-h, -h, -h], [h, -h, -h], [h, h, -h], [-h, h, -h], // back face
|
||||
[-h, -h, h], [h, -h, h], [h, h, h], [-h, h, h], // front face
|
||||
];
|
||||
|
||||
// 12 edges of the cube
|
||||
let edges = [
|
||||
(0, 1), (1, 2), (2, 3), (3, 0), // back face
|
||||
(4, 5), (5, 6), (6, 7), (7, 4), // front face
|
||||
(0, 4), (1, 5), (2, 6), (3, 7), // connecting edges
|
||||
];
|
||||
|
||||
edges.iter().map(|(a, b)| {
|
||||
let pa = points[*a];
|
||||
let pb = points[*b];
|
||||
Line {
|
||||
points: vec![
|
||||
LinePoint { point: Vector3 { x: pa[0], y: pa[1], z: pa[2] }, thickness, color: color_val },
|
||||
LinePoint { point: Vector3 { x: pb[0], y: pb[1], z: pb[2] }, thickness, color: color_val },
|
||||
],
|
||||
cyclic: false,
|
||||
}
|
||||
}).collect()
|
||||
}
|
||||
|
||||
fn create_wireframe_sphere(color_val: stardust_xr_fusion::values::Color, thickness: f32) -> Vec<Line> {
|
||||
let segments = 32;
|
||||
let r = 0.5; // radius
|
||||
let mut lines = Vec::new();
|
||||
|
||||
// Create 3 orthogonal circles (XY, XZ, YZ planes)
|
||||
for axis in 0..3 {
|
||||
let mut points = Vec::new();
|
||||
for i in 0..=segments {
|
||||
let angle = (i as f32 / segments as f32) * std::f32::consts::TAU;
|
||||
let (sin, cos) = angle.sin_cos();
|
||||
let point = match axis {
|
||||
0 => Vector3 { x: cos * r, y: sin * r, z: 0.0 }, // XY plane
|
||||
1 => Vector3 { x: cos * r, y: 0.0, z: sin * r }, // XZ plane
|
||||
_ => Vector3 { x: 0.0, y: cos * r, z: sin * r }, // YZ plane
|
||||
};
|
||||
points.push(LinePoint { point, thickness, color: color_val });
|
||||
}
|
||||
lines.push(Line { points, cyclic: true });
|
||||
fn get_model_path(entity_type: u8) -> Option<PathBuf> {
|
||||
let cache_dir = dirs::cache_dir()?.join("starworld/primitives");
|
||||
let filename = match entity_type {
|
||||
1 => "cube.glb", // Box
|
||||
2 => "sphere.glb", // Sphere
|
||||
3 => "model.glb", // Model (using Suzanne as placeholder)
|
||||
_ => return None,
|
||||
};
|
||||
let path = cache_dir.join(filename);
|
||||
if path.exists() {
|
||||
Some(path)
|
||||
} else {
|
||||
eprintln!("[bridge/reify] Model file not found: {}", path.display());
|
||||
None
|
||||
}
|
||||
lines
|
||||
}
|
||||
|
||||
fn create_octahedron_wireframe(color_val: stardust_xr_fusion::values::Color, thickness: f32) -> Vec<Line> {
|
||||
let r = 0.5;
|
||||
// 6 vertices of octahedron
|
||||
let verts = [
|
||||
Vector3 { x: 0.0, y: r, z: 0.0 }, // top
|
||||
Vector3 { x: r, y: 0.0, z: 0.0 }, // +X
|
||||
Vector3 { x: 0.0, y: 0.0, z: r }, // +Z
|
||||
Vector3 { x: -r, y: 0.0, z: 0.0 }, // -X
|
||||
Vector3 { x: 0.0, y: 0.0, z: -r }, // -Z
|
||||
Vector3 { x: 0.0, y: -r, z: 0.0 }, // bottom
|
||||
];
|
||||
|
||||
// 12 edges
|
||||
let edges = [
|
||||
(0, 1), (0, 2), (0, 3), (0, 4), // top pyramid
|
||||
(5, 1), (5, 2), (5, 3), (5, 4), // bottom pyramid
|
||||
(1, 2), (2, 3), (3, 4), (4, 1), // equator
|
||||
];
|
||||
|
||||
edges.iter().map(|(a, b)| {
|
||||
Line {
|
||||
points: vec![
|
||||
LinePoint { point: verts[*a], thickness, color: color_val },
|
||||
LinePoint { point: verts[*b], thickness, color: color_val },
|
||||
],
|
||||
cyclic: false,
|
||||
}
|
||||
}).collect()
|
||||
}
|
||||
|
||||
let children = self.nodes.iter().filter_map(|(id, node)| {
|
||||
@@ -163,63 +100,38 @@ impl Reify for BridgeState {
|
||||
|
||||
let (scale, rot, trans) = node.transform.to_scale_rotation_translation();
|
||||
let vis_scale = if dims.length() > 0.001 { dims } else { scale };
|
||||
let node_color = color::rgba_linear!(node.color[0], node.color[1], node.color[2], node.color[3]);
|
||||
|
||||
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);
|
||||
|
||||
// Create appropriate visual based on entity type
|
||||
match node.entity_type {
|
||||
1 => {
|
||||
// Box - use wireframe cube with color
|
||||
eprintln!("[bridge/reify] Creating box (wireframe) for node {}", id);
|
||||
let cube_lines = create_wireframe_cube(node_color, 0.005);
|
||||
Some((*id, Spatial::default()
|
||||
.transform(transform)
|
||||
.build()
|
||||
.child(Lines::new(cube_lines).build())))
|
||||
}
|
||||
2 => {
|
||||
// Sphere - use wireframe sphere with color
|
||||
eprintln!("[bridge/reify] Creating sphere (wireframe) for node {}", id);
|
||||
let sphere_lines = create_wireframe_sphere(node_color, 0.005);
|
||||
Some((*id, Spatial::default()
|
||||
.transform(transform)
|
||||
.build()
|
||||
.child(Lines::new(sphere_lines).build())))
|
||||
}
|
||||
3 => {
|
||||
// Model - attempt to load from URL if provided, fallback to wireframe
|
||||
if !node.model_url.is_empty() {
|
||||
eprintln!("[bridge/reify] Creating model for node {} from URL: {}", id, node.model_url);
|
||||
// For now, we can't easily load arbitrary HTTP URLs in the Overte format
|
||||
// Fall back to wireframe octahedron as a distinct placeholder
|
||||
let oct_lines = create_octahedron_wireframe(node_color, 0.005);
|
||||
Some((*id, Spatial::default()
|
||||
.transform(transform)
|
||||
.build()
|
||||
.child(Lines::new(oct_lines).build())))
|
||||
} else {
|
||||
eprintln!("[bridge/reify] Creating model placeholder for node {} (no URL)", id);
|
||||
let oct_lines = create_octahedron_wireframe(node_color, 0.005);
|
||||
Some((*id, Spatial::default()
|
||||
.transform(transform)
|
||||
.build()
|
||||
.child(Lines::new(oct_lines).build())))
|
||||
// Try to load the appropriate model based on entity type
|
||||
let model_child = if let Some(model_path) = get_model_path(node.entity_type) {
|
||||
eprintln!("[bridge/reify] Loading {} model for node {} from {}",
|
||||
match node.entity_type {
|
||||
1 => "cube",
|
||||
2 => "sphere",
|
||||
3 => "3D model",
|
||||
_ => "unknown"
|
||||
}, id, model_path.display());
|
||||
|
||||
match Model::direct(&model_path) {
|
||||
Ok(model) => Some(model.build()),
|
||||
Err(e) => {
|
||||
eprintln!("[bridge/reify] Failed to load model for node {}: {}", id, e);
|
||||
None
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
// Unknown or unsupported type - render as wireframe cube
|
||||
eprintln!("[bridge/reify] Creating wireframe for unknown node {} type={}", id, node.entity_type);
|
||||
let cube_lines = create_wireframe_cube(node_color, 0.003);
|
||||
Some((*id, Spatial::default()
|
||||
.transform(transform)
|
||||
.build()
|
||||
.child(Lines::new(cube_lines).build())))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
eprintln!("[bridge/reify] No model available for entity type {} (node {})", node.entity_type, id);
|
||||
None
|
||||
};
|
||||
|
||||
Some((*id, Spatial::default()
|
||||
.transform(transform)
|
||||
.build()
|
||||
.maybe_child(model_child)))
|
||||
});
|
||||
|
||||
PlaySpace.build().stable_children(children)
|
||||
|
||||
41
bridge/src/primitives.rs
Normal file
41
bridge/src/primitives.rs
Normal file
@@ -0,0 +1,41 @@
|
||||
use std::path::PathBuf;
|
||||
use std::fs;
|
||||
|
||||
// Embedded GLTF primitives for basic shapes
|
||||
pub mod embedded_models {
|
||||
use super::*;
|
||||
use std::sync::OnceLock;
|
||||
|
||||
static CACHE_DIR: OnceLock<PathBuf> = OnceLock::new();
|
||||
|
||||
pub fn get_cache_dir() -> &'static PathBuf {
|
||||
CACHE_DIR.get_or_init(|| {
|
||||
let dir = dirs::cache_dir()
|
||||
.unwrap_or_else(|| PathBuf::from("/tmp"))
|
||||
.join("starworld/primitives");
|
||||
let _ = std::fs::create_dir_all(&dir);
|
||||
dir
|
||||
})
|
||||
}
|
||||
|
||||
pub fn get_cube_model() -> PathBuf {
|
||||
let path = get_cache_dir().join("cube.glb");
|
||||
if !path.exists() {
|
||||
std::fs::write(&path, CUBE_GLB).expect("Failed to write cube.glb");
|
||||
}
|
||||
path
|
||||
}
|
||||
|
||||
pub fn get_sphere_model() -> PathBuf {
|
||||
let path = get_cache_dir().join("sphere.glb");
|
||||
if !path.exists() {
|
||||
std::fs::write(&path, SPHERE_GLB).expect("Failed to write sphere.glb");
|
||||
}
|
||||
path
|
||||
}
|
||||
|
||||
// Minimal cube GLB (binary GLTF) - this is a placeholder
|
||||
// TODO: Generate proper GLB data or bundle actual primitive files
|
||||
const CUBE_GLB: &[u8] = b""; // Will be filled with actual data
|
||||
const SPHERE_GLB: &[u8] = b""; // Will be filled with actual data
|
||||
}
|
||||
BIN
primitives/cube.glb
Normal file
BIN
primitives/cube.glb
Normal file
Binary file not shown.
BIN
primitives/sphere.glb
Normal file
BIN
primitives/sphere.glb
Normal file
Binary file not shown.
@@ -284,7 +284,14 @@ bool StardustBridge::loadBridge() {
|
||||
const char* overridePath = std::getenv("STARWORLD_BRIDGE_PATH");
|
||||
std::vector<std::string> candidates;
|
||||
if (overridePath) {
|
||||
candidates.emplace_back(std::string(overridePath));
|
||||
std::string pathStr(overridePath);
|
||||
// If it's a directory, append the library filename
|
||||
struct stat st;
|
||||
if (stat(pathStr.c_str(), &st) == 0 && S_ISDIR(st.st_mode)) {
|
||||
candidates.emplace_back(pathStr + "/libstardust_bridge.so");
|
||||
} else {
|
||||
candidates.emplace_back(pathStr);
|
||||
}
|
||||
}
|
||||
// Likely local dev output
|
||||
candidates.emplace_back("./bridge/target/debug/libstardust_bridge.so");
|
||||
|
||||
133
tools/blender_export_primitives.py
Normal file
133
tools/blender_export_primitives.py
Normal file
@@ -0,0 +1,133 @@
|
||||
import bpy
|
||||
import os
|
||||
import traceback
|
||||
|
||||
# Clear existing objects
|
||||
bpy.ops.object.select_all(action='SELECT')
|
||||
bpy.ops.object.delete(use_global=False)
|
||||
|
||||
# Output directory
|
||||
output_dir = os.path.expanduser("~/.cache/starworld/primitives")
|
||||
os.makedirs(output_dir, exist_ok=True)
|
||||
|
||||
def create_material(name, base_color):
|
||||
"""Create a PBR material with the specified base color (RGBA)"""
|
||||
try:
|
||||
mat = bpy.data.materials.new(name=name)
|
||||
mat.use_nodes = True
|
||||
nodes = mat.node_tree.nodes
|
||||
|
||||
# Find the Principled BSDF node (should be created by default)
|
||||
bsdf = None
|
||||
for node in nodes:
|
||||
if node.type == 'BSDF_PRINCIPLED':
|
||||
bsdf = node
|
||||
break
|
||||
|
||||
# If not found, create it
|
||||
if not bsdf:
|
||||
bsdf = nodes.new(type='ShaderNodeBsdfPrincipled')
|
||||
|
||||
# Set material properties
|
||||
bsdf.inputs['Base Color'].default_value = base_color
|
||||
bsdf.inputs['Roughness'].default_value = 0.5
|
||||
bsdf.inputs['Metallic'].default_value = 0.1
|
||||
|
||||
print(f" Created material '{name}' with color {base_color}")
|
||||
return mat
|
||||
except Exception as e:
|
||||
print(f"ERROR creating material: {e}")
|
||||
traceback.print_exc()
|
||||
return None
|
||||
|
||||
try:
|
||||
print("Creating GREEN sphere...")
|
||||
# Create and export UV Sphere with GREEN material
|
||||
bpy.ops.mesh.primitive_uv_sphere_add(segments=32, ring_count=16, radius=0.5, location=(0, 0, 0))
|
||||
sphere = bpy.context.active_object
|
||||
sphere.name = "Sphere"
|
||||
sphere.select_set(True)
|
||||
bpy.context.view_layer.objects.active = sphere
|
||||
bpy.ops.object.shade_smooth()
|
||||
|
||||
# Apply green material
|
||||
green_mat = create_material("SphereMaterial", (0.2, 1.0, 0.2, 1.0)) # Green
|
||||
if green_mat:
|
||||
sphere.data.materials.append(green_mat)
|
||||
|
||||
sphere_path = os.path.join(output_dir, "sphere.glb")
|
||||
print(f" Exporting to {sphere_path}...")
|
||||
bpy.ops.export_scene.gltf(
|
||||
filepath=sphere_path,
|
||||
export_format='GLB',
|
||||
use_selection=True
|
||||
)
|
||||
print(f"✓ Exported sphere.glb (GREEN)")
|
||||
except Exception as e:
|
||||
print(f"ERROR with sphere: {e}")
|
||||
traceback.print_exc()
|
||||
|
||||
try:
|
||||
print("\nCreating RED cube...")
|
||||
# Delete sphere and create cube with RED material
|
||||
bpy.ops.object.delete(use_global=False)
|
||||
bpy.ops.mesh.primitive_cube_add(size=1.0, location=(0, 0, 0))
|
||||
cube = bpy.context.active_object
|
||||
cube.name = "Cube"
|
||||
cube.select_set(True)
|
||||
bpy.context.view_layer.objects.active = cube
|
||||
bpy.ops.object.shade_smooth()
|
||||
|
||||
# Apply red material
|
||||
red_mat = create_material("CubeMaterial", (1.0, 0.2, 0.2, 1.0)) # Red
|
||||
if red_mat:
|
||||
cube.data.materials.append(red_mat)
|
||||
|
||||
cube_path = os.path.join(output_dir, "cube.glb")
|
||||
print(f" Exporting to {cube_path}...")
|
||||
bpy.ops.export_scene.gltf(
|
||||
filepath=cube_path,
|
||||
export_format='GLB',
|
||||
use_selection=True
|
||||
)
|
||||
print(f"✓ Exported cube.glb (RED)")
|
||||
except Exception as e:
|
||||
print(f"ERROR with cube: {e}")
|
||||
traceback.print_exc()
|
||||
|
||||
try:
|
||||
print("\nCreating BLUE icosphere...")
|
||||
# Delete cube and create ico sphere for the "model" placeholder with BLUE material
|
||||
bpy.ops.object.delete(use_global=False)
|
||||
bpy.ops.mesh.primitive_ico_sphere_add(subdivisions=2, radius=0.5, location=(0, 0, 0))
|
||||
ico = bpy.context.active_object
|
||||
ico.name = "IcoSphere"
|
||||
ico.select_set(True)
|
||||
bpy.context.view_layer.objects.active = ico
|
||||
bpy.ops.object.shade_smooth()
|
||||
|
||||
# Apply blue material
|
||||
blue_mat = create_material("IcoSphereMaterial", (0.2, 0.2, 1.0, 1.0)) # Blue
|
||||
if blue_mat:
|
||||
ico.data.materials.append(blue_mat)
|
||||
|
||||
model_path = os.path.join(output_dir, "model.glb")
|
||||
print(f" Exporting to {model_path}...")
|
||||
bpy.ops.export_scene.gltf(
|
||||
filepath=model_path,
|
||||
export_format='GLB',
|
||||
use_selection=True
|
||||
)
|
||||
print(f"✓ Exported model.glb (BLUE)")
|
||||
except Exception as e:
|
||||
print(f"ERROR with icosphere: {e}")
|
||||
traceback.print_exc()
|
||||
|
||||
print("\n" + "="*60)
|
||||
print("✓ EXPORT COMPLETE!")
|
||||
print("="*60)
|
||||
print(f"Output directory: {output_dir}")
|
||||
print(" - cube.glb (RED)")
|
||||
print(" - sphere.glb (GREEN)")
|
||||
print(" - model.glb (BLUE)")
|
||||
print("="*60)
|
||||
232
tools/generate_primitives.py
Executable file
232
tools/generate_primitives.py
Executable file
@@ -0,0 +1,232 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Generate simple GLTF primitive models for Starworld entity rendering.
|
||||
Requires: pip install pygltflib numpy
|
||||
"""
|
||||
|
||||
import numpy as np
|
||||
from pygltflib import *
|
||||
import struct
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
def create_cube_gltf(output_path):
|
||||
"""Create a 1x1x1 cube centered at origin"""
|
||||
|
||||
# Cube vertices (8 vertices)
|
||||
vertices = np.array([
|
||||
[-0.5, -0.5, -0.5], # 0
|
||||
[ 0.5, -0.5, -0.5], # 1
|
||||
[ 0.5, 0.5, -0.5], # 2
|
||||
[-0.5, 0.5, -0.5], # 3
|
||||
[-0.5, -0.5, 0.5], # 4
|
||||
[ 0.5, -0.5, 0.5], # 5
|
||||
[ 0.5, 0.5, 0.5], # 6
|
||||
[-0.5, 0.5, 0.5], # 7
|
||||
], dtype=np.float32)
|
||||
|
||||
# Cube indices (12 triangles, 36 indices)
|
||||
indices = np.array([
|
||||
# Front face
|
||||
0, 1, 2, 0, 2, 3,
|
||||
# Back face
|
||||
5, 4, 7, 5, 7, 6,
|
||||
# Top face
|
||||
3, 2, 6, 3, 6, 7,
|
||||
# Bottom face
|
||||
4, 5, 1, 4, 1, 0,
|
||||
# Right face
|
||||
1, 5, 6, 1, 6, 2,
|
||||
# Left face
|
||||
4, 0, 3, 4, 3, 7,
|
||||
], dtype=np.uint16)
|
||||
|
||||
# Normals (per-face normals repeated for each vertex of triangle)
|
||||
normals = np.array([
|
||||
# Front
|
||||
[0, 0, -1], [0, 0, -1], [0, 0, -1], [0, 0, -1], [0, 0, -1], [0, 0, -1],
|
||||
# Back
|
||||
[0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1], [0, 0, 1],
|
||||
# Top
|
||||
[0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0], [0, 1, 0],
|
||||
# Bottom
|
||||
[0, -1, 0], [0, -1, 0], [0, -1, 0], [0, -1, 0], [0, -1, 0], [0, -1, 0],
|
||||
# Right
|
||||
[1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0], [1, 0, 0],
|
||||
# Left
|
||||
[-1, 0, 0], [-1, 0, 0], [-1, 0, 0], [-1, 0, 0], [-1, 0, 0], [-1, 0, 0],
|
||||
], dtype=np.float32)
|
||||
|
||||
# Create binary data
|
||||
vertices_binary = vertices.tobytes()
|
||||
indices_binary = indices.tobytes()
|
||||
|
||||
# Build GLTF
|
||||
gltf = GLTF2(
|
||||
scene=0,
|
||||
scenes=[Scene(nodes=[0])],
|
||||
nodes=[Node(mesh=0)],
|
||||
meshes=[
|
||||
Mesh(primitives=[
|
||||
Primitive(
|
||||
attributes=Attributes(POSITION=0),
|
||||
indices=1,
|
||||
)
|
||||
])
|
||||
],
|
||||
accessors=[
|
||||
Accessor(
|
||||
bufferView=0,
|
||||
componentType=FLOAT,
|
||||
count=len(vertices),
|
||||
type=VEC3,
|
||||
max=vertices.max(axis=0).tolist(),
|
||||
min=vertices.min(axis=0).tolist(),
|
||||
),
|
||||
Accessor(
|
||||
bufferView=1,
|
||||
componentType=UNSIGNED_SHORT,
|
||||
count=len(indices),
|
||||
type=SCALAR,
|
||||
),
|
||||
],
|
||||
bufferViews=[
|
||||
BufferView(
|
||||
buffer=0,
|
||||
byteOffset=0,
|
||||
byteLength=len(vertices_binary),
|
||||
target=ARRAY_BUFFER,
|
||||
),
|
||||
BufferView(
|
||||
buffer=0,
|
||||
byteOffset=len(vertices_binary),
|
||||
byteLength=len(indices_binary),
|
||||
target=ELEMENT_ARRAY_BUFFER,
|
||||
),
|
||||
],
|
||||
buffers=[
|
||||
Buffer(byteLength=len(vertices_binary) + len(indices_binary))
|
||||
],
|
||||
)
|
||||
|
||||
gltf.set_binary_blob(vertices_binary + indices_binary)
|
||||
gltf.save(output_path)
|
||||
print(f"Created cube: {output_path}")
|
||||
|
||||
def create_sphere_gltf(output_path, segments=32, rings=16):
|
||||
"""Create a UV sphere"""
|
||||
|
||||
vertices = []
|
||||
normals = []
|
||||
indices = []
|
||||
|
||||
# Generate vertices
|
||||
for ring in range(rings + 1):
|
||||
theta = ring * np.pi / rings
|
||||
sin_theta = np.sin(theta)
|
||||
cos_theta = np.cos(theta)
|
||||
|
||||
for seg in range(segments + 1):
|
||||
phi = seg * 2 * np.pi / segments
|
||||
sin_phi = np.sin(phi)
|
||||
cos_phi = np.cos(phi)
|
||||
|
||||
x = cos_phi * sin_theta
|
||||
y = cos_theta
|
||||
z = sin_phi * sin_theta
|
||||
|
||||
vertices.append([x * 0.5, y * 0.5, z * 0.5]) # radius 0.5
|
||||
normals.append([x, y, z])
|
||||
|
||||
# Generate indices
|
||||
for ring in range(rings):
|
||||
for seg in range(segments):
|
||||
first = ring * (segments + 1) + seg
|
||||
second = first + segments + 1
|
||||
|
||||
indices.extend([first, second, first + 1])
|
||||
indices.extend([second, second + 1, first + 1])
|
||||
|
||||
vertices = np.array(vertices, dtype=np.float32)
|
||||
normals = np.array(normals, dtype=np.float32)
|
||||
indices = np.array(indices, dtype=np.uint16)
|
||||
|
||||
vertices_binary = vertices.tobytes()
|
||||
normals_binary = normals.tobytes()
|
||||
indices_binary = indices.tobytes()
|
||||
|
||||
gltf = GLTF2(
|
||||
scene=0,
|
||||
scenes=[Scene(nodes=[0])],
|
||||
nodes=[Node(mesh=0)],
|
||||
meshes=[
|
||||
Mesh(primitives=[
|
||||
Primitive(
|
||||
attributes=Attributes(POSITION=0, NORMAL=1),
|
||||
indices=2,
|
||||
)
|
||||
])
|
||||
],
|
||||
accessors=[
|
||||
Accessor(
|
||||
bufferView=0,
|
||||
componentType=FLOAT,
|
||||
count=len(vertices),
|
||||
type=VEC3,
|
||||
max=vertices.max(axis=0).tolist(),
|
||||
min=vertices.min(axis=0).tolist(),
|
||||
),
|
||||
Accessor(
|
||||
bufferView=1,
|
||||
componentType=FLOAT,
|
||||
count=len(normals),
|
||||
type=VEC3,
|
||||
),
|
||||
Accessor(
|
||||
bufferView=2,
|
||||
componentType=UNSIGNED_SHORT,
|
||||
count=len(indices),
|
||||
type=SCALAR,
|
||||
),
|
||||
],
|
||||
bufferViews=[
|
||||
BufferView(
|
||||
buffer=0,
|
||||
byteOffset=0,
|
||||
byteLength=len(vertices_binary),
|
||||
target=ARRAY_BUFFER,
|
||||
),
|
||||
BufferView(
|
||||
buffer=0,
|
||||
byteOffset=len(vertices_binary),
|
||||
byteLength=len(normals_binary),
|
||||
target=ARRAY_BUFFER,
|
||||
),
|
||||
BufferView(
|
||||
buffer=0,
|
||||
byteOffset=len(vertices_binary) + len(normals_binary),
|
||||
byteLength=len(indices_binary),
|
||||
target=ELEMENT_ARRAY_BUFFER,
|
||||
),
|
||||
],
|
||||
buffers=[
|
||||
Buffer(byteLength=len(vertices_binary) + len(normals_binary) + len(indices_binary))
|
||||
],
|
||||
)
|
||||
|
||||
gltf.set_binary_blob(vertices_binary + normals_binary + indices_binary)
|
||||
gltf.save(output_path)
|
||||
print(f"Created sphere: {output_path}")
|
||||
|
||||
if __name__ == "__main__":
|
||||
# Create output directory
|
||||
cache_dir = Path.home() / ".cache" / "starworld" / "primitives"
|
||||
cache_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# Generate primitives
|
||||
create_cube_gltf(str(cache_dir / "cube.glb"))
|
||||
create_sphere_gltf(str(cache_dir / "sphere.glb"))
|
||||
|
||||
print(f"\n✓ Primitive models generated in: {cache_dir}")
|
||||
print(f" - cube.glb")
|
||||
print(f" - sphere.glb")
|
||||
12
tools/gltf_primitives/Cargo.toml
Normal file
12
tools/gltf_primitives/Cargo.toml
Normal file
@@ -0,0 +1,12 @@
|
||||
[package]
|
||||
name = "gltf_primitives"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[[bin]]
|
||||
name = "generate_primitives"
|
||||
path = "generate.rs"
|
||||
|
||||
[dependencies]
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0"
|
||||
Reference in New Issue
Block a user