Log inGet started
World

Builtin Library

Branchmain
Branchesmainpublish
Commit3bb1da2647

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.