To draw the player's view, an intermediate lookup table is used to map a pixel on the screen to a pixel on the cubemap. The lens is responsible for the structure of the lookup table. In other words, the lens is responsible for directing a ray of light from the environment to a pixel on the screen, or vice versa. This is called a forward mapping and an inverse mapping, respectively.
This is the simplest coordinate system commonly used to define physical camera lenses. It describes the relationship between the radial position R on the film and the angle θ from the optical axis of the lens.
The rest of the modes assume the same coordinate systems for the screen, but different coordinate systems for the environment. The screen uses 2 coordinates (x,y). The origin (0,0) is at the center of the screen. Positive x to the right. Positive y is up.
You can use two angles to define a 3D direction. You may know them by any of the following standard names:
You can also define a 3D direction using a unit vector (x,y,z). Quake Lenses currently uses a left-handed coordinate system, as is standard in Quake. Positive z is straight forward. Positive x is right. Positive y is up.
This is a non-standard coordinate system that allows you to directly refer to a point on a given cubemap face using (side,u,v). u and v are between 0 and 1. Positive u is right. Positive v is down. The side is an integer with the following enumeration:
To create a lens, you must write a Lua script. The game will look for the following variables and functions in order to build the lookup table for the player's view.
Variables
Functions
A lens creates an image of varying size. It can be anything from 1x1 units to 1000x1000 units, or even infinitely large. Due to the wide range of sizes, the game needs to know how to scale it to the screen. Different scales result in different zoom levels. There are currently two ways of scaling: FOV or fitting.
FOVWhen the user specifies an FOV, the game must use a forward map to determine the scale of the image. It uses the theta_to_r, latlon_to_xy, or ray_to_xy to determine where the edges of the screen should be on the image. For example, if you have an hfov of 180, you can call latlon_to_xy(0,pi/2) to get the x position of the furthest right position. For simplicity, the game assumes a centered image, so it just multiplies this by 2 to get the width of the image. The game then scales the image so that it fits the width of the screen.
FittingAs an alternative to the FOV, you can explicity specify a maximum width and height of the image created by your lens, using hfit_size and vfit_size, respectively. The game will then use this instead of the forward map to scale it to the screen. The user can then type "fit" to scale the image as large as possible to fit the screen. The user can also type "hfit" or "vfit" to only fit the image horizontally or vertically regardless of cropping. (The "fit" command automatically determines which one to use to avoid cropping.)
The game constructs the lookup table in one of two ways. It will first try to find an inverse function in the lens script. The game will then convert the position in the lookup table to a screen coordinate (r or xy) and check if it is inside the image by calling r_isvalid or xy_isvalid. If it is not valid it will leave the pixel blank. If it is valid, it will pass the screen coordinate to the inverse map function. It will then use the returned value to select a pixel from the cubemap.
If the game does not find an inverse function, it will look for the forward function instead. The game will then iterate through the cubemap, converting the position to an environment coordinate (radial, spherical, cartesian, or cubic) and passing it to the forward function. It will then convert the returned screen coordinate to a position in the lensmap for storing the cubemap pixel. This does not guarantee that all the positions in the lensmap will be filled, and will often result in noticeable holes. (Potential improvement: The holes can be filled by drawing a filled quad determined by the corners of the pixel)
The lookup table takes some time to calculate, and the performance is slowed by using the Lua interpreter instead of C code for the lens mappings. We can exploit the potential symmetry of the lens to make the calculation up to four times faster. If the lens is horizontally or vertically symmetric, you can set the hsym or vsym flag to true, respectively. That way, the game can deduce the rest of the lookup table after calculating a single half or quadrant.