IN THIS ARTICLE
Extending the Terrain System
The terrain system is designed to be highly decoupled and easily extensible at a number of different layers.
Gradient components are the underlying data source for height data and surface weight data. New gradient component types can be created as needed to provide data from different sources, such as streaming satellite data, or to modify existing gradient data, such as adding complex erosion filters.
New gradient components need to support the following at a minimum:
- Implement the
GradientRequestBusto generically return a floating-point output value for an input world position.
- Declare that it implements the
- Support thread safety. Ideally, this will mean using a
shared_lockin the query APIs and a
unique_lockfor any places that change the underlying data used in the query API methods. This will let multiple queries run in parallel while safely blocking on data changes.
DependencyNotificationBus::Events::OnCompositionRegionChangedwhenever any data within the gradient changes. This notifies everything “above” the gradient that it will need to refresh itself.
In addition, new gradient components should also support the following:
- Use the Shape components to define how the gradient data maps into world space.
- Support the Gradient Transform Modifier component for performing arbitrary transforms to the gradient data.
- By convention, all gradients currently listen to
OnEntityVisibilityChangedand use that to enable or disable the gradient data in the Editor. This makes it easy to turn gradients on and off while authoring, though it’s important to recognize that they are still active at runtime regardless of the authoring visibility setting.
Each of the terrain components can be replaced with new components that meet the same requirements. For example, a new component that streams down real-world satellite height data can replace the Terrain Height Gradient List. It’s also possible to combine components together, such as a simplified Terrain Spawner component that implements the functionality of the Terrain Layer Spawner, Terrain Height Gradient List, and Terrain Surface Gradient List in one single component.
Any replacement components need to support the following:
- Implement the request bus that matches the component being replaced. For example,
- Declare that it implements each service that matches the component being replaced in
GetProvidedServices. For example,
- Support thread safety.
OnCompositionRegionChangedwhenever any data within the component changes.
- Call the appropriate APIs on the
TerrainSystemServiceRequestBusto register/unregister/refresh a terrain region.
The terrain physics support is provided through a divided component system. One half is the
TerrainPhysicsCollider, which knows about the generic
TerrainDataRequestBus for terrain data and the generic
HeightfieldProvider buses for physics communication. The other half is the
PhysXHeightfieldCollider, which knows about PhysX and heightfields, but not about terrain. To integrate terrain with a new physics system, a new
HeightfieldCollider component needs to be implemented to replace the PhysX version that works with the
HeightfieldProvider buses and with the new physics system.
The entire terrain renderer can be replaced by creating new rendering level components, rendering entity components, feature processors, and shaders. Replacing the renderer doesn’t affect the base terrain system or the terrain physics integrations.
The underlying terrain system can be replaced entirely as well, as long as it implements the
TerrainSystemServiceRequestBus, and it uses the
TerrainDataNotificationBus to send change notifications. There’s no obvious reason to replace the terrain system itself in isolation though, as it mostly just routes data requests and notifications.