Allocation was wrong.
Implemented room geometry loading.

There are four difficult tasks (and many boring ones) to what I want to do: Loading meshes, loading rooms, TR's animation system and portal rendering. So far, I've implemented two out of four successfully, and I have a fairly good idea for number 3. So it is only portal rendering that will be interesting, and will be the most likely reason why I might choose to abandon Scene Kit.
Make it possible to look up static mesh objects by their object ID.
Added static mesh derived property.
Adding static objects to rooms.

Static meshes are easy to add. The main problem right now is that their lighting is incorrect.
Added way to get color of static mesh instance.

Necessary to make them stand out less in a room. Not yet sure what I'm going to do with it, though, SceneKit would require me to do a copy of the geometry in order to apply it.
Adjusted lighting of static meshes.

Static meshes that have a color set now get this in SceneKit as well. To do this, the geometry and the materials have to be copied. A more intelligent implementation might set up dictionaries for both by light value, to avoid extraneous copies, but so far, I don't see the need for this. I'm also less and less sure that SceneKit is really the solution for me here.
Added rendering of full levels.

That seemed easy! But it isn't, because it will horribly fail when rooms overlap each other (such as the TR3 mansion, everywhere). This is not a tiny issue here and there; it happens all over the place, especially in Tomb Raider 3, and is completely unacceptable. The correct solution is to implement portal rendering, something I'm afraid of at the best of times, and doing it with SceneKit is going to be very, very hard.

I'm thinking about doing some sort of fork; with scene kit rendering for basic stuff, and hand-written, suboptimal OpenGL for really dealing with levels.
Use calibrated instead of device.

I don't think it makes a difference, but it looks nicer.
Allow bulk processing of all room vertices.

This was only used for some debugging, but I'm leaving it in as a "to be used later, maybe, some day" sort of thing.
Save the level, and change all lights.

Saving the level is a big one, because I didn't test my saving code in the engine before, but it works admirably - not a big surprise given that the entire system was specifically built for that, but nice. The important thing here was to set all room vertices to the same light value, because the ones used when following TRosettaStone just don't make any sense and I wanted to experiment.
New light scaling code.

The TRosetta stone lists the TR2 color scale as ranging from 0 (maximum brightness) to 8192 (minimum brightness). While technically true, this mapping is not linear; using it linearly will generate levels that are too dark. Through experimentation, I reached the conclusion that the real range is 4096-8192, with anything < 4096 having the same lighting as 4096. Thus this formula. I think it may still generate incorrect values, but at least not as incorrect as they were.
Deleted class because light normalization happens in the level.

I first thought that light normalization should take place in the mesh.
But that won't work; light values are used all over the place.
Normalizing it is thus the work of the level, which means that TR3Mesh
doesn't need this method anymore, and without any method, it's lost its
right to exist.