I got a bit sidetracked while working on creature components, but still ended up making important progress for the overall system.
First, I created a new
transform class by abstracting and cleaning up the
shape class's position, angle, and size attributes and methods. I also created a
transformable "mixin" class, which can be used by any class that has a transform. (Lisp doesn't have mixins in the same sense that Ruby does, but since Lisp supports multiple inheritance, you can get more or less the same effect by designing a class in a mixin-ish style.) The transformable class provides accessors for the transform's position, angle, and size, so that you can treat them as if they were direct slots of the object holding the transform.
Next, I decided to enhance the shape class so that shapes can have a color. This turned out to be somewhat challenging, because of how I had designed the OpenGL wrapper classes.
I have a
job class that holds a program object (which is a collection of compiled shaders linked together) and the vertex attribs, uniforms, and elements used to render a certain thing (such as a shape). For memory efficiency, each kind of shape (rectangle, triangle, or circle) has a base job which holds the generated vertex data and elements.
But, for each shape instance to have its own color (or other such data in the future), each shape instance must have its own job, because the color would be a uniform contained in that job. If I set the color in the base job, all instances of that kind of shape would have the same color. But if each instance has its own job, I lose the benefit of having the vertex data stored in one place.
I solved this by letting a job have a "parent", from which it inherits anything it's missing, such as a program or elements. For vertex attribs and uniforms, the inheritance works by merging the parent's list of attribs/uniforms with the child's list, with any of the child's attribs/uniforms taking precedence when it has the same name as one of the parent's. It's wonderfully easy to do that in Lisp:
(union (attribs parent) (own-attribs job) :key #'name :test #'string=)
This code takes the attribs list from the parent, including any attribs the parent itself inherits from its parent, and the list of attribs belonging directly to the job, and performs a set union of those two lists, which discards duplicates. But what is a "duplicate"? By providing the
:key #'name, I'm telling it to call the
name function on each attrib, and then check the names to see if they are duplicates. The
:test #'string= part tells it to use the
string= function, which performs case-sensitive string comparison, to decide if the two names are the same. The result is that if an attrib in the parent's list and an attrib in the child's list have the same name, only the child's is used (since it is given as a later argument to the
While doing this, I noticed that the inheritance code was exactly the same for attribs and uniforms, except for the word "attrib" or "uniform". So, I wrote a fairly simple, albeit long, macro to keep things
Now that jobs can have a parent, I can use each shape class base job (the job containing the vertex data and elements) as the parent for each shape instance's job (the job containing the color and other instance-specific data). So, the instance's job can inherit the vertex data and elements, while still having its own color. And later, I'll be able to have base jobs at different levels of detail, and easily change the instances' parents whenever I want to change their level of detail.
After a bit more work, I finally had support for different colors in each shape instance. Victory!
It was a long detour, but my code base is much better for all the work. Now I can finally get around to shape groups and creature components!