Drawing party portraits from live bodies in Godot
The party rack and the inventory doll now show each member's own body, head and hair, drawn 12 times a second into a viewport, with no measurable frame cost.
The four portraits on the party rack and the paper doll in the inventory used to be still pictures filed by class. They now show each member’s own race’s body, head and hair, moving. A portrait is the head and shoulders, and it breathes and blinks. When the member is hurt it plays the body’s hit clip so the face flinches, and when the member is down it holds a slump with the eyes shut. The doll in the vat is the whole body.
I asked for this because the race was saved and prices already read it, but nothing that drew a party member used it to pick a model. What’s left is that a member has no face of their own yet. Creation only picks the race, and only humans have more than one head. I also haven’t decided whether every race should keep one scale in the vat, so for now they all do.
How do you draw five 3D bodies without a frame cost?
Each picture is a SubViewport with a small 3D world of its own, and the control that shows it puts the viewport’s texture on its glass. Drawing five scenes every frame is the obvious cost, so nothing is. Each view steps the body’s animation and redraws 12 times a second, and each one starts on its own beat so four portraits don’t all land in the same frame. A view also draws only while the control showing it is on the screen. A closed inventory or a hidden binder builds no body, steps nothing and draws nothing.
The animation players are stepped by hand with an advance() call instead of by the engine each frame. That’s what makes the 12 a second possible.
I measured it with the HUD’s frame time. On the rack it was 13.51 ms with the old stills, 13.84 ms drawing at 12 a second, and 14.13 ms drawing every frame. With the vat open it was 9.96, 9.61 and 10.54 ms. That’s inside the noise from one run to the next, so I’m calling 12 a second free.
Which body and face does a member wear?
A small set of static functions, MemberLook, answers it. The race comes from the member’s saved race, and the face is a new saved string on the member, either a head name or a head name plus the hair as the hair code reads it. When it’s empty, the head is picked from the letters of the member’s name, and that pick is never written back to the save. A race with no body installed wears the default race’s body. signature() is a string of everything the picture depends on, and each view checks it once a second and rebuilds only if it changed.
How is the bust framed?
The camera has a 20 degree field of view and stands 13 degrees to one side and 4 above the face. It’s framed on the head’s own bounding box, not the body’s, so a tall race and a short one fill the glass the same. The frame is 1.7 head heights tall, centered 0.2 head heights below the middle of the head. A warm light is in front, a bile green one behind, and the glass behind is dark. The rack’s old shader still runs over the picture.
How does the doll line up with its cables?
The whole body is seen square on with no perspective, so a point on the body is a point in the picture by plain arithmetic. That lets the doll’s cable ends be read from the body’s bones, named in a table, instead of drawn at fixed spots. The doll draws every race at 128 pixels per meter, and its background is clear so the vat’s liquid shows through. A member with nothing installed, or a test that turns the views off, gets the old stills.
A new test, member_pictures, has 123 checks and passes. Run by word at the landing, the nine related tests passed too. Gear a member wears isn’t on the picture yet.