## GLGeom UV generators

Per Dave’s request, I’ve recently added a new function to generate UV coordinates on a glgeom::primitive_buffer. The function name is glgeom::compute_uv_mapping().

The principle is simple: it’s a callback-based function that iterates over all the vertices and calls the callback to generate a UV coordinate. The existing glgeom_extension_mappers module already provides worker functions to use in the callback argument (e.g. mapper_cube, mapper_spherical).

### Images

Above is a spherical mapper with a 10x scale applied to the terrain geometry.

Above is a cube mapper with a 10x scale applied to the terrain geometry.

### Code

The syntax for generating a planar XY mapping is as follows:

glgeom::compute_uv_mapping(primitive, 0, [](const glgeom::point3f& p, const glgeom::vector3f& n) -> glgeom::point2f {
return glgeom::scale( glgeom::mapper_planar_xy(p), glgeom::vector2f(10, 10));
});

Using namespace glgeom, it looks like this:

compute_uv_mapping(primitive, 0, [](const point3f& p, const vector3f& n) -> point2f {
return scale( mapper_planar_xy(p), vector2f(10, 10));
});

For anyone living in the dark ages of pre-C++11, the above code reads as follows:

• Compute a UV mapping for “primitive”
• Store the computed UVs in channel 0
• Generate a lambda function as callback…
• …which takes in a 3d position and a normal and returns a 2d point (i.e. the UV coordinate)
• …which uses the GLGeom built-in “mapper_planar_xy” function to generate the UV
• …and lastly applies a scale factor of 10 to both the u and v coordinates

### API Design Commentary

I have to admit, I’ve not thoroughly satisfied with the resulting code above. It’s a bit verbose and has a very high syntactical-boilerplate-to-content ratio. However, this is the best compromise I’ve come up with yet.

• The lamdba notation is heavy-weight: there’s almost as much code to define the lambda signature as there is in the lambda body
• It’s possibly too general: in an ideal design, everything should have an immediately obvious place. Mappers transformations (scaling and rotation) as quite common so I’d be nice to have a “built-in” argument for these rather than relying on the user adding them manually into the lambda (and possibly introducing a typo into the multiplication order)
• Is a UV coordinate a “point”? In theory yes, in practice UVs are often treated as generic tuples, making the glgeom::point2t type occasionally cumbersome

#### Why not allow function pointers to be passed in directly?

I’d like it better if syntax like this worked:

  glgeom::compute_uv_mapping(primitive, glgeom::mapper_cube);

But this doesn’t work well. I want to support mapper functions that require positions and normals (e.g. mapper_cube) as well as those that depend only on position (e.g. mapper_spherical). I also want to support lambda functions for inlined custom mappers. Overloading a compute_uv_mapping template method to automatically choose the right variation did not seem to work (VS2010, at least, doesn’t seem to handle overloading well with lambdas of varying type). The compiler doesn’t seem to automatically resolve to the right template overload in a way that provides the syntax I’d like.

With this approach, I ended up with compiler complaints about ambiguous type resolution.

#### Why not provide an explicit set of compute_uv_* methods?

The ugly boilerplate syntax would go away if a series of functions where provided:

  glgeom::compute_uv_cube_mapping(primitive, uv_transform);
glgeom::compute_uv_spherical_mapping(primitive, uv_transform);
glgeom::compute_uv_planar_xy_mapping(primitive, uv_transform);
...

I like this approach that (a) syntax doesn’t get in the way and (b) it leads to about as self-documenting code as is possible.

What I don’t like is (a) it requires a mirror function for every mapper defined in the glgeom_extension_mappers and (b) creates a dependency between the glgeom_extension_mappers module and the glgeom_extension_primitive_buffer module.

#### So…?

I much prefer the principle that a generic function to apply an mapper exists alongside and independent of the set of mappers that can be applied (i.e. something a bit more along the lines of functional programming, thank you Javascript and JQuery for teaching me how nice this approach can be!). With that principle in mind, I’ve ended up with the syntactically verbose lambda based approach.

### What’s Next?

Hopefully, generating tangents and bi-tangents. I wrote a bit before about bump mapping and generating those values in the GLSL shader, but it would be nice to compute such data easily on the CPU side as well.

In any case, this will require me to add some functions to compute mesh adjacency information for a primitive buffer – a feature I’ve been meaning to add anyway.

Written by arthur

April 11th, 2012 at 11:42 am

Posted in glgeom