8. Light

The engine provides an opt-in, per-target lighting system exposed through the global mbm table. Lighting uses a classic (Phong-style) material model: ambient, directional (diffuse) and point lights combine with each subset’s material to produce the final lit color.

Attention

Lighting state is tracked separately per target. The only two valid target strings are '3d' and '2dw' (exact, case sensitive). '2ds' lighting is not implemented.
Lighting is disabled by default for both targets; call setLightEnabled to turn it on.
Loading a new scene resets all light state for both targets back to defaults.

8.1. Lighting only affects objects created after it is turned on

Attention

Whether an object receives lighting at all is decided once, at the moment its default shader is built (when it is loaded, or when an animation is added to it) — not every frame, and not retroactively. It depends on whether setLightEnabled was already true for that object’s target at that exact moment.
Calling setLightEnabled afterwards only updates the light values consumed by objects that were already classified as lit; it does not upgrade objects that were already classified as unlit, and it does not downgrade lit objects back to unlit either.

This is an easy contract to trip over, so here is the exact scenario:

-- lighting for '3d' is still OFF here
local meshesBeforeLight = {}
for i = 1, 5 do
    meshesBeforeLight[i] = mesh:new('3d')
    meshesBeforeLight[i]:load('crate.msh') -- classified unlit right now
end

mbm.setLightEnabled('3d', true) -- turn the light ON

local meshesAfterLight = {}
for i = 1, 3 do
    meshesAfterLight[i] = mesh:new('3d')
    meshesAfterLight[i]:load('crate.msh') -- classified lit right now
end

-- meshesBeforeLight (5 objects) stay unlit forever, even though the '3d' light is now on.
-- meshesAfterLight (3 objects) are lit and will react to mbm.setAmbientLight/setDirectionalLight/etc.

If you need previously-created objects to become lit (or unlit), you have to make them go through that same classification moment again — for example by calling load (or the equivalent load/loadAsync method for sprite, tile, particle or backGround) again after changing setLightEnabled, or by adding a fresh animation to the object. Simply toggling the light does not reach back and change shaders that already exist.

Note

In practice this means: decide and set lighting for a target with setLightEnabled before creating/loading the objects that should be lit for that target.

8.2. Light methods

8.2.1. setLightEnabled

setLightEnabled(string target, boolean enabled)

Enable or disable lighting for a target. When disabled the target renders unlit, same as before lighting existed.

Parameters
  • stringtarget either '3d' or '2dw'.

  • booleanenabled value.

Example:

mbm.setLightEnabled('3d', true)

8.2.2. resetLight

resetLight(string target)

Reset every light setting for a target back to its default values: disables lighting, restores the default ambient/directional colors and direction, clears the point-light list added through addPointLight, and restores the default requested max lights and selection mode.

Parameters

stringtarget either '3d' or '2dw'.

Example:

mbm.resetLight('3d')

8.2.3. getLightState

getLightState(string target)

Return a snapshot table describing the current light state of a target.

Parameters

stringtarget either '3d' or '2dw'.

Returns

table with the fields below.

enabled - boolean.
target - string, '3d' or '2dw'.
requestedMaxLights - number.
supportedMaxLights - number.
validatedMaxLights - number (0 if the requested value is invalid).
lightSelectionMode - string.
ambientColor - {r, g, b, a}.
directionalColor - {r, g, b, a}.
directionalDirection - {x, y, z} (normalized).
pointColor - {r, g, b, a} of the legacy single point light slot.
pointPosition - {x, y, z} of the legacy single point light slot.
pointRadius - number of the legacy single point light slot.
pointLights - 1-indexed array of {position={x,y,z}, radius, color={r,g,b,a}}, one per light added with addPointLight.

Example:

local light = mbm.getLightState('3d')
print(light.enabled, light.ambientColor.r)

8.2.4. Default values

enabled = false
ambientColor = {r = 0.2, g = 0.2, b = 0.2, a = 1.0}
directionalColor = {r = 1.0, g = 1.0, b = 1.0, a = 1.0}
directionalDirection normalized {x = 0.0, y = -0.707, z = -0.707}
requestedMaxLights equals the engine’s compiled maximum (currently 4)
lightSelectionMode = 'per_object_nearest'
point-light list (see addPointLight) starts empty

Color channels passed to any light function below are silently clamped to the 0..1 range.

8.3. Ambient & Directional Light

8.3.1. setAmbientLight

setAmbientLight(string target, table color)

Set the ambient light color of a target, applied uniformly regardless of light direction or distance.

Parameters
  • stringtarget either '3d' or '2dw'.

  • tablecolor {r, g, b, *a} (a optional, defaults to 1.0).

Example:

mbm.setAmbientLight('3d', {r = 0.2, g = 0.2, b = 0.2, a = 1.0})
setAmbientLight(string target, number r, number g, number b, number * a)

Same as above passing the color channels directly instead of a table.

Parameters
  • stringtarget either '3d' or '2dw'.

  • numberr red channel.

  • numberg green channel.

  • numberb blue channel.

  • numbera alpha channel (optional, defaults to 1.0).

Example:

mbm.setAmbientLight('3d', 0.2, 0.2, 0.2)

8.3.2. setDirectionalLight

setDirectionalLight(string target, table direction, table color)

Set the directional (diffuse) light of a target: a single light with a direction but no position, similar to sunlight.

Parameters
  • stringtarget either '3d' or '2dw'.

  • tabledirection {x, y, z}. It is normalized internally; a zero-length direction silently falls back to the default direction instead of raising an error.

  • tablecolor {r, g, b, *a} (a optional, defaults to 1.0).

Example:

mbm.setDirectionalLight('3d',
    {x = 0.0, y = -0.707, z = -0.707},
    {r = 1.0, g = 1.0, b = 1.0, a = 1.0})
setDirectionalLight(string target, number x, number y, number z, number r, number g, number b, number * a)

Same as above passing direction and color channels directly instead of tables.

Parameters
  • stringtarget either '3d' or '2dw'.

  • numberx, y, z direction.

  • numberr, g, b color, a alpha (optional, defaults to 1.0).

8.3.3. setDirectionalLightDirection

setDirectionalLightDirection(string target, table direction)

Update only the direction of the target’s directional light, keeping its current color.

Parameters
  • stringtarget either '3d' or '2dw'.

  • tabledirection {x, y, z} (normalized internally, same zero-length fallback as above).

setDirectionalLightDirection(string target, number x, number y, number z)

Same as above passing the direction channels directly.

8.3.4. setDirectionalLightColor

setDirectionalLightColor(string target, table color)

Update only the color of the target’s directional light, keeping its current direction.

Parameters
  • stringtarget either '3d' or '2dw'.

  • tablecolor {r, g, b, *a} (a optional, defaults to 1.0).

setDirectionalLightColor(string target, number r, number g, number b, number * a)

Same as above passing the color channels directly.

8.4. Point Light

Note

The engine keeps two independent point-light storages per target:
1. A single legacy point-light slot, mutated by setPointLight and its related setPointLight* functions below.
2. A growable list of point lights, appended to by addPointLight and emptied by clearPointLights. There is no function to update or remove a single entry of this list by index.
getSelectedPointLights uses the list when it is not empty. If the list is empty, it falls back to treating the legacy slot as a single candidate light.

8.4.1. setPointLight

setPointLight(string target, table position, number radius, table color)

Set the legacy single point-light slot of a target (position, radius and color).

Parameters
  • stringtarget either '3d' or '2dw'.

  • tableposition {x, y, z} (or a vec3).

  • numberradius of influence. Negative values are clamped to 0.

  • tablecolor {r, g, b, *a} (a optional, defaults to 1.0).

Example:

mbm.setPointLight('3d', {x = 0, y = 0, z = 128}, 512, {r = 1, g = 1, b = 1, a = 1})
setPointLight(string target, number x, number y, number z, number radius, number r, number g, number b, number * a)

Same as above passing position and color channels directly instead of tables.

8.4.2. setPointLightPosition

setPointLightPosition(string target, table position)

Update only the position of the legacy single point-light slot.

Parameters
  • stringtarget either '3d' or '2dw'.

  • tableposition {x, y, z} (or a vec3).

setPointLightPosition(string target, number x, number y, number z)

Same as above passing the position channels directly.

8.4.3. setPointLightRadius

setPointLightRadius(string target, number radius)

Update only the radius of the legacy single point-light slot. Negative values are clamped to 0.

Parameters
  • stringtarget either '3d' or '2dw'.

  • numberradius of influence.

8.4.4. setPointLightColor

setPointLightColor(string target, table color)

Update only the color of the legacy single point-light slot.

Parameters
  • stringtarget either '3d' or '2dw'.

  • tablecolor {r, g, b, *a} (a optional, defaults to 1.0).

setPointLightColor(string target, number r, number g, number b, number * a)

Same as above passing the color channels directly.

8.4.5. addPointLight

addPointLight(string target, table position, number radius, table color)

Append a new point light to the target’s point-light list. There is no fixed cap enforced when adding - only getSelectedPointLights limits how many of them are actually used at render time (see setRequestedMaxLights).

Parameters
  • stringtarget either '3d' or '2dw'.

  • tableposition {x, y, z} (or a vec3).

  • numberradius of influence. Negative values are clamped to 0.

  • tablecolor {r, g, b, *a} (a optional, defaults to 1.0).

Example:

mbm.addPointLight('3d', {x = 100, y = 0, z = 0}, 300, {r = 1, g = 0, b = 0, a = 1})
mbm.addPointLight('3d', {x = -100, y = 0, z = 0}, 300, {r = 0, g = 0, b = 1, a = 1})
addPointLight(string target, number x, number y, number z, number radius, number r, number g, number b, number * a)

Same as above passing position and color channels directly instead of tables.

8.4.6. clearPointLights

clearPointLights(string target)

Remove every point light previously added with addPointLight for a target. This is the only way to remove point lights from the list; there is no function to remove a single entry by index.

Parameters

stringtarget either '3d' or '2dw'.

Example:

mbm.clearPointLights('3d')

8.5. Multi-light Limits & Selection

Note

The engine renders a bounded number of point lights per object (shader arrays need a fixed size). There are three related numbers per target: what the game requests, what the active backend actually supports, and what ends up validated (the smaller of the two).

8.5.1. setRequestedMaxLights

setRequestedMaxLights(string target, number max_lights)

Request how many point lights should be considered per object for a target. Values below 1 are silently raised to 1. Requesting more than getSupportedMaxLights raises a Lua error.

Parameters
  • stringtarget either '3d' or '2dw'.

  • numbermax lights requested.

Example:

mbm.setRequestedMaxLights('3d', 4)

8.5.2. getSupportedMaxLights

getSupportedMaxLights(string target)

Get the maximum number of point lights the active backend supports for a target (currently a fixed engine constant regardless of graphics backend).

Parameters

stringtarget either '3d' or '2dw'.

Returns

number.

8.5.3. getValidatedMaxLights

getValidatedMaxLights(string target)

Get the currently requested max lights re-validated against the backend’s supported max lights for a target.

Parameters

stringtarget either '3d' or '2dw'.

Returns

number.

8.5.4. setLightSelectionMode

setLightSelectionMode(string target, string mode)

Set how point lights are selected per object for a target. Currently the only supported (and default) mode is 'per_object_nearest'.

Parameters
  • stringtarget either '3d' or '2dw'.

  • stringmode, exact string 'per_object_nearest'.

8.5.5. getSelectedPointLights

getSelectedPointLights(string target, table objectCenter, table objectBoundingAABB)

Compute which point lights (from the list added through addPointLight) would affect an object, following the target’s selection mode. Lights whose distance to objectCenter is greater than their own radius plus the object’s radius are discarded; the remaining ones are sorted by distance (nearest first) and truncated to min(validatedMaxLights, supportedMaxLights) entries.

Parameters
  • stringtarget either '3d' or '2dw'.

  • tableobjectCenter {x, y, z} (or a vec3).

  • tableobjectBoundingAABB {x, y, z} full size (not half-extents) of the object’s bounding box.

Returns

1-indexed table array, each entry shaped as {sourceIndex, distanceToObjectCenter, position={x,y,z}, radius, color={r,g,b,a}}.

Example:

local lights = mbm.getSelectedPointLights('3d', tMesh:getPos(), tMesh:getAABB())
for i, l in ipairs(lights) do
    print(i, l.sourceIndex, l.distanceToObjectCenter, l.radius)
end