Fix extended-region scene rendering and delivery
Some checks failed
CI / rust-skia (Rust only) (push) Has been cancelled
CI / required (push) Has been cancelled

This commit is contained in:
2026-08-22 13:45:31 +02:00
parent c101007362
commit f6f5abe464
14 changed files with 1349 additions and 533 deletions

View File

@@ -94,6 +94,7 @@ impl OfflineGridBackend {
#[cfg(feature = "live-grid")]
pub struct LibremetaverseClientOwner {
client: libremetaverse::GridClient,
network: libremetaverse::NetworkManager,
agent: Arc<libremetaverse::AgentManager>,
delivery_generation: Arc<std::sync::RwLock<Option<u64>>>,
}
@@ -132,6 +133,7 @@ impl fmt::Debug for LibremetaverseClientOwner {
pub struct LibremetaverseSessionBackend {
network: libremetaverse::NetworkManager,
agent: Arc<libremetaverse::AgentManager>,
throttle: libremetaverse::AgentThrottle,
login_url: String,
first_name: String,
last_name: String,
@@ -168,7 +170,11 @@ impl LibremetaverseClientOwner {
.map_err(|_| BackendError::Configuration {
component: "libremetaverse client defaults",
})?;
// The compatibility client caches its network manager weakly. Retain the
// composition root before managers register packet subscriptions on it.
let network = client.network();
let _grid = client.grid();
let _terrain = client.terrain();
let agent = Arc::new(
libremetaverse::AgentManager::new(Some(Arc::new(client.clone()))).map_err(|_| {
BackendError::Configuration {
@@ -178,6 +184,7 @@ impl LibremetaverseClientOwner {
);
Ok(Self {
client,
network,
agent,
delivery_generation: Arc::new(std::sync::RwLock::new(None)),
})
@@ -273,8 +280,12 @@ impl LibremetaverseClientOwner {
});
}
Ok(LibremetaverseSessionBackend {
network: self.client.network(),
network: self.network.clone(),
agent: Arc::clone(&self.agent),
throttle: libremetaverse::AgentThrottle::new_with_grid_client(self.client.clone())
.map_err(|_| BackendError::Configuration {
component: "libremetaverse agent throttle",
})?,
login_url: connection.login_url.expose_url().to_owned(),
first_name: first_name.to_owned(),
last_name: last_name.to_owned(),
@@ -794,30 +805,7 @@ impl crate::behavior::EmbodimentSink for LibremetaverseEmbodimentSink {
return Err(crate::behavior::BehaviorError::Cancelled);
}
self.ensure_generation(generation)?;
let avatar = self.agent.sim_position();
let forward = libremetaverse_types::Vector3::mul_with_vector3_quaternion(
libremetaverse_types::Vector3::unit_x(),
self.agent.sim_rotation(),
);
let position = libremetaverse_types::Vector3 {
x: avatar.x,
y: avatar.y,
z: avatar.z + 1.6,
};
let target = libremetaverse_types::Vector3 {
x: position.x + forward.x * 4.0,
y: position.y + forward.y * 4.0,
z: position.z + forward.z * 4.0,
};
self.agent
.movement
.camera
.look_at_with_vector3_vector3(position, target)
.and_then(|()| {
self.agent
.movement
.set_fov_vertical_angle(60.0_f32.to_radians())
})
native_reset_camera(&self.agent)
.and_then(|()| self.agent.movement.send_update_with_boolean(Some(true)))
.map_err(|_| crate::behavior::BehaviorError::NativeOperation)
})
@@ -847,6 +835,30 @@ fn native_position(value: libremetaverse_types::Vector3) -> crate::perception::W
}
}
#[cfg(feature = "live-grid")]
fn native_reset_camera(agent: &libremetaverse::AgentManager) -> Result<(), libremetaverse::Error> {
let avatar = agent.sim_position();
let forward = libremetaverse_types::Vector3::mul_with_vector3_quaternion(
libremetaverse_types::Vector3::unit_x(),
agent.sim_rotation(),
);
let position = libremetaverse_types::Vector3 {
x: avatar.x,
y: avatar.y,
z: avatar.z + 1.6,
};
let target = libremetaverse_types::Vector3 {
x: position.x + forward.x * 4.0,
y: position.y + forward.y * 4.0,
z: position.z + forward.z * 4.0,
};
agent
.movement
.camera
.look_at_with_vector3_vector3(position, target)?;
agent.movement.set_fov_vertical_angle(60.0_f32.to_radians())
}
#[cfg(feature = "live-grid")]
fn native_inventory_metadata(
client: &libremetaverse::GridClient,
@@ -1089,6 +1101,23 @@ impl crate::session::GridSessionBackend for LibremetaverseSessionBackend {
)
})?;
if logged_in {
let simulator = self.network.current_sim().ok_or_else(|| {
crate::session::SessionFailure::new(
crate::session::SessionFailureKind::TransientTransport,
)
})?;
self.throttle
.set_with_simulator(Some(simulator))
.map_err(|_| {
crate::session::SessionFailure::new(
crate::session::SessionFailureKind::TransientTransport,
)
})?;
native_reset_camera(&self.agent).map_err(|_| {
crate::session::SessionFailure::new(
crate::session::SessionFailureKind::TransientTransport,
)
})?;
break;
}
let error_key = self.network.native_login_error_key();

File diff suppressed because it is too large Load Diff

View File

@@ -35,9 +35,76 @@ fn uuid(value: u32) -> UUID {
fn triangle(z: f32, color: [u8; 4]) -> SceneTriangle {
SceneTriangle {
vertices: [[-1.0, -1.0, z], [1.0, -1.0, z], [0.0, 1.0, z]],
color_srgb: color,
colors_srgb: [color; 3],
}
}
#[cfg(feature = "live-grid")]
#[test]
fn extended_terrain_layout_uses_received_patch_coordinates() {
let mut terrain = (0..65_536)
.map(|_| libremetaverse::TerrainPatch {
data: Vec::new(),
x: 0,
y: 0,
})
.collect::<Vec<_>>();
terrain[33 * 256 + 42] = libremetaverse::TerrainPatch {
data: vec![20.0; 256],
x: 42,
y: 33,
};
assert_eq!(terrain_layout(&terrain, (256.0, 256.0)), (688, 544, 256));
assert_eq!(terrain_height(&terrain, 256, 675, 538), Some(20.0));
}
#[cfg(feature = "live-grid")]
#[test]
fn renderer_uses_the_camera_axis_that_points_at_the_requested_target() {
let camera = libremetaverse::AgentManagerAgentMovementAgentCamera::new().unwrap();
camera
.look_at_with_vector3_vector3(
libremetaverse_types::Vector3::zero(),
libremetaverse_types::Vector3::unit_y(),
)
.unwrap();
assert_eq!(
native_camera_forward(&camera),
libremetaverse_types::Vector3::unit_y()
);
}
#[cfg(feature = "live-grid")]
#[test]
fn scene_faces_are_shaded_without_changing_alpha() {
let shade = triangle_shade([[0.0, 0.0, 0.0], [1.0, 0.0, 0.0], [0.0, 1.0, 0.0]]);
let color = shade_color([200, 160, 120, 77], shade);
assert!(color[0] < 200);
assert_eq!(color[3], 77);
}
#[cfg(feature = "live-grid")]
#[test]
fn scene_broad_phase_rejects_object_centers_behind_the_camera() {
assert!(scene_center_in_front(
libremetaverse_types::Vector3 {
x: 4.0,
y: 0.0,
z: 0.0
},
[0.0; 3],
[1.0, 0.0, 0.0]
));
assert!(!scene_center_in_front(
libremetaverse_types::Vector3 {
x: -4.0,
y: 0.0,
z: 0.0
},
[0.0; 3],
[1.0, 0.0, 0.0]
));
}
fn scene() -> SceneSnapshot {
SceneSnapshot {
generation: 7,
@@ -123,7 +190,7 @@ async fn golden_scene_is_deterministic_depth_ordered_and_privacy_marked() {
#[cfg(feature = "live-grid")]
#[tokio::test(flavor = "multi_thread")]
async fn offscreen_wgpu_renders_depth_ordered_scene_when_an_adapter_is_available() {
let Ok(renderer) = WgpuRenderer::new().await else {
let Ok(renderer) = metacrate_rendering_wgpu::Renderer::new().await else {
return;
};
let limits = VisionLimits {
@@ -132,10 +199,28 @@ async fn offscreen_wgpu_renders_depth_ordered_scene_when_an_adapter_is_available
minimum_interval: std::time::Duration::ZERO,
..VisionLimits::default()
};
let rgba = tokio::task::spawn_blocking(move || renderer.render(&scene(), limits))
.await
.expect("render worker")
.expect("offscreen render");
let scene = scene();
let triangles = scene
.entities
.iter()
.flat_map(|entity| entity.triangles.iter().cloned())
.collect::<Vec<_>>();
let rgba = tokio::task::spawn_blocking(move || {
renderer.render(
scene.camera,
&triangles,
[72, 96, 120, 255],
metacrate_rendering_wgpu::RenderLimits {
width: limits.width,
height: limits.height,
max_triangles: limits.max_triangles,
far_distance: 512,
},
)
})
.await
.expect("render worker")
.expect("offscreen render");
assert_eq!(rgba.len(), 64 * 64 * 4);
let center = (32 * 64 + 32) * 4;
assert!(rgba[center + 1] > 180 && rgba[center] < 80 && rgba[center + 2] < 80);