# tecs.gfx.layers Depth bands, sorting, coordinate spaces, parallax, and lighting. A layer occupies one of sixteen depth bands. Entities sort inside their band and never against another band. Higher layer numbers draw nearer, so a HUD on layer 8 covers a world on layer 1. ```teal tecs.gfx.layers.configure(1, {sort = "topdown", parallax = 0.4}) tecs.gfx.layers.configure( 8, {sort = "z", screenSpace = true, unlit = true} ) world:spawn( tecs.Transform2D(16, 16, 0, 8, 0, 96, 24), tecs.gfx.Tint(1.0, 1.0, 1.0, 1.0), tecs.gfx.Renderable2D() ) ``` The fourth [`Transform2D`](/modules/ecs/#tecs.ecs.Transform2D) argument selects the layer. ## Sort modes `"topdown"` sorts by Y with Z for height. `"z"` ignores position. `"isometric"` combines X, Y, and Z for a diamond grid. Equal depths retain instance order. `maxZ` and `maxY` define the authored extents that each sort maps into its band. Values past an extent clamp to its edge. They still draw, but clamped entities no longer sort against one another. Reduce an extent when `Renderer:depthSortCollapse` reports that the target depth format merges nearby values. ## Coordinate spaces World coordinates form the default. `screenSpace` uses target pixels and ignores the camera. `virtualCoords` stretches one `virtualWidth` by `virtualHeight` coordinate system to the target. `ignoreZoom` follows camera position while preserving drawn size. `parallax` scales camera movement. One layer cannot combine `screenSpace` and `virtualCoords`. Virtual coordinates preserve the authored coordinate system but do not letterbox or preserve square pixels. `unlit` bypasses scene lighting for UI, debug overlays, and other content that must retain its own color. `configure` replaces the complete layer configuration. Any omitted option returns to its default. ## Module contents ### Types | Type | Kind | Description | | --- | --- | --- | | [`Config`](/modules/gfx/layers/#tecs.gfx.layers.Config) | record | Defines every decision a layer makes about its contents. | | [`Sort`](/modules/gfx/layers/#tecs.gfx.layers.Sort) | enum | Selects how a layer orders its contents within its own band. | ### Functions | Function | Kind | Description | | --- | --- | --- | | [`bandOf`](/modules/gfx/layers/#tecs.gfx.layers.bandOf) | Static | Returns the two terms depthIn takes, resolved once for a layer. | | [`configure`](/modules/gfx/layers/#tecs.gfx.layers.configure) | Static | Sets what a layer does with its contents. | | [`depthIn`](/modules/gfx/layers/#tecs.gfx.layers.depthIn) | Static | Returns one entity's depth on a band bandOf already resolved. | | [`depthOf`](/modules/gfx/layers/#tecs.gfx.layers.depthOf) | Static | Returns one entity's depth from zero to one, with zero nearest. | | [`depthResolution`](/modules/gfx/layers/#tecs.gfx.layers.depthResolution) | Static | Returns the smallest depth difference depthOf produces between two entities one unit apart, taken over every sort mode. | | [`entryOf`](/modules/gfx/layers/#tecs.gfx.layers.entryOf) | Static | Returns the four floats that position and light one layer: its mode, its parallax offset factor, whether it ignores... | | [`isScreenSpace`](/modules/gfx/layers/#tecs.gfx.layers.isScreenSpace) | Static | Returns whether a layer uses screen-pixel coordinates. | | [`revision`](/modules/gfx/layers/#tecs.gfx.layers.revision) | Static | Returns the layer table's configuration revision. | | [`sortOf`](/modules/gfx/layers/#tecs.gfx.layers.sortOf) | Static | Returns a layer's sort as the identifier accepted by depthOf. | | [`viewCulled`](/modules/gfx/layers/#tecs.gfx.layers.viewCulled) | Static | Returns whether the camera's view rectangle culls this layer. | ### Values | Value | Type | Description | | --- | --- | --- | | [`MAX`](/modules/gfx/layers/#tecs.gfx.layers.MAX) | `integer` | Read-only. Reports how many bands divide the depth range. | | [`maxY`](/modules/gfx/layers/#tecs.gfx.layers.maxY) | `number` | Caller-writable. Sets half the expected world extent used to normalize position into a band. | | [`maxZ`](/modules/gfx/layers/#tecs.gfx.layers.maxZ) | `number` | Caller-writable. Sets the highest z a scene expects to use and the resulting resolution of sorting within a band. | | [`virtualHeight`](/modules/gfx/layers/#tecs.gfx.layers.virtualHeight) | `number` | Caller-writable. Sets the vertical resolution used to author a virtual-coordinate layer. | | [`virtualWidth`](/modules/gfx/layers/#tecs.gfx.layers.virtualWidth) | `number` | Caller-writable. Sets the horizontal resolution used to author a virtual-coordinate layer. | ## Types ### tecs.gfx.layers.Config record Defines every decision a layer makes about its contents. A field left out takes its default, so this says what a layer is rather than amending what it was. ```teal record tecs.gfx.layers.Config sort: Sort screenSpace: boolean ignoreZoom: boolean virtualCoords: boolean unlit: boolean parallax: number end ``` #### tecs.gfx.layers.Config.sort field Caller-writable. Selects how contents sort within the layer's band. The caller must set this field. ```teal tecs.gfx.layers.Config.sort: Sort ``` #### tecs.gfx.layers.Config.screenSpace field Caller-writable. Positions contents in screen pixels and ignores the camera entirely. What a HUD wants. Defaults to false. ```teal tecs.gfx.layers.Config.screenSpace: boolean ``` #### tecs.gfx.layers.Config.ignoreZoom field Caller-writable. Makes contents follow the camera's position but not its zoom, so they stay a constant size on screen while moving with the world. Defaults to false. ```teal tecs.gfx.layers.Config.ignoreZoom: boolean ``` #### tecs.gfx.layers.Config.virtualCoords field Caller-writable. Positions contents in `virtualWidth` by `virtualHeight`; Tecs scales that coordinate system to fill the target. Defaults to false and cannot combine with `screenSpace`. ```teal tecs.gfx.layers.Config.virtualCoords: boolean ``` #### tecs.gfx.layers.Config.unlit field Caller-writable. Makes contents bypass the lighting pass and appear at their own color. Defaults to false. A material can ask for the same thing, and a fragment is lit only where the material and the layer agree. ```teal tecs.gfx.layers.Config.unlit: boolean ``` #### tecs.gfx.layers.Config.parallax field Caller-writable. Sets how much the camera's position carries the contents. One moves them with the world; a half drifts them at half speed, which is what a background wants. Defaults to one. ```teal tecs.gfx.layers.Config.parallax: number ``` ### tecs.gfx.layers.Sort enum Selects how a layer orders its contents within its own band. ```teal enum tecs.gfx.layers.Sort "isometric" "topdown" "z" end ``` ## Functions ### tecs.gfx.layers.bandOf Static Returns the two terms `depthIn` takes, resolved once for a layer. What `depthOf` works out before it looks at an entity at all, and what a caller writing many depths on one layer should work out once instead of once a row: the band's near edge, and which sort decides where in the band a row lands. ```teal function tecs.gfx.layers.bandOf(layer: integer): number, integer ``` #### Arguments | Name | Type | Description | | --- | --- | --- | | `layer` | `integer` | One to `MAX`. Outside that range this resolves the nearest layer whole, exactly as `depthOf` does, so the sort answered here is the clamped layer's and not what `sortOf` answers for the same argument. | #### Returns | Type | Description | | --- | --- | | `number` | The band's near edge, then the sort identifier. | | `integer` | | ### tecs.gfx.layers.configure Static Sets what a layer does with its contents. Replaces the layer's configuration rather than amending it. A [`Config`](/modules/gfx/layers/#tecs.gfx.layers.Config) defines the whole layer, so each omitted field takes its default instead of retaining its previous value. ```teal function tecs.gfx.layers.configure( layer: integer, config: layers.Config ) ``` #### Arguments | Name | Type | Description | | --- | --- | --- | | `layer` | `integer` | One to `MAX`. Anything else raises. | | `config` | [`layers.Config`](/modules/gfx/layers/#tecs.gfx.layers.Config) | Raises on a missing or unknown `sort`, and on asking for `screenSpace` and `virtualCoords` together. | #### Returns None. ### tecs.gfx.layers.depthIn Static Returns one entity's depth on a band `bandOf` already resolved. The per-row half of `depthOf`, split out so a caller writing a run of rows on one layer pays the band lookup once. Bit for bit what `depthOf` answers for the layer the two terms came from. ```teal function tecs.gfx.layers.depthIn( base: number, sort: integer, z: number, x: number, y: number ): number ``` #### Arguments | Name | Type | Description | | --- | --- | --- | | `base` | `number` | The band's near edge, from `bandOf`. | | `sort` | `integer` | The sort identifier, from `bandOf`. | | `z` | `number` | Height in the sort, against `maxZ`. Read by every sort mode. | | `x` | `number` | World x, read only by the isometric sort, against `maxY`. | | `y` | `number` | World y, read by the topdown and isometric sorts, against `maxY`. Larger is lower on screen and so nearer. | #### Returns | Type | Description | | --- | --- | | `number` | A depth inside the band, held to zero and one the same way `depthOf` holds its own. | ### tecs.gfx.layers.depthOf Static Returns one entity's depth from zero to one, with zero nearest. Exact ties carry no tie-breaker. The renderer draws instances in index order and the depth test lets an equal fragment through, so two entities at the same depth already resolve the same way every frame: the later one wins. Nudging depth by identity would resolve them the other way, since a larger value is farther, and quietly invert the order a scene was built expecting. ```teal function tecs.gfx.layers.depthOf( layer: integer, z: number, x: number, y: number ): number ``` #### Arguments | Name | Type | Description | | --- | --- | --- | | `layer` | `integer` | One to `MAX`, which picks the band and, through `sortOf`, what the rest of the arguments mean. Outside that range this takes the nearest layer whole and goes on sorting within it, because a band is the only thing a depth can name. | | `z` | `number` | Height in the sort, against `maxZ`. Read by every sort mode. | | `x` | `number` | World x, read only by the isometric sort, against `maxY`. | | `y` | `number` | World y, read by the topdown and isometric sorts, against `maxY`. Larger is lower on screen and so nearer. | #### Returns | Type | Description | | --- | --- | | `number` | Returns a depth inside the layer's own band. The function normalizes and clamps each argument to zero through one, so a scene beyond the configured extent stops sorting instead of spilling into the next layer. | ### tecs.gfx.layers.depthResolution Static Returns the smallest depth difference `depthOf` produces between two entities one unit apart, taken over every sort mode. A depth buffer must preserve this difference. A format with a larger step keeps the bands, so layer order remains safe, but loses the sort inside them: the two entities land on one depth value and draw in write order rather than their sorted order. The calculation includes every mode because `configure` may assign any sort after the depth target exists. `MAX`, `maxZ`, and `maxY` determine the result, so raising an extent changes it. ```teal function tecs.gfx.layers.depthResolution(): number ``` #### Arguments None. #### Returns | Type | Description | | --- | --- | | `number` | A depth difference, in the input that moves depth least. That is the topdown sort's y at the defaults, which spends the smallest share of a band on the widest range. | ### tecs.gfx.layers.entryOf Static Returns the four floats that position and light one layer: its mode, its parallax offset factor, whether it ignores zoom, and whether it is lit. Returns these values together because consumers pack them together when `revision` changes, not every frame. ```teal function tecs.gfx.layers.entryOf( layer: integer ): number, number, number, number ``` #### Arguments | Name | Type | Description | | --- | --- | --- | | `layer` | `integer` | One to `MAX`. Outside that every return is nil, since only that range is ever filled in. | #### Returns | Type | Description | | --- | --- | | `number` | The positioning mode, then the camera position's multiplier, then one when the layer ignores zoom, then one when it is lit. | | `number` | | | `number` | | | `number` | | ### tecs.gfx.layers.isScreenSpace Static Returns whether a layer uses screen-pixel coordinates. ```teal function tecs.gfx.layers.isScreenSpace(layer: integer): boolean ``` #### Arguments | Name | Type | Description | | --- | --- | --- | | `layer` | `integer` | One to `MAX`. Outside that this answers false, which is what an unconfigured layer answers too. | #### Returns | Type | Description | | --- | --- | | `boolean` | Whether `configure` selected `screenSpace` for the layer. | ### tecs.gfx.layers.revision Static Returns the layer table's configuration revision. ```teal function tecs.gfx.layers.revision(): integer ``` #### Arguments None. #### Returns | Type | Description | | --- | --- | | `integer` | A count that starts at zero and only rises. A consumer compares it against the value it packed at, so the number itself means nothing beyond having changed. | ### tecs.gfx.layers.sortOf Static Returns a layer's sort as the identifier accepted by `depthOf`. ```teal function tecs.gfx.layers.sortOf(layer: integer): integer ``` #### Arguments | Name | Type | Description | | --- | --- | --- | | `layer` | `integer` | One to `MAX`. Outside that this answers topdown rather than raising, since it is the default every layer starts on. | #### Returns | Type | Description | | --- | --- | | `integer` | The sort identifier, which is an internal number and not one of the `Sort` strings. | ### tecs.gfx.layers.viewCulled Static Returns whether the camera's view rectangle culls this layer. False on a layer the camera does not place where its world bound says: screen-space and virtual-coordinate contents have no world position for the view to test, and parallax and ignore-zoom draw them somewhere the bound does not describe. Extraction reads this per row and gives those contents a bound that every view contains, so they draw regardless of camera position. Layers using none of these modes retain exact culling. ```teal function tecs.gfx.layers.viewCulled(layer: integer): boolean ``` #### Arguments | Name | Type | Description | | --- | --- | --- | | `layer` | `integer` | One to `MAX`. Outside that this answers true, which is what an unconfigured layer answers too. | #### Returns | Type | Description | | --- | --- | | `boolean` | Whether extraction should write this row's real world bound. | ## Values ### tecs.gfx.layers.MAX variable Read-only. Reports how many bands divide the depth range. Load-bearing at sixteen. A sort mode packs into two bits, this value sizes the vertex shader's table, and the shader recovers a layer by multiplying depth by it. `LAYER_BANDS` in `assets/shaders/instance.vert.glsl` is the same number, and the two only work while they agree. Raising it is not a constant change. ```teal tecs.gfx.layers.MAX: integer ``` ### tecs.gfx.layers.maxY variable Caller-writable. Sets half the expected world extent used to normalize position into a band. A scene larger than this still sorts, with entities beyond the edge compressed against it. ```teal tecs.gfx.layers.maxY: number ``` ### tecs.gfx.layers.maxZ variable Caller-writable. Sets the highest z a scene expects to use and the resulting resolution of sorting within a band. A scene reaching past this still draws, with entities beyond the edge resting on it and no longer sorting against each other. ```teal tecs.gfx.layers.maxZ: number ``` ### tecs.gfx.layers.virtualHeight variable Caller-writable. Sets the vertical resolution used to author a virtual-coordinate layer. Tecs scales it with `virtualWidth` to fill the target. ```teal tecs.gfx.layers.virtualHeight: number ``` ### tecs.gfx.layers.virtualWidth variable Caller-writable. Sets the horizontal resolution used to author a virtual-coordinate layer. Every such layer shares one resolution because a game has one authored layout size. Tecs scales it to fill the target, so the layout keeps its internal proportions at any window size and stretches with the window's aspect. ```teal tecs.gfx.layers.virtualWidth: number ```