fix(terrain): preserve zero-area welded foundation faces
Docs Deploy / Build and Deploy MkDocs (push) Successful in 32s

This commit is contained in:
Valentin Popov committed 2026-10-18 11:34:48 +04:00
1 parent 5a48d0b266
commit f6586eed49
1 file changed
+58 -3
+58 -3
View File
@@ -341,7 +341,7 @@ impl TerrainWorld {
let mut surfaces = Vec::with_capacity(foundation.fragments.len());
let mut generated = 0_usize;
for (fragment_index, fragment) in foundation.fragments.iter().enumerate() {
if !valid_foundation_triangle(fragment.positions) {
if !valid_foundation_triangle(fragment.positions, fragment.source_face.is_none()) {
return Err(TerrainError::InvalidFoundationFragment {
fragment: fragment_index,
});
@@ -1031,7 +1031,7 @@ fn build_surfaces(mesh: &LandMeshDocument) -> Result<Vec<RuntimeTriangle>, Terra
Ok(triangles)
}
fn valid_foundation_triangle(vertices: [[f32; 3]; 3]) -> bool {
fn valid_foundation_triangle(vertices: [[f32; 3]; 3], allow_zero_xy_area: bool) -> bool {
if !vertices.iter().flatten().all(|value| value.is_finite()) {
return false;
}
@@ -1043,7 +1043,9 @@ fn valid_foundation_triangle(vertices: [[f32; 3]; 3]) -> bool {
f64::from(vertices[2][0]) - f64::from(vertices[0][0]),
f64::from(vertices[2][1]) - f64::from(vertices[0][1]),
];
first[0] * second[1] - first[1] * second[0] != 0.0
let cross = first[0] * second[1] - first[1] * second[0];
// Native vertex welding can collapse generated band faces; query math naturally misses zero area.
cross != 0.0 || allow_zero_xy_area
}
fn surface_vertex(
@@ -1364,6 +1366,54 @@ mod tests {
.is_some());
}
#[test]
fn foundation_install_keeps_finite_welded_zero_area_band_for_queries() {
let mesh = synthetic_land_mesh();
let positions = [
[1644.398, 1118.5295, 132.54382],
[1644.4183, 1118.5342, 132.54755],
[1644.4183, 1118.5342, 132.54755],
];
let foundation = BasementFoundation {
fragments: vec![BasementFoundationFragment {
source_face: None,
surface_mask: FullSurfaceMask(0x8300),
positions,
}],
placements: Vec::new(),
};
let world = TerrainWorld::from_land_msh(&mesh)
.expect("source world")
.with_basement_foundation(foundation)
.expect("finite generated fragment retained after native vertex welding");
assert_eq!(world.surface_count(), 1);
assert_eq!(
world
.basement_foundation()
.expect("installed foundation")
.fragments[0]
.positions,
positions
);
assert_eq!(
world
.height_at([positions[1][0], positions[1][1]])
.expect("degenerate height query"),
None
);
assert_eq!(
world
.raycast(
[positions[1][0], positions[1][1], 200.0],
[0.0, 0.0, -1.0],
FullSurfaceMask(0),
)
.expect("degenerate ray query"),
None
);
}
#[test]
fn foundation_install_rejects_invalid_source_identity_masks_and_generated_flags() {
let mesh = synthetic_land_mesh();
@@ -1385,6 +1435,11 @@ mod tests {
surface_mask: FullSurfaceMask(0x0300),
positions: generated_triangle,
},
BasementFoundationFragment {
source_face: None,
surface_mask: FullSurfaceMask(0x8300),
positions: [[0.0, 0.0, 0.0], [0.25, 0.0, 0.0], [0.0, f32::NAN, 0.0]],
},
];
for (index, fragment) in invalid.into_iter().enumerate() {