gfx.patch

index · gfx

Overview

The bake side of a tangent patch. A sphere_tex window is a finer map laid over a patch of the sphere. To fill that map, a baker needs the frame the primitive reads the patch in, and the direction each texel's centre faces.

A window has a centre c on the sphere and an along direction e1, both in the object frame. It has half-extents a, b in the tangent plane. The primitive derives the frame from c and e1 and reads the patch gnomonically in it. A map baked in any other frame comes out mirrored, turned or shifted, and only a flight over it shows the error. So frame repeats the C's operations in the C's order, and dir gives texel (i, j)'s centre in that frame:

  s = -a + (i - 1/2) * 2a/w,   t = -b + (j - 1/2) * 2b/h
  n = (c + s*e1 + t*e2) / |c + s*e1 + t*e2|

i, j count from 1, as pixel does: (1, 1) is the first column along e1 and the first row along e2. The formula is linear in i and j, so a half-integer names a texel's edge.

A window's map is a ring. With an origin ox, oy, the window covers columns ox .. ox + w - 1 and rows oy .. oy + h - 1 of the patch's grid. Grid texel (I, J) lives at canvas texel ((I mod w) + 1, (J mod h) + 1). The baker samples it at dir(f, a, b, w, h, I + 1, J + 1). A map scrolled by moving the origin and baking the new band holds the same bytes as a fresh bake at the new origin. The primitive reads the window at plane offset s0 = ox * (2a/w), t0 = oy * (2b/h). dir uses the same product for its texel width.

The arithmetic is + - * / and the square root alone. So a bake gives the same bytes on every machine, as the primitive does.

Functions

NameSignature
frameframe(c, e1) -> frame
dirdir(frame, a, b, w, h, i, j) -> x, y, z

frame(c, e1) -> frame

The orthonormal frame the primitive reads a patch in

c is the patch's centre and e1 its along direction, in the object frame: +y the pole, +x the zero meridian. Each is {x, y, z} or {x=, y=, z=}, and need not be unit length.

Returns { c = {x, y, z}, e1 = {x, y, z}, e2 = {x, y, z} }: c normalised; e1 with its component along c removed, normalised; and e2 = c × e1. Map columns run along e1, rows along e2. frame.c and frame.e1 can go into a window entry as they are.

Returns nil for a zero-length centre, an along within a microradian of the centre or its opposite, or a non-finite component. The primitive refuses such a window too.

dir(frame, a, b, w, h, i, j) -> x, y, z

The unit normal at the centre of texel (i, j) of a w x h map over a patch

frame comes from frame. a, b are the patch's half-extents: the tangent of the half-angle each side subtends at the centre. w, h are the map's size in texels. i, j are the texel, counted from 1.

Returns the unit normal in the object frame at s = -a + (i - 1/2) * 2a/w, t = -b + (j - 1/2) * 2b/h. i, j can lie outside the map: a half-integer names a texel's edge, and 0.5 the patch's own edge.

Returns nil when: