# 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
