Builtin Library
readme.md
meshLod
A mesh costs the same to draw at 200 m as it does at 2 m. This package picks a cheaper mesh once the subject stops covering enough of the frame for the difference to be visible.
What decides the level
The fraction of the viewport's height the subject's own bounds span:
fraction = radius / (distance * tan(fovY / 2))
radius is half the diagonal of the entity's world-space box, so it holds for
any proportions, and it already includes the entity's scale. Selecting on this
rather than on distance means a boulder and a pebble drop to their cheaper
meshes at the ranges their sizes earn, and a scene that changes field of view
re-selects without any threshold being re-authored.
Authoring
Add MeshLod beside a Model. The mesh the Model already carries is level 0.
entity.find("rock").component.add("MeshLod", {
lod1 = "@builtin::meshes.rock_mid", screen1 = 0.25,
lod2 = "@builtin::meshes.rock_far", screen2 = 0.08,
})
screenN is the fraction below which that level takes over. A gap ends the
chain — setting lod3 while leaving lod2 empty stops at level 1, rather than
silently skipping the level that was meant to draw.
hysteresis (default 0.15) is the extra span required to climb back to a
richer level, as a fraction of the threshold that was crossed. Leaving a level
happens at the threshold; returning to it takes 1 + hysteresis times that.
Reading the state
@builtin::systems.meshLod.meshLod owns the selection, so it can be measured
without a camera:
local MeshLod = require("@builtin::systems.meshLod.meshLod")
MeshLod.screenFraction(1.5, 40, 60) -- what a subject spans
MeshLod.selectLevel(0.08, {0.25, 0.10}, 1, 0.15) -- which level that earns
MeshLod.levelOf(id) -- what an entity draws now
MeshLod.stats() -- every tracked entity at once
Removing the component restores the mesh the Model started with.