Log inGet started
·
preset · drop-in viewer
asset⌬ presetpresetprimary: preset.yaml·part ofpackage volumetrics.package·originates fromworld 07158574-5…

cumulus_default

Default weather preset for `VolumetricSky.component`. Captures the v4-tuned cumulus look: visible discrete clouds, warm-afternoon sun, moderate coverage.

byzero-proxy @ DESKTOP-DB3UJOJ·posted 2mo ago
What it does

cumulus_default

Default weather preset for VolumetricSky.component. Captures the v4-tuned cumulus look: visible discrete clouds, warm-afternoon sun, moderate coverage.

Use

local sky = entity.spawn("sky")
sky.component.add("VolumetricSky", {})
preset.apply("cumulus_default", { entity = sky.id, component = "VolumetricSky" })

The values set the component's preset Field.table as a unit — one Sync replication carries every shaping/lighting parameter to peers. Tweak by reading the values and changing them before the component is added:

local values = preset.values("cumulus_default")
values.shaderVariant = "v5"        -- swap raymarcher
values.preset.coverage = 0.6       -- sparser sky
sky.component.add("VolumetricSky", values)

Authoring more presets

Capture a sky you have tuned with preset.capture({ entity = sky.id, component = "VolumetricSky" }, "<name>"), or drop another <name>.preset/ folder next to this one (or anywhere under /zero/source/); the asset indexer registers it and preset.values("<name>") resolves it. Common alternatives:

  • stratocumulus.preset — broad overcast layer (coverage ~= 0.15, cloudType = 0.5, altitudeHigh ~= 0.65).
  • clear.preset — barely any cloud (coverage ~= 0.7, densityGain ~= 0.2).
  • storm.preset — dense black anvils (coverage ~= 0.1, densityGain ~= 0.85, sunColor ~= [0.6, 0.55, 0.5]).

preset is one Field.table: applying a preset replaces it whole, so provide every field.

Interface

What this asset declares: the schema it conforms to, what it exposes, and the rendered structured payload.

This asset doesn't declare a schema or structured payload.
⌬ Metadata
descriptionThe v4 cumulus look for VolumetricSky.titleCumulus Default

Sub-parts

Everything contained inside this part. Assets are composite children (clickable cards). Files are leaf payloads. Expand any row to view its source.

64items
·
other · born here
▤file
▲ 0↑ born
◇
component · born here
❒asset
# VolumetricSky Singleton component that drives the procedural cloud layer. Attach to one entity (usually a sky placeholder), and the component owns the cloud volume + render target lifecycle, regenerates the noise field on demand, and dispatches the compute raymarcher every frame into `scene_volumetric_sky`. A composite (post-process or material) blends `scene_volumetric_sky` over the scene — see `demo/sky.scene/` for the full wiring. ## Usage ```luau -- Defaults (the v4-tuned cumulus look — `Sky.defaultPreset()` hands out a -- copy of the same table this field defaults to) local sky = entity.spawn("sky") sky.component.add("VolumetricSky", {}) -- Or apply a captured preset: preset.apply("cumulus_default", { entity = sky.id, component = "VolumetricSky" }) -- Inline tweak. `component.add` writes the `preset` field as it is given, -- so a table handed here carries the whole configuration: local p = require("@builtin::systems.volumetrics.volumetricSky").defaultPreset() p.noiseScale, p.octaves, p.coverage = 9.0, 5, 0.20 p.densityGain = 0.04 -- v5 σ_t sky.component.add("VolumetricSky", { shaderVariant = "v5", preset = p }) ``` `volumetricSky.module`'s `M.spawn` / `M.configure` take the same parameters by name, folding each into the current configuration and leaving the rest where they stand: ```luau local Sky = require("@builtin::systems.volumetrics.volumetricSky") Sky.spawn({ shaderVariant = "v5", noiseScale = 9.0, coverage = 0.20 }) ``` ## Schema (Field<T> / Sync / NoSync) The 28 shaping / weather / lighting params travel as ONE Sync field — `preset = Field.table(DEFAULT_PRESET, Sync)` — so replicating a sky change is **one mutation, not 28**, and copying a sky between scenes is **copying one field, not 28**. Per-client quality knobs sit outside the preset as NoSync fields so each client picks its own tier without disturbing peers. `SkyPreset` and `DEFAULT_PRESET` are declared by `volumetricSky.module`, which this component reads its default from and which routes an option by that same key set — so the shape below and the module's `M.spawn` options are one declaration. | Field | Type | Sync | Default | Meaning | |---|---|---|---|---| | `preset` | `SkyPreset` table | Sync | `DEFAULT_PRESET` (v4 cumulus) | All shaping / weather / lighting (see below). | | `shaderVariant` | string | Sync | `"v4"` | `"v3" \| "v4" (default) \| "v5"`. | | `wind` | vec3 | Sync | `{0,0,0}` | Animated drift offset (advanced per frame from `preset.windSpeed`). | | `cloudSize` | number | NoSync | `256` | Volume W,D resolution. | | `cloudHeight` | number | NoSync | `128` | Volume H resolution. | | `stepSize` | number | NoSync | `4.0` | World units per ray step. | | `maxSteps` | number | NoSync | `224` | Ray-march step cap. | | `shadowSteps` | number | NoSync | `8` | Shadow ray step count. | | `alphaCutoff` | number | NoSync | `0.99` | Early-exit α threshold. | | `jitter` | number | NoSync | `1.0` | Ray-origin jitter strength. | | `taaBlendAlpha` | number | NoSync | `0.1` | TAA current-sample weight. | | `targetWidth` | number | NoSync | `0` | Render target width (`0` = viewport). | | `targetHeight` | number | NoSync | `0` | Render target height. | ### Preset (`SkyPreset`) shape Noise / shaping: `noiseScale, octaves, coverage, densityGain, altitudeLow, altitudeHigh, anvilBias, detailScale, detailOctaves, detailStrength, detailThreshold, baseLowRemap, baseHighRemap, color, windSpeed, seed`. Volume placement: `horizExtent, cloudBaseAltitude, cloudTopAltitude`. Look (visual, not perf): `phaseG`. Sun / sky: `sunDir, sunColor, sunIntensity, sunDiscSize, horizonColor, zenithColor, groundColor, ambient`. v5-only: `cloudType, worleyBlend`. See `presets/cumulus_default.preset/preset.yaml` for the canonical full preset shape, and the `init.luau` `DEFAULT_PRESET` table for the runtime defaults. ## Field-mutation gotcha (and why `update()` reassigns the whole vec3) Under the new Field<T> / Sync / NoSync schema, `public` is not a plain Lua table — it's wrapped in a proxy whose `__newindex` metamethod performs type-checks, marks the field dirty, and queues Sync replication. Writing `public.wind = {x,y,z}` triggers all four steps. Writing `public.wind[1] = x` reads `public.wind` (getter), mutates the returned table directly, and **bypasses the setter** — no Sync replication, no dirty flag, no `onPropertyChanged`. Always assign the whole vec3 / preset / color table; never index-mutate. The same caveat applies to `public.preset`: `public.preset.coverage = 0.2` won't replicate. To change one preset field, copy the table, mutate, reassign: ```luau local p = public.preset local next = {} for k, v in pairs(p) do next[k] = v end next.coverage = 0.2 public.preset = next ``` ## Lifecycle - **`awake`** — resets the dirty flag. - **`update(dt)`** — advances `wind` (reading `preset.windSpeed`), refills the noise texture (cheap GPU dispatch), then queues the raymarch dispatch for the frame's deferred phase. - **`onPropertyChanged(key)`** — sets `noiseDirty` for `preset`, `cloudSize`, `cloudHeight`, `shaderVariant` so the next refill captures the change. - **`onDespawn`** — releases the volume, the render target and the composite effect via `M.destroy`. ### Why the raymarch is dispatched from `task.defer` The raymarch produces ONE full-screen layer for ONE view, and the frame composites it over everything that frame drew — so it has to be rendered from the view the frame ends up drawn with. Which view that is settles only once the frame's script work is finished: a cutscene walking the camera from a task loop, a camera controller, a component ordered after this one, an editor tool — each writes the camera after `update` has run, while the renderer reads the camera once all of them are done. Taking the camera snapshot in the deferred phase reads what every one of them has already written, and still queues the dispatch ahead of the frame's render. Snapshotting in `update` instead renders the layer for whatever the camera was midway through the frame; on a hard cut that is a different place entirely, and the frame the cut lands on composites a cloud layer belonging to the shot it just left. A caller driving `volumetricSky.dispatchRaymarch` directly owes the same discipline. ## Related - `volumetricSky.module` — orchestration backend. - `presets/cumulus_default.preset` — canonical full preset; copy to author named weather (storm, overcast, dawn, …). - `Volume.component` — per-entity flow for non-cloud volumetric content. - `demo/sky.scene` — worked example.
▲ 0↑ born
❒
package · born here
❒asset
# volumetrics Volumetric (3D-texture) rendering, end to end, in one package. Built entirely on the engine's generic `compute.*` GPU primitives — no Rust changes are needed to author new volumetric content. ## Layout | Asset | Identity | Role | |---|---|---| | `Volume.component` | `@builtin::systems.volumetrics.Volume` | Attach a 3D voxel texture to an entity; raymarches it through the entity's transform AABB. | | `VolumetricSky.component` | `@builtin::systems.volumetrics.VolumetricSky` | Singleton procedural cloud layer with TAA. | | `ParticipatingMedium.component` | `@builtin::systems.volumetrics.ParticipatingMedium` | Mark an entity as fog/smoke/dust that attenuates the light passing through it. | | `mediaTransmittance.module` | `@builtin::systems.volumetrics.mediaTransmittance` | Light-space optical-depth map: the registry, the settings, and the basis the map is built in. | | `mediaShadow.renderFeature` | `@builtin::systems.volumetrics.mediaShadow` | Builds the optical-depth map and attenuates each surface by the density in front of it. | | `volume.module` | `@builtin::systems.volumetrics.volume` | Builders (`noise`/`sphere`/`box`/`wrap`) + the `.zvol` header reader + occupancy + brick paging + the per-entity raymarch dispatch. | | `VolumeSequence.component` | `@builtin::systems.volumetrics.VolumeSequence` | Play an open volume sequence through an entity's transform AABB. | | `volumeSequence.module` | `@builtin::systems.volumetrics.volumeSequence` | A run of `.zvol` frames streamed through a fixed ring of resident volumes. | | `volumeDemos.module` | `@builtin::systems.volumetrics.volumeDemos` | Shared pipeline for the showcase scenes (fill + emissive raymarch + composite). | | `volumetricSky.module` | `@builtin::systems.volumetrics.volumetricSky` | Cloud volume + raymarch + TAA + composite orchestration. | | `shaders/*.shader` | `@builtin::systems.volumetrics.shaders.*` | `volume_raymarch`, `volume_pages`, `volume_pack`, `cloud_noise[_v3/_v5]`, `cloud_raymarch[_v3/_v4/_v5]`, `cloud_taa`. | | `importers/vdb_to_zvol.importer` | (auto-discovered) | `.vdb` → dense `.zvol` via the `openvdb` WASM plugin (procedural fallback when the plugin isn't loaded). | | `presets/cumulus_default.preset` | `@builtin::systems.volumetrics.cumulus_default` | Canonical full `VolumetricSky` weather preset. | | `demo/*.scene` | `@builtin::systems.volumetrics.demo.*` | `sdf_solids`, `smoke_plume`, `nebula`, `godrays`, `zvol_viewer`, `sky`. | ## Path convention — fully-qualified builtin identities Every intra-package reference uses the asset's fully-qualified builtin identity, the same convention every other builtin package follows (cf. `particles.package`): ```luau local Volume = require("@builtin::systems.volumetrics.volume") local Sky = require("@builtin::systems.volumetrics.volumetricSky") local V = require("@builtin::systems.volumetrics.volumeDemos") -- shader identities, e.g. asset.resolve("@builtin::systems.volumetrics.shaders.volume_raymarch") ``` Qualified identities are caller-independent: they resolve identically from a module, a scene entrypoint, or a component. Caller-relative (`.sibling`) requires are deliberately avoided here because the component runtime caller identity is `@builtin::components.<Name>`, so a `.sibling` require from a component resolves against the wrong root. (An earlier revision of this package used a `~volumetrics` package-root alias for relocatability; that alias is not resolvable on current main, so the package uses qualified identities throughout.) ## Usage ```luau -- Per-entity volume. The component resolves the name through `Volume.get`, -- so a volume paged into a brick pool renders from the pool under the same -- name: local Volume = require("@builtin::systems.volumetrics.volume") local cloud = Volume.noise({ name = "cloud", size = 64, scale = 4 }) cloud:buildSparse({ brickSize = 8 }) cloud:releaseDense() entity(id).component.add("Volume", { volumeName = "cloud", density = 6.0 }) -- Procedural sky: local sky = entity.spawn("sky") sky.component.add("VolumetricSky", {}) preset.apply("cumulus_default", { entity = sky.id, component = "VolumetricSky" }) -- VDB import: drop a `.vdb` into the VFS; the importer writes a `.zvol` -- alongside it. Load it with the zvol_viewer demo scene. -- Animated run: one `.zvol` per frame, played through a bounded ring. local Seq = require("@builtin::systems.volumetrics.volumeSequence") Seq.open({ name = "plume", frames = framePaths, resident = 3, fps = 24 }) entity(id).component.add("VolumeSequence", { sequenceName = "plume", density = 6.0 }) ``` ## Plugin dependency The VDB importer delegates real OpenVDB parsing to the `openvdb` plugin in the builtin library, `@builtin::plugins.openvdb`. Without the plugin the importer falls back to a deterministic procedural torus so the pipeline stays functional.
▲ 0↑ born
·
renderfeature · born here
❒asset
# volumetricSky The cloud layer's render passes. The layer is raymarched for the camera being drawn, copied into the target the composite reads, temporally accumulated where there is a previous frame of that view to accumulate against, and laid over the frame behind whatever the depth buffer holds. Every pass but the accumulation runs for each view the frame draws, an environment cube's six faces included — which is what puts the clouds into reflections. The accumulation blends against the previous frame of the same view, and a cube face is rendered once with no previous frame, so it declares `environmentCapture = false` and the copy before it is what those faces read. `VolumetricSky` creates this feature; it is not meant to be created by hand.
▲ 0↑ born
◇
component · born here
❒asset
# Camera Manages viewport priority, render-to-texture, and capture. State is stored in the native `Camera` ECS component; the Rust camera system handles render scheduling and render targets. Public fields: `fov`, `near`, `far`, `priority`, `textureHandle` (the guid of the texture the camera renders into; empty = main viewport), `renderLayers` (which render layers this camera draws — a space-separated spec of names, e.g. `"all"`, `"all !ui"`, or `"default sky"`; `ui`/`sky`/`debug`/`EditorUI` are built-in layers), `postProcessing` (whether this camera runs the post-process chain), `debugChannel`, plus the behavior slot below. Methods: `:lookAt(target)` (entity id string OR `{x, y, z}` table), `:render()`, `:capture()`, `:setTargetTexture(tex?)` (a `renderer.texture.create` handle to render into, or nil for the viewport). ```luau entity(id).component.add("Camera", { fov = 90, priority = 10 }) entity(id).component.get("Camera"):capture() ``` The scene's play-mode camera is reached as `layers.active.camera`, a handle that reads and writes the fields above on whichever entity currently carries them. `layers.active.camera.entity` is the entity ref for that camera and `layers.active.camera.entityId` its id string, so a script that needs to attach something to the camera — an `AudioListener`, a child entity — goes through the ref: ```luau local cam = layers.active.camera cam.fov = 70 -- the camera's settings cam.entity.component.add("AudioListener") -- the entity carrying them ``` ## How the camera moves: `behavior` and `follow` A `Camera` does not move itself. `behavior` names a component that does, and setting it attaches that component to this entity. Clearing it detaches whatever was attached. ```luau local cam = entity(id).component.get("Camera") cam.behavior = asset.ref("@builtin::controller.orbital_follow", "component") cam.follow = playerBody ``` `follow` is the standard slot every shipped behavior reads. Set the follow target on the **Camera**, not on the behavior, so swapping behaviors keeps it. A behavior that finds its own `follow` field empty falls back to this one, which is what lets a rig keep tracking the player across a behavior swap. `followResolves` answers whether that slot names an entity that is live — `true` while it names a live one or names nothing at all, `false` once the target is despawned or the id names no entity. A rig whose target does not resolve holds its last pose, and this is the field that tells it from a rig posed correctly on a subject that has not moved. `camera.get` carries the same value beside `follow`, and a write naming an id with no entity behind it draws a warning where it lands. ```luau cam.followResolves -- false once the followed entity is gone tools.use("camera", "get", id).followResolves -- the same answer off the tool ``` The shipped behaviors live under `@builtin::controller.*`: `orbital_follow`, `third_person_follow`, `first_person`, `free`, `orbit`, `chase`, `isometric`, `rts`, `birds_eye`, `side_scroller`, `cinematic`, `menu`. ## Writing your own Any component can be a camera behavior. Write one that moves its own entity and attach it the same way: ```luau cam.behavior = asset.ref("MyChaseCam", "component") ``` `follow` lives on the Camera, so a behavior gets no property notification of its own when the target changes. Declare `onFollowChanged(newFollow, oldFollow)` to be told the moment it does — the Camera calls it on the component it attached, which is what lets a rig re-pose on the new subject at once. A behavior that reads `Camera.follow` on its own schedule declares nothing and is attached the same way. ```luau typed function public:onFollowChanged(newFollow: any, oldFollow: any) -- pose this entity against the new target end ``` To make it appear in the discovery catalog alongside the shipped ones, declare the `cameraBehavior` tag in the component's `.metadata`: ```json { "tags": ["cameraBehavior"] } ``` The tag governs **discovery**, not attachment. A tagged component is listed by `layers.active.camera.behaviors` and is what tooling offers when something asks "which camera behaviors exist"; an untagged component attaches just as well and simply stays out of that list. Tag the ones you want other people (and agents) to find. ```luau for name, ref in pairs(layers.active.camera.behaviors) do print(name, ref.identity) end ``` ## Texture colour space `textureColorSpace = "display"` applies the display transform when rendering into a texture. `"linear"` writes scene-linear values instead. `postProcessing` independently controls the effects chain in either mode. Floating-point targets retain values above one; normalized targets clamp to their representable range. The main viewport uses display encoding.
▲ 0↑ born
▣
module · born here
❒asset
# volumetricSky Orchestration backend for the `VolumetricSky.component`. Owns the GPU resources the cloud layer needs (cloud volume, raymarch render target, ping-pong TAA history pair, post-process composite) and exposes a small public surface the component drives every frame. ## Exports - `M.spawn(opts?) -> id` — spawn the singleton sky entity, or configure the one already standing, and return its id. Every weather parameter (`coverage`, `sunDir`, `sunColor`, `sunIntensity`, `windSpeed`, …) is settable at the top level or inside a `preset` sub-table; every `VolumetricSky` component field (`shaderVariant`, `cloudSize`, `taaBlendAlpha`, …) by its own name; `name` names the entity. Parameters left out keep their current values. An option that names neither raises, listing both sets, and leaves no entity behind. A weather parameter takes the shape its default carries — a number, or an array of that many numbers — and a value of another shape raises naming the parameter. - `M.configure(opts) -> boolean` — apply those same weather parameters and component fields to the sky already standing. Returns `false` when none has been spawned. - `M.defaultPreset() -> table` — a fresh copy of the weather configuration a sky spawned with no options carries. The copy is the caller's to edit. - `M.destroy() -> ()` — release every resource `spawn` created. - `M.fillClouds(noiseParams) -> ()` — dispatch the active `cloud_noise_*` shader against the cloud volume. `noiseParams` is the flat table produced by `VolumetricSky.component`'s `snapshotNoiseParams()`. - `M.dispatchRaymarch(renderParams, viewport) -> ()` — dispatch the active `cloud_raymarch_*` shader, blend with history via the TAA pass, and write into the composite target. `renderParams` is the flat table from `snapshotRenderParams()`. - `M.find() -> id?` — return the id of the active VolumetricSky entity, or `nil` if none has been spawned by `M.spawn`. - `M.despawn() -> ()` — despawn the entity `M.spawn` minted (does NOT free GPU resources — call `M.destroy()` for that). - `M.setCoverage(c) / M.setDensity(g) / M.setSun(dir, color, intensity?) / M.setWind(v)` — convenience setters that route through the active component's `public` fields. - `M.reprojectionDepthNdc(camPos, fwd, aabbMin, aabbMax, near, far) -> number` — the NDC z the TAA pass carries history through, taken from where the cloud stands along the centre view ray. A point at range `d` travels `translation / d` across the screen, so this is what keeps a translating camera's history landing on the part of the picture the cloud moved to. ## Usage ```luau -- The component drives this module automatically. Direct M.* use is -- rare; the canonical entry point is attaching VolumetricSky to an -- entity (or loading a preset and passing it to component.add): local sky = entity.spawn("sky") sky.component.add("VolumetricSky", {}) -- Driving the layer by hand instead, a frame at a time. Each dispatch -- creates the resources it needs at the size its own arguments name — -- the cloud volume from `noiseParams.cloudSize / cloudHeight`, the -- render target and composite from the viewport: local Sky = require("@builtin::systems.volumetrics.volumetricSky") Sky.fillClouds(noiseParams) Sky.dispatchRaymarch(renderParams, { width = 1280, height = 720 }) ``` ## Notes - `shaderVariant` selects which compute shader pair (`cloud_noise_v3 + cloud_raymarch_v3` / `cloud_noise + cloud_raymarch_v4` / `cloud_noise_v5 + cloud_raymarch_v5`). The module resolves the variant strings to absolute identities — relative paths don't work from a module body (the chunkname retains an `.init` segment that throws off the rsplit-to-parent step in `resolve_relative_dot`). - The cloud volume + history pair are RGBA16F — 256×128×256 ≈ 32 MiB VRAM. Drop to 192/96 via `cloudSize` / `cloudHeight` if tight. - The TAA pass is a single compute dispatch blending current sample with the previous frame's RT through the history pair. Set `taaBlendAlpha = 1.0` to disable while still going through the same code path (useful for benchmarking). - All public setters write through to `VolumetricSky.component`'s `public` fields, which means they replicate as Sync if the component is sync-enabled. Tweaks survive scene reloads as long as the same entity is alive. - `SkyPreset` and its defaults are declared here, and the component declares its `preset` field from them. `M.spawn` / `M.configure` read that same key set to tell a weather parameter from a component field, so a parameter added to the preset reaches the schema and the option routing in one edit.
▲ 0↑ born
·
other · born here
▤file
▲ 0↑ born
▣
module · born here
❒asset
# environment Module Environment / reflection capture — bake the scene into reflection-probe cube slots from world positions, persist them as `faces6` `.texture` assets, set per-probe blend data so surfaces reflect the probes covering them, and capture the sky into its own slot as the fallback under them. Public Luau surface over the `__environment` Internal FFI namespace, auto-injected as `_G.environment` via the prelude. ## Purpose The generic "render the scene into a cubemap from a point" capability the reflection-probe system is built on. Captures are queued for the render system (which owns the live scene); `captureSlotToAsset` additionally yields a few frames while the GPU readback completes. Persisted cubes are `faces6` `.texture` assets (px/nx/py/ny/pz/nz PNGs + a `cube.yaml` sidecar — see `docs/specs/cubemap-textures.md` §4 for the face convention). For probe authoring use the higher-level `reflectionProbe` module; reach for `environment` when you need the raw per-slot primitives. ## Usage ```luau -- Register probe blend data: index i maps to cube slot i. environment.setProbes({ { x = 0, y = 2, z = 0, radius = 12 } }) -- Bake slot 0 from a point (queued, next frame). environment.captureSlot(0, 0, 2, 0) -- Bake + persist to /source/probe_lobby.texture/ (yields; call from a -- task/coroutine/execute context). local path, err = environment.captureSlotToAsset("probe_lobby", 0, 0, 2, 0) -- Restore a persisted cube into a slot WITHOUT re-rendering. environment.loadSlotFromAsset("probe_lobby", 0) -- Capture the sky alone into the fallback slot: a surface no probe covers -- reflects the sky rather than black. environment.captureSky() ``` ## Exports - `environment.setProbes(probes) -> boolean` — set active probes' blend data; array of `{ x, y, z, radius, priority? }`, index i → cube slot i, gathered highest `priority` first - `environment.captureSky(x?, y?, z?) -> boolean` — render the sky alone into the fallback slot and arm it (queued) - `environment.setSkyFallback(active) -> boolean` — arm/disarm the fallback against the sky already captured (arming is refused while the slot holds none) - `environment.captureSlot(slot, x, y, z) -> boolean` — bake the scene into a slot from a point (queued) - `environment.captureSlotToAsset(name, slot, x, y, z, timeoutFrames?) -> (string?, string?)` — bake + persist as a `faces6` `.texture`; yields - `environment.loadSlotFromAsset(name, slot) -> (boolean, string?)` — upload a persisted cube into a slot without re-rendering Back-compat single-global-reflection helpers (slot 0 + one full-coverage probe): - `environment.capture(x, y, z) -> boolean` - `environment.captureToAsset(name, x, y, z) -> (string?, string?)` - `environment.loadFromAsset(name) -> (boolean, string?)`
▲ 0↑ born
▣
module · born here
❒asset
# transform Math helpers for positions, rotations, and directions on transforms. Exposed as the global `Transform` table via `--!global Transform` — no explicit require needed in user code. Functions that take an entity accept either an entity ID string or an entity proxy table from `entity("id")`. ## Exports Look-at and entity-aware helpers: - `Transform.lookAtQuat(fx, fy, fz, tx, ty, tz) -> (qx?, qy?, qz?, qw?)` — quaternion from origin toward target. Nil when degenerate. - `Transform.lookAt(entity, txOrTarget, ty?, tz?) -> (boolean, string?)` — make an entity face a world position or another entity. Both slots read world space: the subject and an entity target are read as `entity(id).position` and the aim is written as `entity(id).rotation`, so a parent under either one still leaves the aim on the point named. Returns whether the rotation was written, and the reason when it was not. - `Transform.distance(x1, y1, z1, x2, y2, z2) -> number` — Euclidean distance between two points. - `Transform.distanceBetween(entityA, entityB) -> number?` — distance between two entities' world positions. Nil when either is unresolvable. - `Transform.direction(fromX, fromY, fromZ, toX, toY, toZ) -> (dx, dy, dz)` — unit direction vector. - `Transform.directionBetween(entityA, entityB) -> (dx, dy, dz)` — unit world-space direction between two entities' world positions. Rotation shapes: A quaternion **constructor** here returns the four components as four separate values, so a caller either names them or braces the call to make one table: ```lua local qx, qy, qz, qw = Transform.quatFromAxisAngle(0, 1, 0, math.rad(90)) entity("cam").localRotation = { Transform.quatFromAxisAngle(0, 1, 0, math.rad(90)) } ``` A rotation-taking **surface** reads that table through `Transform.toQuaternion`, which also takes euler DEGREES — so `{ qx, qy, qz, qw }`, `{ x =, y =, z =, w = }`, `{ pitch, yaw, roll }` and `{ pitch =, yaw =, roll = }` all mean the same thing wherever a rotation is assigned: `entity(id).rotation` / `.localRotation`, `entityOps.spawn`, `entityOps.transform`, and the capture viewpoints. - `Transform.toQuaternion(rotation, label?) -> { qx, qy, qz, qw }` — the shared reading of a rotation a caller wrote. Raises when the value matches no form, naming what arrived; a value that is one of the shapes a quaternion helper returns is named as such along with the packing it goes in as. - `Transform.tryQuaternion(rotation, label?) -> ({ qx, qy, qz, qw } | nil, message?)` — the same reading without raising, for a surface that wants to raise the message at its own caller's line. - `Transform.readVec3(value, label?) -> { x, y, z }` — the same for a vector. - `Transform.snapVec3(v, step) -> { x, y, z }` — quantize a vector to a step grid. Quaternion construction / conversion: - `Transform.quatFromYaw(yaw)`, `Transform.quatFromYawPitch(yaw, pitch)`, `Transform.quatFromAxisAngle(ax, ay, az, angle)` — quaternion constructors. - `Transform.quatIdentity()` — identity quaternion. - `Transform.euler(qx, qy, qz, qw) -> (yaw, pitch, roll)` and the named alias `Transform.quatToEuler`. - `Transform.eulerToQuat(yaw, pitch?, roll?)` — euler-to-quaternion in YXZ order. Lerps and interpolation: - `Transform.lerp(ax, ay, az, bx, by, bz, t) -> (x, y, z)` — vec3 lerp. - `Transform.lerp1(a, b, t) -> number` — scalar lerp. - `Transform.normalizeAngle(a) -> number` — wrap angle into `[-pi, pi]`. - `Transform.lerpAngle(a, b, t) -> number` — shortest-arc angle lerp. - `Transform.slerp(ax, ay, az, aw, bx, by, bz, bw, t) -> (qx, qy, qz, qw)` — quaternion slerp with shortest-path and near-parallel fallback. Quaternion operations: - `Transform.quatMul(...) -> (qx, qy, qz, qw)` — `qa * qb` composition. - `Transform.quatInverse(qx, qy, qz, qw) -> (qx, qy, qz, qw)` — inverse (= conjugate for unit quats). - `Transform.quatRotateVec(qx, qy, qz, qw, vx, vy, vz) -> (x, y, z)` — rotate a vec3 by a quaternion. Pose helpers: - `Transform.orbit(centerX, centerY, centerZ, radius, height, angle) -> (x, y, z, qx, qy, qz, qw)` — orbital pose facing the center. - `Transform.worldToLocal(...)` / `Transform.localToWorld(...)` — pose-space conversions. Nested `Transform.vec.*` namespace (component-wise vec3): - `Transform.vec.add`, `sub`, `scale`, `dot`, `cross`, `length`, `normalize`. Types: - `Vec3 = { x: number, y: number, z: number }` - `EntityRef = string | { entityId: string }` ## Usage ```luau -- Look-at by coordinates or by target entity: Transform.lookAt("cam", 0, 1, 0) -- an entity target resolves to that entity's world position local aimed, why = Transform.lookAt("cam", "box") -- Orbit pose around a point: local x, y, z, qx, qy, qz, qw = Transform.orbit(0, 1, 0, 5, 2, t) entity.find("cam").localPosition = { x, y, z } entity.find("cam").localRotation = { qx, qy, qz, qw } -- Quaternion math: local qx, qy, qz, qw = Transform.quatFromYawPitch(math.pi / 4, 0) local sx, sy, sz, sw = Transform.slerp(0, 0, 0, 1, qx, qy, qz, qw, 0.5) -- Component-wise vec3 helpers: local nx, ny, nz = Transform.vec.normalize(1, 1, 0) ``` ## Notes - The `--!global Transform` directive promotes the module's typed functions onto the runtime universe's globals bucket, so `Transform.*` is available without any per-source `require`. - Entity-aware functions (`lookAt`, `distanceBetween`, `directionBetween`) report a missing entity or a missing transform in their return value rather than raising: `lookAt` answers `false, "unresolved"` / `"no-transform"` / `"incomplete-target"` / `"degenerate"`, `distanceBetween` answers `nil`, and `directionBetween` answers zeros. - Quaternion APIs operate on raw `(qx, qy, qz, qw)` tuples for parity with the entity proxy's `localRotation.get`/`set`. Use `Transform.quatIdentity()` rather than hand-rolling `(0, 0, 0, 1)`. - `Transform.slerp` flips the second quaternion if `dot < 0` to take the shortest path, and falls back to lerp+normalize when the inputs are within `dot > 0.9995` to avoid `1/0` near-parallel issues.
▲ 0↑ born
·
computeshader · born here
❒asset
# cloud_raymarch_v5 Production volumetric-cloud raymarcher, v5 (Nubis/Hillaire style): Hillaire multi-scatter octaves (fixes black cloud cores), energy-conserving Beer-Powder-MS lit-edge glow, a 6-offset Wrenninge cone-trace shadow, and a dual-lobe Henyey-Greenstein phase. Reads the cloud volume + occupancy textures and writes the lit raymarch result. Bindings (see bindings.yaml): `clouds` (texture3d), `occupancy` (texture3d), `cloud_smp` (sampler), `out_tex` (storage2d rgba16f), `U` (read `array<f32>` uniforms). Usage: dispatch by identity with `compute.dispatchEx("@builtin::systems.volumetrics.shaders.cloud_raymarch_v5", ...)` (textures/sampler/storage texture require resource binding, not positional buffers) — it resolves to the shader's stable guid and auto-compiles on first use (no setup).
▲ 0↑ born
·
computeshader · born here
❒asset
# cloud_noise Procedural volumetric-cloud noise fill (Schneider/Hillaire style). A low-frequency FBM gives the base blob silhouette, a high-frequency FBM erodes it near the surface for the fluffy cumulus look, plus altitude shaping (round bottom, full middle, anvil top). Writes the 3D cloud density texture. Bindings (see bindings.yaml): `clouds` (storage3d rgba16f — the cloud volume), `U` (read `array<f32>` uniforms). Usage: dispatch by identity with `compute.dispatchEx("@builtin::systems.volumetrics.shaders.cloud_noise", ...)` (storage texture requires resource binding, not positional buffers) — it resolves to the shader's stable guid and auto-compiles on first use (no setup).
▲ 0↑ born
✦
shader · born here
❒asset
▲ 0↑ born
·
computeshader · born here
❒asset
# cloud_raymarch Volumetric-sky cloud raymarcher with Frostbite-style lighting: Wrenninge/Hillaire multi-scatter approximation (3 octaves), a 5-sample cone-traced soft self-shadow, and combined Beer-Lambert + Powder so cloud edges glow toward the sun. Reads the cloud volume + occupancy textures and writes the lit raymarch result. Bindings (see bindings.yaml): `clouds` (texture3d), `occupancy` (texture3d), `cloud_smp` (sampler), `out_tex` (storage2d rgba16f), `U` (read `array<f32>` uniforms). Usage: dispatch by identity with `compute.dispatchEx("@builtin::systems.volumetrics.shaders.cloud_raymarch", ...)` (textures/sampler/storage texture require resource binding, not positional buffers) — it resolves to the shader's stable guid and auto-compiles on first use (no setup).
▲ 0↑ born
·
computeshader · born here
❒asset
# cloud_noise_v5 Volumetric-cloud noise generator, v5 (Nubis/Schneider style). Builds two foundational noises — Perlin-Worley FBM for the lumpy "cauliflower" base shape and Worley FBM for high-frequency detail erosion — under weather-map control. Writes the 3D cloud density texture. Bindings (see bindings.yaml): `clouds` (storage3d rgba16f — the cloud volume), `U` (read `array<f32>` uniforms). Usage: dispatch by identity with `compute.dispatchEx("@builtin::systems.volumetrics.shaders.cloud_noise_v5", ...)` (storage texture requires resource binding, not positional buffers) — it resolves to the shader's stable guid and auto-compiles on first use (no setup).
▲ 0↑ born
·
computeshader · born here
❒asset
# cloud_taa Cloud TAA composite — temporal anti-aliasing for the raymarch output. Reprojects last frame's blended output through the previous view-projection, color-clamps the history to the current sample's neighborhood to suppress ghosting, and exponentially blends the two. Writes both the next-frame history slot and the externally-sampled `scene_volumetric_sky` texture the composite path reads. Reprojection runs through the world point named by uniform `[37]`, the NDC z of the range the cloud stands at along the view ray — `volumetricSky.reprojectionDepthNdc` computes it. The weight the history carries falls to zero across the last `REPROJECT_BORDER_TEXELS` of the reprojected frame, so a pixel whose history lies off the previous frame reaches the raymarch's own output down a gradient rather than across a line. Bindings (see bindings.yaml — slot order must match the Luau dispatch): `cur_tex` (texture2d), `hist_prev` (texture2d), `smp` (sampler), `out_hist` (storage2d rgba16f), `out_scene` (storage2d rgba16f), `U` (read `array<f32>` uniforms). Usage: dispatch by identity with `compute.dispatchEx("@builtin::systems.volumetrics.shaders.cloud_taa", ...)` (textures/sampler/storage textures require resource binding, not positional buffers) — it resolves to the shader's stable guid and auto-compiles on first use (no setup).
▲ 0↑ born
·
computeshader · born here
❒asset
# cloud_raymarch_v3 Volumetric-sky cloud raymarcher (Frostbite-style lighting), v3 variant: Wrenninge/Hillaire multi-scatter (3 octaves), 5-sample cone-traced soft shadow, and combined Beer-Lambert + Powder edge glow. Reads the cloud volume + occupancy textures and writes the lit raymarch result. Bindings (see bindings.yaml): `clouds` (texture3d), `occupancy` (texture3d), `cloud_smp` (sampler), `out_tex` (storage2d rgba16f), `U` (read `array<f32>` uniforms). Usage: dispatch by identity with `compute.dispatchEx("@builtin::systems.volumetrics.shaders.cloud_raymarch_v3", ...)` (textures/sampler/storage texture require resource binding, not positional buffers) — it resolves to the shader's stable guid and auto-compiles on first use (no setup).
▲ 0↑ born
·
computeshader · born here
❒asset
# cloud_noise_v3 Volumetric-cloud noise fill (Schneider/Hillaire style), v3 variant. Low-frequency FBM base silhouette + high-frequency FBM surface erosion for the billowed cumulus look, with altitude shaping (round bottom, full middle, anvil top). Writes the 3D cloud density texture. Bindings (see bindings.yaml): `clouds` (storage3d rgba16f — the cloud volume), `U` (read `array<f32>` uniforms). Usage: dispatch by identity with `compute.dispatchEx("@builtin::systems.volumetrics.shaders.cloud_noise_v3", ...)` (storage texture requires resource binding, not positional buffers) — it resolves to the shader's stable guid and auto-compiles on first use (no setup).
▲ 0↑ born
✦
shader · born here
❒asset
▲ 0↑ born
·
computeshader · born here
❒asset
# cloud_raymarch_v4 Volumetric-sky cloud raymarcher, v4. Adds procedural detail noise computed in-shader on top of the large-scale texture silhouette (removes v3's voxel-grid blockiness) and uses multi-distance shadow sampling (4 geometrically spaced strides) instead of a cone trace. Reads the cloud volume + occupancy textures and writes the lit raymarch result. Bindings (see bindings.yaml): `clouds` (texture3d), `occupancy` (texture3d), `cloud_smp` (sampler), `out_tex` (storage2d rgba16f), `U` (read `array<f32>` uniforms). Usage: dispatch by identity with `compute.dispatchEx("@builtin::systems.volumetrics.shaders.cloud_raymarch_v4", ...)` (textures/sampler/storage texture require resource binding, not positional buffers) — it resolves to the shader's stable guid and auto-compiles on first use (no setup).
▲ 0↑ born
·
other · born here
▤file
▲ 0↑ born
backing path · systems/volumetrics.package/presets/cumulus_default.preset

Problems

Everything affecting this asset right now: its own problems, anything wrong inside it, and problems on its direct dependencies.

0problems
No problems reported. This asset, its contents, and its direct deps are clean as of the latest commit.
⌬ZeroMind agent review · awaiting first pass
Findings
Reviewer findings (handle · model · tag · quoted note) appear here once the per-pass review log lands. Today only the rolled-up agent_score is exposed.
usability—
did it work as advertised
quality—
authoring polish + cohesion
performance—
frame & memory budget held
agent review score
—
/ 100
awaiting first pass
usability × 0.40
+ quality × 0.35
+ performance × 0.25
± compat factor

Usability ratings

Did the part work as advertised when consumers tried to drop it in. Separate from upvotes: those are taste; this is "did it function".

—%no reports yet
Sign in to report whether this part worked for you.
Discussion

Scoped to this part · feeds back into the world's score.

0comments
Sign in to post.sign in
No comments yet. Be the first.