# Layers, Groups & Tags `oxide_engine::layer` is the engine's shared answer to *"which things interact with which?"*. Instead of every subsystem inventing its own notion of collision groups, render masks, or query filters, they all reference one primitive — the [`LayerMask`] — so a layer named once is honored everywhere. This system lands in Stage 5 (Engine Core Framework). The consumers below are wired up in their own stages, but they all build on the types here. ## The model: one Layer, many Groups Oxide follows the **Unity model**, separating two concepts: - **[`Layer`]** — *single-valued* membership. Every entity is on exactly **one** of 32 logical layers (its index `0..32`). This is the fast filter slot used by rendering, physics, and queries. - **[`Tags`]** — *multi-valued* gameplay grouping. An entity can be in **any number** of named groups (`"Enemies"`, `"Interactables"`, `"SaveOnExit"`) that game code and scripts look up by name. So: *what kind of thing is this, for fast filtering?* → one **Layer**. *What gameplay categories does it belong to?* → many **Groups** (tags). The two never fight over the same slot. ## `LayerMask`: the filter primitive A `LayerMask` is a 32-slot bitset packed into a `u32`. An entity's `Layer` is a single index, but the things that *select* entities carry a **mask** — a camera's visibility, a physics collision filter, a raycast's query mask — so a filter can target several layers at once. An entity is selected when its layer is one of the bits in the filter: ```rust use oxide_engine::layer::{Layer, LayerMask}; let entity = Layer::on(2); // membership: layer 2 let filter = LayerMask::NONE.with(1).with(2); // a filter selecting 1 or 2 assert!(entity.matches(filter)); // layer 2 is in the mask → selected ``` `a.intersects(b)` (equivalently `(a & b) != 0`) is the universal mask interaction test. Everything else (`with`/`without`/`union`/`intersection`/ `complement`, the `|`/`&`/`^`/`!` operators, `iter`) is convenience around that. Layer indices run `0..32`. Passing an index `>= 32` panics in every build — a mistake worth catching loudly rather than wrapping silently. `Layer` is **node-baked**: `Scene::spawn` auto-attaches `Layer::DEFAULT` (index 0) to every entity, so a fresh entity is visible to broad "see everything" filters out of the box. ## Naming layers: `LayerRegistry` Bits are not self-documenting, so a project keeps a [`LayerRegistry`] mapping indices to names. Index 0 is seeded as `"Default"`; the rest are unnamed until assigned. The registry is project-level data (serialized with the project in a later stage), and renaming a layer never moves an entity — only the label changes. ```rust use oxide_engine::layer::LayerRegistry; let mut registry = LayerRegistry::new(); registry.set(1, "Player"); registry.set(2, "NPC"); // Author a filter by name instead of by magic number: let visible_to_camera = registry.mask_of(["Player", "NPC"]); assert_eq!(registry.index_of("Player"), Some(1)); ``` The editor seeds a small, generally-useful starter set — `Default`, `UI`, `Player`, `World` — and exposes the registry through its **Layer Names** editor (opened from the inspector's `Layer` dropdown). Because both a camera's visibility mask and a raycast's filter can be built from the same names, the *named layer* is the single source of truth. ## Gameplay grouping: `Tags` + `GroupRegistry` [`Tags`] is the per-entity, string-keyed set that holds an entity's **group** membership — the multi-valued counterpart to its single `Layer`: ```rust use oxide_engine::layer::Tags; let mut tags = Tags::single("Enemy"); tags.insert("Flying"); assert!(tags.contains("Enemy")); ``` The set of *valid* group names is project-level data in a [`GroupRegistry`], so the editor offers a fixed vocabulary to pick from (predefined, like layers) rather than free-typed strings. Defining or deleting a group only changes that vocabulary — it never touches the tags already on entities: ```rust use oxide_engine::layer::GroupRegistry; let mut groups = GroupRegistry::new(); groups.define("Enemies"); groups.define("Interactables"); assert!(groups.contains("Enemies")); ``` In the editor, the inspector's **Groups** dropdown is a multi-select of the defined groups (each a checkbox toggling membership in the entity's `Tags`), with an **Edit groups…** entry opening the Groups editor to manage the vocabulary. Use a **layer** when something must filter quickly and en masse (rendering, physics, queries). Use a **group** when you need to ask "what gameplay categories is this in?" and an entity may be in several at once. ## Attaching to entities `Layer` is auto-attached; `Tags` is an ordinary ECS component — attach it through the scene's world and query it like anything else: ```rust use oxide_engine::prelude::*; use oxide_engine::layer::{LayerMask, Layer, Tags}; let mut scene = Scene::new(); let guard = scene.spawn("guard", Transform::IDENTITY); // Layer::DEFAULT auto-attached scene.world_mut().insert_one(guard, Layer::on(2)).unwrap(); // move to layer 2 scene.world_mut().insert_one(guard, Tags::single("Enemy")).unwrap(); // Find everything a layer-2 query would hit: let filter = LayerMask::layer(2); for (entity, layer) in scene.world().query::<&Layer>().iter() { if layer.matches(filter) { // ... this entity is selected } } ``` All five types (`LayerMask`, `LayerRegistry`, `Layer`, `Tags`, `GroupRegistry`) are `serde`-serializable, so layer/group data round-trips through RON and is dual-editable from the editor and from scripts/AI agents like every other engine component. ## Who consumes this | Consumer | Stage | How it uses layers | |----------|-------|--------------------| | Render pass pipeline | 5 | A camera holds a visibility `LayerMask`; only entities whose `Layer` is in it are drawn | | Physics | 9 | A collider's membership + filter masks drive collision groups and sensor/trigger filtering | | Scene queries | 9 | A raycast/shape-cast carries a filter mask tested against candidates' layer | | Editor | 6+ | Single-select `Layer` dropdown + Layer Names editor; multi-select `Groups` dropdown + Groups editor | [`LayerMask`]: ../engine/src/layer/mask.rs [`LayerRegistry`]: ../engine/src/layer/registry.rs [`Layer`]: ../engine/src/layer/components.rs [`Tags`]: ../engine/src/layer/components.rs [`GroupRegistry`]: ../engine/src/layer/groups.rs