Actor data source questions
I just fielded a few good questions via email about actor data sources, and thought it would make a good post.
Actor data sources are a way of getting little bits of your code (actors) into the world, handing spatial indexing and cache management for you. When each actor is in view, they are given the opportunity to execute code in the Render and Update functions. When the cache is full and an actor is evicted, they again may execute code in the OnRemove function.
You create them by implementing a DataSource with usage Actor, and an ActorBuilder which translates the Primitives returned into Actor objects, along with information like what areas of the Earth the actors should be used in, as an ActorBounds object. All of this can be seen in the ActorDataSource sample.
In the sample, however, only one Primitive is returned per request, and only one Actor. What if you want more? The important thing to know is that there has to be a 1-1 correspondence between Primitives and Actors *. You must create one Actor for every Primitive used. If you create more than one Actor for a single Primitive, only one will appear.
The reason for this is the Actor update story. It is possible to update an Actor already in the system by two methods. When an Actor is updated, the PrimitiveId from the passed-in primitive is used to find and update the actor in question. If you add multiple actors per primitive, an ambiguity arises as to what you actually want to do.
It is possible to resolve this ambiguity by creating "nested" actors. This is simple. Just create the actors you want, and then a "container"
*: actually you can create a single Actor with multiple Primitives, but I don't want to confuse the issue for now.
Updating an Actor:
Expiration. On the Entity attached to your primitive, add a property "ExpiresInSeconds":
e.EntityTypes["bunny"].Properties.Create("ExpiresInSeconds", typeof(double));
...
entitySpec.Properties.SetValue("ExpiresInSeconds", 10.0);
Your QueryPrimitivesInternal function will be called again for every area the affected Actors live, and OnRemove will be called for the expiring Actors.
Manual. You can specify a specific PrimitiveId to update using OnDataChanged in your source DataSource.
PrimitiveIdWriteableCollection wc = new PrimitiveIdWriteableCollection();
wc.Add(new BunnyPrimitiveId(this, tile));
OnDataChanged(new SpatialExtent(null, null, wc.GetReadOnlyCopy()));
You must also implement the QueryPrimitivesByIdsInternal function. A collection of ids is provided, and you must return an array of Primitives that matches the input (so, the first item in the id collection should result in a Primitive at array index 0, etc). If an element of the array is left null, that means to delete the Actor corresponding to that PrimitiveId. Your ActorBuilder is then called to allow you to create the new replacement Actors.
You can also specify a region of the Earth to update in the SpatialExtent, but update by entity is not supported. Updating very large areas can be expensive, so use spatial regions with care. Update by PrimitiveId is the most efficient.