# `@combos-fun/plugin-renderer-3d` — Agent notes

3D rendering foundation for Combos Fun. Wraps Three.js (`three` ^0.172) and exposes `Renderer3DSystem`, the `Renderer3D` base class, and a `ThreeContext` that owns the Three.js scene / camera / lights / WebGL renderer / `Clock` / `GLTFLoader`.

## When to read

Read for any 3D rendering task: setting up the canvas, blank-3D-canvas debugging, custom 3D renderer plugins, async asset loading. All `plugin-renderer-3d-*` sub-plugins assume this file is already loaded.

## Public API

```ts
import {
  Renderer3DSystem,
  Renderer3D,
  Renderer3DManager,
  ThreeContext,
} from '@combos-fun/plugin-renderer-3d';
```

### `Renderer3DSystem`

`systemName = 'Renderer3DSystem'`. Init params:

| Field | Type | Default |
|-------|------|---------|
| `canvas` | `HTMLCanvasElement?` | — |
| `container` | `HTMLElement?` | — |
| `width` | `number` | `750` |
| `height` | `number` | `1000` |
| `antialias` | `boolean` | `true` |
| `backgroundColor` | `number` | `0x000000` |
| `backgroundAlpha` | `number` | `1` |

Either `canvas` or `container` must be provided.

### Built-in scene defaults

`ThreeContext` automatically creates:

- `PerspectiveCamera` (FOV 75, z=5)
- `AmbientLight` (`0xffffff`, intensity `0.6`)
- `DirectionalLight` (`0xffffff`, intensity `0.8`, position `(5, 10, 7.5)`)
- `WebGLRenderer`, `Clock`, `GLTFLoader`

There are **no separate camera or light plugins** — these come for free.

### `Renderer3D` base class

Extend this for any custom 3D rendering plugin:

```ts
class MyRenderer3D extends Renderer3D {
  init() {
    this.rendererSystem = this.game.getSystem(Renderer3DSystem);
    this.rendererSystem.rendererManager.register(this);
  }
  componentChanged(changed) {
    const scene = this.threeContext.scene;
    /* create / update / dispose THREE.Object3D under scene */
  }
  rendererUpdate(gameObject) {
    /* per-frame sync */
  }
}
```

### Async loading pattern

For 3D plugins that load assets (`Img3D`, `Model3D`, etc.) use the inherited helpers to drop stale work when a component is removed mid-load:

```ts
const asyncId = this.increaseAsyncId(gameObject.id);
const data = await loadSomething();
if (!this.validateAsyncId(gameObject.id, asyncId)) return; // stale
/* attach data */
```

This prevents memory leaks and double-add bugs when the same `GameObject` is re-used quickly.

## Required setup

`Renderer3DSystem` **must** be the first system added before any 3D sub-system that calls `getSystem(Renderer3DSystem)` in `init`.

```ts
new Game({
  systems: [
    new Renderer3DSystem({ canvas, width: 750, height: 1000 }),
    new Graphics3DSystem(),
    new Img3DSystem(),
    // ...other 3D sub-systems, physics, audio
  ],
});
```

## Runtime behaviour

- 3D Component fields use **direct URL strings** for `resource`
  (unlike 2D which uses engine resource names).
- `ThreeContext` owns the render loop. `Renderer3D` subclasses register
  with `rendererManager` and are driven each frame.
- Three.js `Object3D`s should always be attached to `this.threeContext.scene`,
  not directly to the renderer.

## Common pitfalls

| Symptom | Fix |
|---------|-----|
| Blank canvas | Add `Renderer3DSystem`; ensure `autoStart: true` or call `game.start()` |
| Nothing draws | Add the matching 3D sub-system (`Graphics3DSystem`, `Model3DSystem`, etc.) before adding components |
| Object loaded but not visible | Object likely loaded at origin — adjust `position*` fields, or check camera distance (default `z=5`) |
| `getSystem` undefined | Use class reference `game.getSystem(Renderer3DSystem)` |
| Memory leak after fast remove | Use `increaseAsyncId` / `validateAsyncId` to drop stale async work |
| CORS errors loading 3D assets | Host on same origin or CORS-enabled CDN |

## Minimal example

```ts
import { Game } from '@combos-fun/engine';
import { Renderer3DSystem } from '@combos-fun/plugin-renderer-3d';

new Game({
  systems: [
    new Renderer3DSystem({
      canvas: document.querySelector('#canvas')!,
      width: 750,
      height: 1000,
    }),
  ],
});
```

This alone shows an empty 3D scene with default lighting. Add `Graphics3DSystem` etc. and matching components to render anything.

## Verification

- `pnpm --filter @combos-fun/plugin-renderer-3d run build`
- Run a 3D example app and check the browser console for Three.js / WebGL
  errors and missing assets.
