I've found a simple way to create UVs for my ceiling geometry, built from Bezier triangles.
For each triangle I compute a LookAt matrix with the normal of the triangle as the view direction. For the up vector I use one edge of the triangle. The UV coordinates can then be calculated by applying this matrix to each sampled points and discarding the Z component.
let mut ps = [
self.room.surface_point(*face, 0.0, 0.0, 1.0),
self.room.surface_point(*face, 1.0, 0.0, 0.0),
self.room.surface_point(*face, 0.0, 1.0, 0.0),
];
ps.sort_by(|x, y| x.y.partial_cmp(&y.y).unwrap());
let v1 = ps[1] - ps[0];
let v2 = ps[2] - ps[0];
let n1 = v1.cross(&v2);
let lt = Matrix3::look_to_rh(n1.normalize().into(), v1.normalize().into());