Status: Mostly implemented.
Space Views visualize a Data Blueprint, i.e. a set of entities with given properties. They are represented as freely arrangeable tiles in the Viewport. Most Space Views are interactive, allowing their data to be explored freely.
All properties are saved as part of the blueprint.
Changing discards Space View State:
- Space View Class
- root entity path
Freely mutable:
- name
- positioning within layout
- class specific properties
- Data Blueprint
- root of the transform hierarchy (if any is used)
- may govern heuristics
- available at various stages of UI drawing & system execution build-up (see below)
In addition to blueprint stored data, a space view has a class specific SpaceViewState
which stored ephemeral state that is not persisted as part of the blueprint.
This is typically used for animation/transition state.
Each Space View refers to an immutable Space View Class, implemented by SpaceViewClass
.
It defines:
- which data it can display and how it is displayed
- how it is interacted with
- what properties are read from the blueprint store and how they are exposed in the UI
Space View differ only in class when they are fundamentally different in the way they display data. Naturally, this means that there are only ever very few distinct Space View classes.
As of writing we have:
- Spatial
- Bar Chart
- Tensor
- Text
- Text Document
- Time Series
The fundamental difference between different space views lies in the kinds of axes a view has.
- Data Table (currently text views) have rows and columns with text
- Text Log have rows with logs sorted in time (not 100% sure this is fundamentally different than Data Table)
- Spatial 2D has two orthogonal axes with defined spatial relationships
- Spatial 3D has three orthogonal axes with defined spatial relationships
- Time Series has one time axis and one numeric axis
- Rich Text is a rich text document (linear in top to bottom with wraparound in horizontal)
It might take some time to get the Archetype Queries + defaults expressive and easy to use enough that it makes sense to merge bar chart with spatial 2D. Right now we have the state that the bar chart space view takes a single 1-D tensor and draws a bar chart with x-axis = tensor indices and y-axis = tensor values. It draws boxes with width 1, centered on integers in x, y-min = 0 and y-max = tensor value.
With the right set of primitives a user should be able to manually build a bar chart in a spatial 2D view. For example they might want a stacked bar chart. Talking about bringing in 3D into a bar chart doesn't likely make sense since there probably doesn't exist a camera projection that maps between 3D and the tensor indices axis (x).
One could imagine that we would have heuristics that generate a Data Blueprint for boxes that creates a bar chart from 1-D tensors.
In the early prototype 2D and 3D Space Views were separate since they would use different
renderers - 3D Space Views were driven by three-d
, 2D Space Views by egui directly.
With the advent or re_renderer
, this distinction was no longer necessary and indeed a hindrance.
Like most modern renderer, re_renderer
does not distinguish 2D and 3D rendering at a fundamental level
(albeit we might add some limited awareness in the future) since shader, hardware acceleration and
data structures are all fundamentally the same.
If the root of a 2D Space View has a camera projection we can have a defined way of displaying any 3D content. Therefore, all 3D content can be displayed in a 2D Space View.
Vice versa, if an entity in a 3D space defines camera intrinsics, any 2D contents under it can be previewed in 3D space. Again, there is no point in putting a limit on what is displayed there.
However, they are more different from a users point of view.
First of all 3D data is only viewable in 2D if combined with a suitable projection (could be through perspective projection or by dropping the data of one dimension). The fact that 2D views are rendered in a 3D pipeline using some kind of pseudo depth (draw order) and an implicitly defined orthographic camera, is not top of mind to me as a user. This is something you need considerable exposure to graphics or 3D computer vision to experience as immediately obvious.
Second, the expectations around how to navigate a 2D visualization are quite different from how I expect to navigate a 3D visualization.
Registration happens on startup in the Viewer owned SpaceViewClassRegistry
.
The Viewer registers all builtin Space View Classes and users may add new types at any point in time.
Space View systems are the primary means how a Space View processes entities. All Space View systems are instantiated and executed every frame. Each System operates on a statically defined set of archetypes. Execution is allowed to store arbitrary state for the duration of the frame.
For the moment we have a simple two step framework:
Instantiation happens before ViewPartSystem
and can not emit drawables, only set internal state.
The results are available during ViewPartSystem
execution as well as SpaceViewClass
drawing.
This is used e.g. to prepare the transform tree.
Each ViewPartSystem
that knows about this TransformContext
can then use it to look up transforms.
Gathers data from the store and emits re_renderer
draw data for later use in the SpaceViewClass
's ui/drawing method.
For convenience, it provides a data() -> &Any
method to make it easy to expose results other than re_renderer
draw data
in a generic fashion.
Example:
The Points2DPart
queries the Points2D
archetype upon execution and produces as a result re_renderer::PointCloudDrawData
.
Since points can have UI labels, it also stores UiLabel
in its own state which the space view class of ui
can read out via Points2DPart::data()
to draw UI labels.
Note on naming:
ViewPartSystem
was called ScenePart
in earlier versions since it formed a part of a per-frame built-up Scene.
We discarded Scene since in most applications scenes are permanent and not per-frame.
However, we determined that they still make up the essential parts of a SpaceViewClass
.
Their behavior is a match to what in ECS implementations is referred to as a System -
i.e. an object or function that queries a set of components (an Archetype) and executes some logic as a result.
Registration is done via SpaceViewSystemRegistry
which SpaceViewClassRegistry
stores for each class.
Space view classes can register their built-in systems upon their own registration via their on_register
method.
As with space view classes themselves, new systems may be added at runtime.
SpaceViewClass::prepare_ui
- default create all registered
ViewContextSystem
into aViewContextCollection
- execute all
ViewContextSystem
- default create all registered
ViewPartSystem
into aViewPartCollection
- execute all
ViewPartSystem
, giving read access to theViewContextSystem
- this produces a list or
re_renderer
draw data
- this produces a list or
- pass all previously assembled objects as read-only into
SpaceViewClass::ui
- here the actual rendering via egui happens
- this typically requires iterating over all
ViewPartSystem
and extract some data either in a generic fashion viaViewPartSystem::data
or with knowledge of the concreteViewPartSystem
types
- this typically requires iterating over all
- currently, we also pass in all
re_renderer
data since the build up of there_renderer
view viaViewBuilder
is not (yet?) unified
- here the actual rendering via egui happens
Despite being few in numbers, Space Views Classes are registered on startup. This is desirable since:
- forces decoupling from other aspects of the Viewer (Viewer should be composable)
- allows for user defined space views
Rust developers can use the Class Registry to register their own Space View types. We do not expect this to be a common workflow, but more of a last resort / highest level extensibility hooks.
These user defined Space Views have no limitations over built-in Space Views and are able to completely reimplement existing Space Views if desired.
In the future A more common extension point will be to add custom systems to an existing Space View emitting re_renderer drawables. (TODO(andreas): We're lacking API hooks and an example for this!)