Thursday, October 27, 2011

Markers and Particle Systems - First Look

Let's start with Markers.  You'll recall that these are just stamps we place at locations on the globe.  Pretty basic stuff.

Marker placed right over Paris.  Woo.

So that's working.  It can also display a series of images, you just give it a list of textures and a period over which to switch them and off it goes.

And it's smoooth.  Why so smooth?  Because I do the switching on the rendering thread.  And that makes it complex.  For me, anyway, not you.

Marker Code

Here's the example code.

// Utility routine to add a texture to the scene
- (SimpleIdentity)makeTexture:(NSString *)name
{
    UIImage *image = [UIImage imageNamed:name];
    if (!image)
        return EmptyIdentity;
    Texture *theTexture = new Texture(image);
    scene->addChangeRequest(new AddTextureReq(theTexture));

    return theTexture->getId();
}

// Add a Marker over Paris
- (void)addMarkers
{
    // Description of the marker
    NSDictionary *markerDesc =
    [NSDictionary dictionaryWithObjectsAndKeys:
     [UIColor whiteColor],@"color",
     nil];
    
    // Set up a texture for the marker
    SimpleIdentity parisArmsTexId = [self makeTexture:@"200px-Grandes_Armes_de_Paris"];
    SimpleIdentity parisFlagTexID = [self makeTexture:@"200px-Flag_of_Paris"];
    SimpleIdentity frenchArmsTexID = [self makeTexture:@"175px-Armoiries_republique_francaise"];
    SimpleIdentity frenchFlagTexID = [self makeTexture:@"320px-Flag_of_France"];

    // Set up the marker
    WGMarker *parisMarker = [[[WGMarker alloc] init] autorelease];
    
    // Stick it right on top of Paris
    GeoCoord paris = GeoCoord::CoordFromDegrees(2.350833, 48.856667);
    [parisMarker setLoc:paris];

    // We're going to give it four different textures that rotate over a period of 10s
    [parisMarker addTexID:parisArmsTexId];
    [parisMarker addTexID:parisFlagTexID];
    [parisMarker addTexID:frenchArmsTexID];
    [parisMarker addTexID:frenchFlagTexID];
    parisMarker.period = 10.0;

    // These values are relative to the globe, which has a radius of 1.0
    parisMarker.width = 0.01;    parisMarker.height = 0.01;
    
    // And add the marker
    [self.markerLayer addMarker:parisMarker desc:markerDesc];
}

That's totally self explanatory.  Definitely.

The only thing I don't like about this is how the textures work.  That's one texture per image, which means one Drawable per state and that kinda sucks.  I may need to expand the TextureAtlas support a bit to help out with this case.

But this is pretty much how it'll work for the WhirlyGlobe 1.2 release.

Particle Systems

These are never going to be full desktop graphics particle systems or non-real time animation particle systems.  They'll be pretty simple.

A couple of fountains
Okay, they won't be quite that simple, but you get the basic idea.  These are fountains with a single color.  A proof of concept, as you might say.

Particle System Code

Here's what the code looks like.

// Add a particle system
- (void)addParticleSystems
{
    NSDictionary *partDesc =
    [NSDictionary dictionaryWithObjectsAndKeys:
     [NSNumber numberWithFloat:0.02],@"minLength",
     [NSNumber numberWithFloat:0.03],@"maxLength",
     [NSNumber numberWithInt:500],@"minNumPerSec",
     [NSNumber numberWithInt:600],@"maxNumPerSec",
     [NSNumber numberWithFloat:1.0],@"minLifetime",
     [NSNumber numberWithFloat:5.0],@"maxLifetime",
     nil];
    
    // Add a single particle system
    ParticleSystem *particleSystem = [[[ParticleSystem alloc] init] autorelease];
    GeoCoord washDc = GeoCoord::CoordFromDegrees(-77.036667,38.895111);
    [particleSystem setLoc:washDc];
    [particleSystem setNorm:PointFromGeo(washDc)];
    
    [self.particleSystemLayer addParticleSystem:particleSystem desc:partDesc];
}

Okay, I'm skipping a step, like where I create the particle system layer.  Trust me, you'll just copy my example for that anyway.  It's easy.  This is the interesting bit.

Again, pretty standard stuff.  You give it some parameters to control its size, some functions of the particles, a location, and you're off.  I'll add a few more parameters to control the distribution and color and maybe another type or two if I'm feeling saucy.  Otherwise, that's about how it'll look for 1.2.

Thursday, October 20, 2011

WhirlyGlobe 1.2 feature - Markers

The paperwork's still being shuffled on this one, but it looks like it'll happen.  I have a client who needs Markers and Selection.

That'll be two new layers that find their way into WhirlyGlobe 1.2.

Markers

Markers are pretty simple, really.  You give the toolkit a location, a size, and an image and it sticks that sucker right where you tell it.  Kind of obvious, really.  I'd say it was an oversight in 1.1, but I just chose to work on vectors instead.

In true WhirlyGlobe fashion, you'll be able to specify a whole NSArray of Markers (and I recommend that you do) for speed.

You'll also be able to specify a whole array of UIImages to iterate over, in much the same way as UIImageView.  I'll be tossing these in to a Texture Atlas, because that's just how I roll.  Should be pretty fast.

The switching will be controlled by a render-side Generator, which I'm not ready to talk about yet.  We'll see more Generators in 1.2.

Selection

This one is more infrastructure-y.  The Selection layer will let you pick features based on a 2D screen location.

Selecting vector features is pretty easy already.  All you need is a geographic location and a big pile of features to look through.  With the MBR (bounding box) cache on top of a shapefile, we can do most of the checking without looking at the actual features.  It's fast and reasonably easy.

Selecting labels is harder.  To do it properly you'd really like to tap within a label or near one and that nearness test makes it tricky.  What coordinate system is it "near" the tap in?  Well, from the user's perspective, it's the screen and that means you need to do it in screen space.

What the Selection layer will do is keep track of objects, such as labels, in 3-space and project them back to the screen on demand for testing.  You'll hand the Selection layer a point, distance, and a callback delegate.  Everything that falls within the given range will be handed to the delegate.

Toolkit Support & WhirlyGlobe 1.2

The Marker and Label layers will automatically pass their data to the Selection layer if given the right parameters.  There's a bit of overhead in keeping the outlines of features available for selection so this will be optional.  In fact, it will probably cost more memory to make a given label available for selection than to render it.  Weird, eh?

Geographic features, such as points, linears, and areals will still be selectable by geographic test.  For now, that makes more sense.

The 1.2 release is being shuffled a bit.  Markers are new and Selection was something I was contemplating for later.  Caching is still in, as are particle systems, since I have those largely implemented.  We'll have to see what else makes the cut.

Tuesday, October 4, 2011

WhirlyGlobe 1.2 - What's Coming

I'm working on WhirlyGlobe 1.2 at the moment.  Well, I'm working on things and those things will wind up in 1.2.  Here's a list of some of those things.

Caching

Here's what I mean by caching.  WhirlyGlobe reads fairly generic data sets: geodetic shape files.  These are relatively unstructured lists of points, linears, and areal features with matching attribution.  All the coordinates are in (latitude,longitude).  Turning these into what you see on the screen involves a bit of processing.

What I'm doing internally is batching all that data into a small set of Drawables and tossing those over to the renderer.  To cache, I save that data out in a form compatible with OpenGL and load it back in on startup.

The caching is mostly finished.  I had a client who wanted to load all the Natural Earth 10m country vectors at once.  Turns out it's possible, even on the iPad1.

Bitmap Fonts

When you add text to the label layer in WhirlyGlobe it's rendering it to a UIImage and turning that into a texture.  That's not deeply efficient.  If you add a whole bunch of labels together, it is stitching those images together into Texture Atlases, so that's not as bad as it could be.  Still, it uses more memory than it should.

The obvious next step is to use a giant texture made up of all the various characters you might want to use.  There are standards for this sort of thing and tools to generate the textures, such as BMFont.  It's just a matter of actually doing the work.

Grid Layer

This one's pretty simple.  It's just a layer that drops down lines at the lat/lon boundaries.  It's in there now, it just doesn't work all that well.

Lofting Layer

The lofting layer is part of WhirlyGraph.  That's the thing that creates transparent geometry for country outlines.  In that sense, this isn't all that new.

What is new is integrating it into the regular toolkit.  I was doing an early version of caching to make it fast enough to use for the bigger countries.  Now caching is worked out, I'll switch the lofting layer over and integrate with the main toolkit.

Particle Generator

I've had requests for simple particle generation, much like in the Smule apps.  It's easy enough, but making it fast requires some design.

You'll most likely be restricted to a fairly simple set of parameters: particle color, speed, and various randomizers.  I think I'll avoid fireworks for the first go and we'll stick to fairly basic fountains.

The Farther Future

That's all for 1.2, I think.  Of course, some of this might slip and anything a client needs gets priority.

There are already some interesting things in store for 1.3.  In particular, discussions with other groups related to doing data fetching over the network.  We'll see how those go.