Files
xrwise-datastructure/data-structure-creator.md
luzieahrens 14de604ad2 update dcs
2026-07-04 09:03:58 +02:00

197 lines
9.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Creator Data Structure
This document describes the internal data structures used by the XRWise Creator editor.
---
## `SceneItem`
A single object in the scene. Stored in the Zustand store as `Record<number, SceneItem>`.
```ts
type SceneItem = {
// --- always present ---
type: ItemType // e.g. "Cube", "Camera", "Directional Light"
category: string // e.g. "Basic", "Light", "Streaming"
position: [number, number, number] // world-space XYZ
// --- transform ---
rotation?: number[] // Euler degrees [x, y, z]
dimensions?: number[] // XYZ scale / size in metres
// --- appearance ---
color?: string // hex color e.g. "#ffffff"
opacity?: number // 01
// --- lights ---
intensity?: number | string // light intensity
target?: number[] // look-at point [x, y, z]
// --- media / streaming ---
file?: string // asset path or storage key
LiveKitRoomID?: string
shader?: string // "unlit" | "lit" | "greenscreen"
GreenScreenColor?: string // hex color
GreenScreenBorderSettings?: string // numeric string
GreenScreenColorNear?: string // numeric string (01)
widthcrop?: number
heightcrop?: number
videoJobId?: string // in-progress conversion job ID
// --- text ---
text?: string
fontWeight?: "light" | "regular" | "bold"
fontSize?: number
// --- camera ---
zoom?: number // 110; maps to FOV = 50 / zoom
// --- interactions ---
grabable?: boolean
locked?: boolean // blocks move gizmo + deletion in the editor
}
```
### Fields by item type
| Type | Required fields | Optional fields |
|---|---|---|
| `Cube` / `Sphere` / `Cylinder` / `Cone` | `position` | `color`, `opacity`, `dimensions`, `rotation` |
| `Floor` | `position` | `color`, `opacity`, `dimensions`, `rotation` |
| `Wall` | `position` | `color`, `opacity`, `dimensions`, `rotation` |
| `Directional Light` | `position`, `target` | `color`, `intensity` |
| `Point Light` | `position` | `color`, `intensity` |
| `Entrance` / `Exit` | `position` | — |
| `Billboard` | `position`, `file` | `dimensions`, `rotation` |
| `LiveKitStream` | `position`, `LiveKitRoomID` | `shader`, `GreenScreenColor`, `GreenScreenBorderSettings`, `GreenScreenColorNear`, `widthcrop`, `heightcrop`, `dimensions`, `rotation` |
| `VideoWall` | `position` | `file`, `videoJobId`, `widthcrop`, `heightcrop`, `dimensions`, `rotation` |
| `Presentation` | `position` | `file`, `dimensions`, `rotation` |
| `Camera` | `position`, `target` | `zoom`, `rotation` |
| `Text` | `position`, `text` | `color`, `fontSize`, `fontWeight`, `dimensions`, `rotation` |
| `Custom` | `position`, `file` | `dimensions`, `rotation` |
| `Area` | `position` | `dimensions` (X/Z footprint only — Y is fixed), `rotation` |
### Common optional field (all types)
| Field | Type | Description |
|---|---|---|
| `grabable` | `boolean` | Whether the item is interactable in-experience |
| `locked` | `boolean` | When `true`, hides the move gizmo for the item and blocks deletion until unlocked from the menubar |
---
## `SceneItems`
The full scene is stored as a flat map keyed by numeric ID:
```ts
type SceneItems = Record<number, SceneItem>
```
IDs are assigned sequentially when items are spawned and do not change.
---
## `SceneGroups`
Used by the Grouping feature to let several items (or nested groups) be selected, moved, and managed as one.
```ts
type GroupChild =
| { kind: "item"; id: number }
| { kind: "group"; id: number }
type SceneGroup = {
name: string
children: GroupChild[]
}
type SceneGroups = Record<number, SceneGroup>
```
Group IDs are assigned sequentially from the store's `nextGroupId` counter, independent of item IDs. A group's `children` reference item/group IDs directly — there is no `groupId` field on `SceneItem` itself.
---
## `SceneStore` (Zustand)
The complete editor state. Fields marked 💾 are persisted to `localStorage` under the key `scene-storage`.
| Field | Type | Default | 💾 | Description |
|---|---|---|---|---|
| `items` | `SceneItems` | `{}` | ✅ | All scene objects |
| `selected` | `number \| null` | `null` | ✅ | Currently selected item ID |
| `skyColor` | `string` | `"#ffffff"` | ✅ | Background color |
| `showGrid` | `boolean` | `true` | ✅ | Grid visibility |
| `customItems` | `CustomItem[]` | `[]` | ✅ | User-uploaded GLB assets |
| `showLights` | `boolean` | `true` | ✅ | Light helper visibility |
| `showWalls` | `boolean` | `true` | ✅ | Wall visibility |
| `showAreas` | `boolean` | `true` | ✅ | Area Collider visibility (Canvas Toggles) |
| `cameraTarget` | `number[]` | `[0,0,0]` | ✅ | OrbitControls look-at point |
| `cameraRotation` | `number[]` | `[0,0,0]` | ✅ | Editor camera rotation |
| `cameraQuaternion` | `[x,y,z,w]` | `[0,0,0,0]` | ✅ | Editor camera quaternion |
| `graphicalCoding` | `{ nodes, connections } \| null` | `null` | ✅ | Legacy node-graph coding state (superseded by `blockCoding`) |
| `blockCoding` | `{ scripts: unknown[] } \| null` | `null` | ✅ | Block Coding scripts — see [Block Coding data structure](./data-structure-block-coding.md) |
| `backgroundSound` | `string \| null` | `null` | ✅ | Uploaded ambient sound storage path (World Settings → Sound) |
| `groups` | `SceneGroups` | `{}` | ✅ | All item/group hierarchies |
| `nextGroupId` | `number` | `1` | ✅ | Next group ID to assign |
| `selectedGroup` | `number \| null` | `null` | — | Currently selected group ID |
| `multiSelectedItems` | `number[]` | `[]` | — | Item IDs in a multi-select (shift-click) |
| `multiSelectedGroups` | `number[]` | `[]` | — | Group IDs in a multi-select |
| `past` / `future` | `HistorySnapshot[]` | `[]` | — | Undo/redo stacks — see [Undo/Redo](#undoredo) below |
| `message` | `string \| null` | `null` | — | Transient toast message |
| `hovered` | `number \| null` | `null` | — | Item ID currently hovered in canvas or panel |
| `focusItem` | `number \| null` | `null` | — | Item ID the camera should focus on |
| `isDragging` | `boolean` | `false` | — | Pointer drag state |
| `isTyping` | `boolean` | `false` | — | Text input focus state |
| `orbitControls` | `OrbitControlsImpl \| null` | `null` | — | Live OrbitControls ref |
| `api` | `ScreenshotAPI \| null` | `null` | — | Screenshot renderer ref |
💾 reflects the `partialize` allowlist in `lib/SceneStore.ts``selectedGroup`, `multiSelectedItems`, `multiSelectedGroups`, `past`, `future`, and the transient/ref fields below them are intentionally excluded, so undo history and multi-selection do not survive a page refresh.
---
## Undo/Redo
`past`/`future` hold up to `MAX_HISTORY` (50) `HistorySnapshot`s:
```ts
type HistorySnapshot = {
items: SceneItems
groups: SceneGroups
nextGroupId: number
}
```
- Mutations that go through `setItems`, `updateItem`, `batchUpdateItems`, or any group action (`createGroup`, `createGroupFromSelection`, `disbandGroup`, `renameGroup`) push the *pre-change* state onto `past` and clear `future`.
- Pushes are coalesced: rapid successive edits (e.g. dragging a gizmo) within 400ms of the last push are merged into one history entry, so undo steps back per gesture rather than per frame.
- `undo()`/`redo()` swap `items`/`groups`/`nextGroupId` between `past`/`future` and clear the current selection state (`selectedGroup`, `multiSelectedItems`, `multiSelectedGroups`).
- History is in-memory only — it is not part of `partialize` and does not round-trip through save/load or the database JSON.
---
## `ITEMS_CONFIG` spawn defaults
When an item is added to the scene, it is initialized with these values from `items-config.tsx`:
| Type | `dimensions` | `color` | `rotation` | `spawnY` | `customAttributes` |
|---|---|---|---|---|---|
| `Cube` | `[1,1,1]` | `#ffffff` | `[0,0,0]` | `0.5` | — |
| `Sphere` | `[1,1,1]` | `#ffffff` | `[0,0,0]` | `0.5` | — |
| `Cylinder` | `[1,1,1]` | `#ffffff` | `[0,0,0]` | `0.5` | — |
| `Cone` | `[1,1,1]` | `#ffffff` | `[0,0,0]` | `0.5` | — |
| `Floor` | `[10,0.1,10]` | `#ffffff` | `[0,0,0]` | `0` | — |
| `Wall` | `[20,5,0.1]` | `#ffffff` | `[0,90,0]` | `2.5` | — |
| `Point Light` | `[1,1,1]` | `#ffffff` | `[0,0,0]` | `5` | `intensity: 50, target: [0,0,0]` |
| `Directional Light` | `[1,1,1]` | `#ffffff` | `[0,0,0]` | `5` | `intensity: 20, target: [0,0,0]` |
| `Entrance` | `[1,1,1]` | `#ffffff` | `[0,0,0]` | `0.1` | — |
| `Exit` | `[1,1,1]` | `#962929` | `[0,0,0]` | `0.1` | — |
| `Billboard` | `[1,1,1]` | `#ffffff` | `[0,0,0]` | `4` | `file: "/img/default-img.png"` |
| `LiveKitStream` | `[1,1,1]` | `#ffffff` | `[0,0,0]` | `4` | `LiveKitRoomID: "Gas Station"` |
| `VideoWall` | `[1,1,1]` | `#ffffff` | `[0,0,0]` | `4` | — |
| `Presentation` | `[1,1,1]` | `#ffffff` | `[0,0,0]` | `4` | — |
| `Camera` | `[1,1,1]` | `#444444` | `[0,0,0]` | `4` | `zoom: 1, target: [0,0,0]` |
| `Text` | `[0.5,0.5,0.5]` | `black` | `[0,0,0]` | `2` | `text: "Text", fontWeight: "regular", fontSize: 5` |
| `Custom` | `[1,1,1]` | `#ffffff` | `[0,0,0]` | `1` | — |
| `Area` | `[10,0.1,10]` | `#ffffff` | `[0,0,0]` | `0` | — |