rafx-visibility is a low-level crate used by rafx-framework for providing a VisibilityWorld. A VisibilityWorld is like a physics world – it’s a retained API that maintains a shadow state of the application’s game world. The API encapsulates the game code from changes to the visibility’s code algorithms or data structures. As new features are added to rafx-visibility like occlusion culling or hierarchical cull structures for large game scenes, those features will become available transparently to the applications relying on rafx-visibility.
The public API to rafx-visibility is contained in src/* and src/geometry/*.
The main entry point is the VisibilityWorldArc. The application creates VisibilityWorldArc and registers new Objects, Models, Zones, Volumes, and View Frustums with it to mirror their game’s scene. Additionally, the application can use a channel to send asynchronous commands to the VisibilityWorldArc in a thread-safe manner that can be processed during the next update of the VisibilityWorld.
The VisibilityWorld uses handles to avoid bleeding internal design into the application.
ViewFrustumHandle represents a view in the application, like the main camera, a mini-map, or a light source needed for shadow casting. It has a specific Projection. The supported Projection types are Orthogonal or Perspective.VisibilityObjectHandle represents a game entity in the application. It is associated with a Model for culling and placed into a Zone with View Frustums.ZoneHandle is used to group Objects and View Frustums together. View Frustums are only able to “see” Objects in the same Zone. Objects and View Frustums may not be in multiple zones simultaneously. This is similar to a layer in a physics API. In a future release, the intent is for Zones to be the way to connect easily segmented levels with Portals. [1]ModelHandle represents the visibility bounds for a game entity in the application. Internally, it’s represented by one of several possible bounding structures. In a physics API, this is like defining the collider for an entity in the physics world.VolumeHandle is like a Model, but instead of being reduced down to the bounding structure, it maintains a well-defined geometric shape like a cone, sphere, capsule, or other supported 3-dimensional shape. Unlike a Model, a Volume is not associated with an VisibilityObjectHandle. It is placed into a Zone and positioned independently. Volumes are included in the list of visibility results from a View Frustum when the Volume intersects with that View Frustum. Volumes are currently not implemented. In the future, Volumes will be the primary way for the application to determine the visibility of light sources in a view for the purpose of culling unseen lights. [2]The public API also includes a VisibleBounds struct for processing an arbitrary PolygonSoup provided by the application into the VisibilityWorld’s internal bounding structures. VisibilityBounds may be computed offline and provided to the VisibilityWorld during loading for initializing Models. The reason for preferring an internal structure is that the VisibilityWorld will need to simplify application models into simpler OccluderBounds in the future when occlusion culling is added.
The VisibilityWorldArc defines the query_visibility function. This function is thread-safe.
pub fn query_visibility(
&self,
view_frustum: ViewFrustumHandle,
result: &mut VisibilityQuery,
) -> Result<(), QueryError>
The result of a VisibilityQuery is a Vec of VisibilityResult<T> where T is either an VisibilityObjectHandle or a VolumeHandle. Each VisibilityResult contains the following information:
pub struct VisibilityResult<T> {
pub handle: T,
pub id: u64,
pub bounding_sphere: BoundingSphere,
pub distance_from_view_frustum: f32,
}
handle is the VisibilityObjectHandle or VolumeHandle visible to the ViewFrustumHandle during the query.id is an application-provided value set & retained on each VisibilityObjectHandle or VolumeHandle with the set_object_id and set_volume_id functions. The id is intended to be used by the application as a key back to their game entity, e.g. a ptr, or an Entity ID in an ECS, or a key in some type of Map.bounding_sphere and distance_from_view_frustum are provided to support applications maintaining a level-of-detail budget. [3]src/internal/* defines the data structures and algorithm for thread-safe frustum culling.src/frustum_culling/* contains an SIMD algorithm for culling bounded spheres quickly and associated data structure – the PackedBoundingSphere and PackedBoundingSphereChunk. [4][1] https://en.wikipedia.org/wiki/Portal_rendering
[2] Lengyel, Eric. (2019). Foundations of Game Engine Development: Vol 2. Rendering.
[3] http://advances.realtimerendering.com/destiny/gdc_2015/Tatarchuk_GDC_2015__Destiny_Renderer_web.pdf
[4] https://www.ea.com/frostbite/news/culling-the-battlefield-data-oriented-design-in-practice