Complex vector styling
In the process of working on #1163, I've been working under the assumption that one vector overlay = one color, line width, etc. But this isn't how vector data is styled in many cases. For example, in the iTwin Feature Service sandcastle, the color of the line primitives is based on metadata from the GeoJSON file. For a more complex example, the MapBox vector tiles styling spec supports all sorts of complex expressions for defining the style of a layer. This is similar to the 3D Tiles styles spec.
We don't have to support all of this complex styling. Cesium Native does not support all data formats, and is not meant to. But we do need to support the iTwin Feature Service vector data, which means at the very least the ability to say, "look up the color of this primitive based on the type property obtained from the GeoJSON source file." Currently, this is something we can't do.
So there's two questions we need to answer:
- How far to do we want to go right now?
- And, with that scope in mind, how will we accomplish it?
First off, accomplishing this through our current styling support - that is, relying on the built-in material editors of Unreal, Unity, etc. - is going to be difficult and rife with issues. The best we could do would be to rasterize with an ID as the color, and use that ID to look up the properties of the vector data from the material. But how do we pass those properties to the material? I don't think there's shader support for JSON data. And even if we find a way to pass the properties to the shader - how do we deal with shapes with an outline? Do we encode a distinct ID for the stroke so that it can be colored appropriately in the shader?
A few ideas:
Styling Callback
The easiest approach to implement. We let the user specify a callback that we run every time we rasterize a primitive, and that callback gets access to the primitive's properties and can modify the style. This supports pretty much every use case with minimal work on our part, and we could expose it through a Blueprint delegate somehow to let users work with it without C++. However, it has pretty horrible implications performance-wise if we're calling a user-provided callback for every primitive we rasterize. We could improve on this by only running the callback once per primitive per overlay, instead of on every rasterization, but users might want to do more dynamic styling which would bring us right back to where we started. It's also not exactly a user-friendly solution.
Styling Expressions
We could implement something akin to the 3D Tiles styling spec. This has the advantage of being a lot more user friendly, and we can take the expressions and turn them into data structures that will be potentially a lot faster to run through than user code. However, JSON doesn't seem to me like the best language to be asking users to write out in the details/inspector window. It's also a good amount more work than just adding a callback - while a very basic expression language like the 3D Tiles styling expressions aren't too difficult to parse (and I've done that sort of thing a fair number of times before), it's certainly not a trivial task.
I think the approach used in CesiumJS is pretty good, and not too much work to implement. Color / width / outline / whatever else are explicitly configurable properties on each feature. Then, we just need to make sure users have an opportunity to edit those explicit properties in between when the features are loaded and when they are rasterized or otherwise rendered. That's pretty similar to the "Styling Callback" proposal above, except that styling is more of a "retained mode" thing rather than an immediate mode thing (potentially with a cache).
Inevitably, people will want to be able to edit style properties after features are already shown. A super common use-case is highlighting a selected feature. We may need to make some improvements to the raster overlay system to make this more feasible.
I think the main downside to all this is that it doesn't necessarily take full advantage of game engine capabilities. We're talking about very simple styling properties, when something that hooks into game engine materials deeper could be much more powerful. Lines that glow? Points with a world position offset? Textured or normal mapped polygons?
But if we're careful about how the style rasterization works, then the above should be possible! We just need to make sure that when we render a line with a particular color, we use exactly that color for the entire line (alpha can be used for anti-aliasing, but we shouldn't change the color beyond that). Then, as you alluded to above, advanced users can use the simple styling API to manually assign each feature (or group of features, or whatever) a unique color, and pair that with a custom game engine material that does whatever it likes to apply fancy effects to that feature.
I think we've handled this decently for now, and I've opened #1208 for further discussion of styling expressions, so I'm going to close this issue.