# Tutorial: first mesh and material

Rendered actors need an explicit mesh and material in scene JSON when the archetype lists `hi.ComponentRender`. Paths use **`asset://` URIs** (`hi.AssetRef` at runtime).

- Unscoped: `asset://./models/cube` (catalog → pack; pack must be retained)
- Explicit pack: `asset://shared/models/cube`

See [Game-facing refs](/docs/systems/refs.md) and [Asset URIs and packs](/docs/systems/assets-and-shinra.md#asset-uris-and-packs). Mesh / material / texture refs are extension-free stems (no `.shinmodel` / `.png`). Scene `"model"` placeables must end with `.model.json` (validator).

Deep reference: [Assets and Shinra](/docs/systems/assets-and-shinra.md), [Shinra asset formats](/docs/systems/asset-formats.md).

## 1. Entity with render

```zig
const hi = @import("hikari_game");

pub const CrateEntity = hi.defineActor(.{
    .archetype = "crate",
    .components = .{hi.ComponentRender},
});
```

Rebuild so the archetype is in the manifest.

## 2. Material JSON

Sample: `hikari-games/example/assets/materials/cube.material.json`

```json
{
  "kind": "com.hikari.material",
  "version": 1,
  "id": "cube",
  "material": {
    "name": "cube_material",
    "shader": {
      "name": "DeferredGeometry",
      "package": "_engine/gbuffer",
      "entry_points": { "vertex": "gbuffer_vertex", "fragment": "gbuffer_fragment" }
    },
    "textures": { "albedo": "asset://./textures/crate" },
    "params": { "base_color": [1, 1, 1, 1] }
  }
}
```

Shinra turns source textures/models into `.shintexture` / `.shinmodel` (and packs). Point materials at **logical stems**, not raw `.png`.

## 3. Scene actor

Scene document is flat **version `1`**. Capability blocks live under `"components"`. The runtime derives required packs from the asset references:

```json
{
  "kind": "com.hikari.scene",
  "version": 1,
  "id": "my_level",
  "name": "My Level",
  "actors": [{
      "id": "crate_01",
      "archetype": "crate",
      "transform": {
        "position": [0, 0.5, 0],
        "rotation_euler": [0, 0, 0],
        "scale": [1, 1, 1]
      },
      "components": {
        "render": {
          "mesh": "asset://./models/cube",
          "material": "asset://./materials/cube"
        }
      }
    }]
}
```

Do not add a scene `packs` field. Configure asset membership in `configs/layout.json`; the loader retains the referenced packs and their dependencies. Sample: `scenes/physics_playground.json`.

## Multi-material models (placeable)

Cook merges glTF primitives that share a material into one slot, then emits companions with **unscoped** refs (`asset://./…`). Place the model with a single actor:

```json
{
  "id": "prop",
  "name": "MosquitoInAmber",
  "archetype": "empty",
  "transform": { "position": [0, 1, 0], "rotation_euler": [0, 0, 0], "scale": [1, 1, 1] },
  "components": {
    "render": {
      "model": "asset://./scenes/mosquito_in_amber/models/MosquitoInAmber.model.json"
    }
  },
  "script": { "path": "asset://./scripts/cinematic_prop_rotate" }
}
```

One entity submits N draws (one per material slot). Optional `components.render.materials[]` overrides slot materials. Samples: `scenes/material_samples.json` (both Khronos multi-slot models side by side).

## Soft failures (expected)

| Missing / failed | Placeholder |
|---|---|
| Mesh | Error cube |
| Material / shader | Pink `error_material` |
